Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #3061 from rouault/saveas_human_readable_values
[FEATURE] Export vector layer with "human-readable" values from edit widgets
  • Loading branch information
rouault committed May 14, 2016
2 parents cfb6100 + 90cc0ed commit abd182c
Show file tree
Hide file tree
Showing 13 changed files with 865 additions and 40 deletions.
39 changes: 33 additions & 6 deletions python/core/qgsvectorfilewriter.sip
@@ -1,11 +1,8 @@
/** \ingroup core
* A convenience class for writing vector files to disk.
There are two possibilities how to use this class:
1. static call to QgsVectorFileWriter::writeAsShapefile(...) which saves the whole vector layer
1. static call to QgsVectorFileWriter::writeAsVectorFormat(...) which saves the whole vector layer
2. create an instance of the class and issue calls to addFeature(...)

Currently supports only writing to shapefiles, but shouldn't be a problem to add capability
to support other OGR-writable formats.
*/
class QgsVectorFileWriter
{
Expand Down Expand Up @@ -107,6 +104,32 @@ class QgsVectorFileWriter
SymbolLayerSymbology //Exports one feature per symbol layer (considering symbol levels)
};

/** Interface to convert raw field values to their user-friendly value.
* @note Added in QGIS 2.16
*/
class FieldValueConverter
{
public:
/** Constructor */
FieldValueConverter();

/** Destructor */
virtual ~FieldValueConverter();

/** Return a possibly modified field definition. Default implementation will return provided field unmodified.
* @param field original field definition
* @return possibly modified field definition
*/
virtual QgsField fieldDefinition( const QgsField& field );

/** Convert the provided value, for field fieldIdxInLayer. Default implementation will return provided value unmodified.
* @param fieldIdxInLayer field index
* @param value original raw value
* @return possibly modified value.
*/
virtual QVariant convert( int fieldIdxInLayer, const QVariant& value );
};

/** Write contents of vector layer to an (OGR supported) vector formt
* @param layer layer to write
* @param fileName file name to write to
Expand All @@ -127,6 +150,7 @@ class QgsVectorFileWriter
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @param fieldValueConverter field value converter (added in QGIS 2.16)
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
Expand All @@ -145,7 +169,8 @@ class QgsVectorFileWriter
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
QgsAttributeList attributes = QgsAttributeList(),
FieldValueConverter* fieldValueConverter = nullptr
);

/** Writes a layer out to a vector file.
Expand All @@ -168,6 +193,7 @@ class QgsVectorFileWriter
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @param fieldValueConverter field value converter (added in QGIS 2.16)
* @note added in 2.2
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
Expand All @@ -187,7 +213,8 @@ class QgsVectorFileWriter
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
QgsAttributeList attributes = QgsAttributeList(),
FieldValueConverter* fieldValueConverter = nullptr
);

/** Create a new vector file writer */
Expand Down
210 changes: 200 additions & 10 deletions src/app/ogr/qgsvectorlayersaveasdialog.cpp
Expand Up @@ -20,22 +20,32 @@
#include "qgsgenericprojectionselector.h"
#include "qgsvectordataprovider.h"
#include "qgscoordinatereferencesystem.h"
#include "qgseditorwidgetfactory.h"
#include "qgseditorwidgetregistry.h"

#include <QSettings>
#include <QFileDialog>
#include <QTextCodec>

static const int COLUMN_IDX_NAME = 0;
static const int COLUMN_IDX_TYPE = 1;
static const int COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE = 2;

QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
, mCRS( srsid )
, mLayer( 0 )
, mAttributeTableItemChangedSlotEnabled( true )
, mReplaceRawFieldValuesStateChangedSlotEnabled( true )
{
setup();
}

QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( QgsVectorLayer *layer, int options, QWidget* parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
, mLayer( layer )
, mAttributeTableItemChangedSlotEnabled( true )
, mReplaceRawFieldValuesStateChangedSlotEnabled( true )
{
if ( layer )
{
Expand Down Expand Up @@ -234,23 +244,70 @@ void QgsVectorLayerSaveAsDialog::on_mFormatComboBox_currentIndexChanged( int idx
if ( mLayer )
{
mAttributeTable->setRowCount( mLayer->fields().count() );
mAttributeTable->setColumnCount( 2 );
mAttributeTable->setHorizontalHeaderLabels( QStringList() << tr( "Name" ) << tr( "Type" ) );

int i = 0;
Q_FOREACH ( const QgsField &fld, mLayer->fields() )
bool foundFieldThatCanBeExportedAsDisplayedValue = false;
for ( int i = 0; i < mLayer->fields().size(); ++i )
{
QgsEditorWidgetFactory *factory;
if ( mLayer->editFormConfig()->widgetType( i ) != "TextEdit" &&
( factory = QgsEditorWidgetRegistry::instance()->factory( mLayer->editFormConfig()->widgetType( i ) ) ) )
{
foundFieldThatCanBeExportedAsDisplayedValue = true;
break;
}
}
if ( foundFieldThatCanBeExportedAsDisplayedValue )
{
mAttributeTable->setColumnCount( 3 );
mAttributeTable->setHorizontalHeaderLabels( QStringList() << tr( "Name" ) << tr( "Type" ) << tr( "Replace with displayed values" ) );
}
else
{
mAttributeTable->setColumnCount( 2 );
mAttributeTable->setHorizontalHeaderLabels( QStringList() << tr( "Name" ) << tr( "Type" ) );
mReplaceRawFieldValues->setVisible( false );
}

mAttributeTableItemChangedSlotEnabled = false;

for ( int i = 0; i < mLayer->fields().size(); ++i )
{
const QgsField &fld = mLayer->fields().at( i );
Qt::ItemFlags flags = mLayer->providerType() != "oracle" || !fld.typeName().contains( "SDO_GEOMETRY" ) ? Qt::ItemIsEnabled : Qt::NoItemFlags;
QTableWidgetItem *item;
item = new QTableWidgetItem( fld.name() );
item->setFlags( flags | Qt::ItemIsUserCheckable );
item->setCheckState( Qt::Unchecked );
mAttributeTable->setItem( i, 0, item );
mAttributeTable->setItem( i, COLUMN_IDX_NAME, item );

item = new QTableWidgetItem( fld.typeName() );
item->setFlags( flags );
mAttributeTable->setItem( i++, 1, item );
mAttributeTable->setItem( i, COLUMN_IDX_TYPE, item );

if ( foundFieldThatCanBeExportedAsDisplayedValue )
{
QgsEditorWidgetFactory *factory;
if ( mLayer->editFormConfig()->widgetType( i ) != "TextEdit" &&
( factory = QgsEditorWidgetRegistry::instance()->factory( mLayer->editFormConfig()->widgetType( i ) ) ) )
{
item = new QTableWidgetItem( tr( "Use %1" ).arg( factory->name() ) );
item->setFlags( Qt::ItemIsUserCheckable );
item->setCheckState( Qt::Unchecked );
mAttributeTable->setItem( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE, item );
}
else
{
item = new QTableWidgetItem();
item->setFlags( Qt::NoItemFlags );
mAttributeTable->setItem( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE, item );
}
}
}

mAttributeTableItemChangedSlotEnabled = true;

mReplaceRawFieldValues->setEnabled( false );

mAttributeTable->resizeColumnsToContents();
}

Expand Down Expand Up @@ -311,6 +368,92 @@ void QgsVectorLayerSaveAsDialog::on_mFormatComboBox_currentIndexChanged( int idx
}
}

void QgsVectorLayerSaveAsDialog::on_mReplaceRawFieldValues_stateChanged( int )
{
if ( !mReplaceRawFieldValuesStateChangedSlotEnabled )
return;
if ( mAttributeTable->columnCount() != 3 )
return;
mReplaceRawFieldValuesStateChangedSlotEnabled = false;
mAttributeTableItemChangedSlotEnabled = false;
if ( mReplaceRawFieldValues->checkState() != Qt::PartiallyChecked )
{
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, COLUMN_IDX_NAME )->checkState() == Qt::Checked &&
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE ) &&
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsEnabled )
{
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setCheckState( mReplaceRawFieldValues->checkState() );
}
}
}
mReplaceRawFieldValues->setTristate( false );
mAttributeTableItemChangedSlotEnabled = true;
mReplaceRawFieldValuesStateChangedSlotEnabled = true;
}

void QgsVectorLayerSaveAsDialog::on_mAttributeTable_itemChanged( QTableWidgetItem * item )
{
if ( !mAttributeTableItemChangedSlotEnabled )
return;
mReplaceRawFieldValuesStateChangedSlotEnabled = false;
mAttributeTableItemChangedSlotEnabled = false;
int row = item->row();
int column = item->column();
if ( column == COLUMN_IDX_NAME &&
mAttributeTable->item( row, column )->checkState() == Qt::Unchecked &&
mAttributeTable->columnCount() == 3 &&
mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE ) &&
( mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsUserCheckable ) )
{
mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setCheckState( Qt::Unchecked );
mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setFlags( Qt::ItemIsUserCheckable );
bool checkBoxEnabled = false;
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE ) &&
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsEnabled )
{
checkBoxEnabled = true;
break;
}
}
mReplaceRawFieldValues->setEnabled( checkBoxEnabled );
if ( !checkBoxEnabled )
mReplaceRawFieldValues->setCheckState( Qt::Unchecked );
}
else if ( column == COLUMN_IDX_NAME &&
mAttributeTable->item( row, column )->checkState() == Qt::Checked &&
mAttributeTable->columnCount() == 3 &&
mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE ) &&
( mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsUserCheckable ) )
{
mAttributeTable->item( row, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
mReplaceRawFieldValues->setEnabled( true );
}
else if ( column == COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE &&
( mAttributeTable->item( row, column )->flags() & Qt::ItemIsUserCheckable ) )
{
bool allChecked = true;
bool allUnchecked = true;
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE ) &&
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsEnabled )
{
if ( mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->checkState() == Qt::Unchecked )
allChecked = false;
else
allUnchecked = false;
}
}
mReplaceRawFieldValues->setCheckState(( !allChecked && !allUnchecked ) ? Qt::PartiallyChecked : ( allChecked ) ? Qt::Checked : Qt::Unchecked );
}
mAttributeTableItemChangedSlotEnabled = true;
mReplaceRawFieldValuesStateChangedSlotEnabled = true;
}

void QgsVectorLayerSaveAsDialog::on_leFilename_textChanged( const QString& text )
{
buttonBox->button( QDialogButtonBox::Ok )->setEnabled( QFileInfo( text ).absoluteDir().exists() );
Expand Down Expand Up @@ -469,7 +612,24 @@ QgsAttributeList QgsVectorLayerSaveAsDialog::selectedAttributes() const

for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, 0 )->checkState() == Qt::Checked )
if ( mAttributeTable->item( i, COLUMN_IDX_NAME )->checkState() == Qt::Checked )
{
attributes.append( i );
}
}

return attributes;
}

QgsAttributeList QgsVectorLayerSaveAsDialog::attributesAsDisplayedValues() const
{
QgsAttributeList attributes;

for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, COLUMN_IDX_NAME )->checkState() == Qt::Checked &&
mAttributeTable->columnCount() == 3 &&
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->checkState() == Qt::Checked )
{
attributes.append( i );
}
Expand Down Expand Up @@ -572,17 +732,47 @@ void QgsVectorLayerSaveAsDialog::on_mGeometryTypeComboBox_currentIndexChanged( i

void QgsVectorLayerSaveAsDialog::on_mSelectAllAttributes_clicked()
{
mAttributeTableItemChangedSlotEnabled = false;
mReplaceRawFieldValuesStateChangedSlotEnabled = false;
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, 0 )->flags() & Qt::ItemIsEnabled )
mAttributeTable->item( i, 0 )->setCheckState( Qt::Checked );
if ( mAttributeTable->item( i, COLUMN_IDX_NAME )->flags() & Qt::ItemIsEnabled )
{
if ( mAttributeTable->columnCount() == 3 &&
( mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsUserCheckable ) )
{
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
}
mAttributeTable->item( i, COLUMN_IDX_NAME )->setCheckState( Qt::Checked );
}
}
if ( mAttributeTable->columnCount() == 3 )
{
mReplaceRawFieldValues->setEnabled( true );
}
mAttributeTableItemChangedSlotEnabled = true;
mReplaceRawFieldValuesStateChangedSlotEnabled = true;
}

void QgsVectorLayerSaveAsDialog::on_mDeselectAllAttributes_clicked()
{
mAttributeTableItemChangedSlotEnabled = false;
mReplaceRawFieldValuesStateChangedSlotEnabled = false;
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
mAttributeTable->item( i, 0 )->setCheckState( Qt::Unchecked );
mAttributeTable->item( i, COLUMN_IDX_NAME )->setCheckState( Qt::Unchecked );
if ( mAttributeTable->columnCount() == 3 &&
( mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->flags() & Qt::ItemIsUserCheckable ) )
{
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setFlags( Qt::ItemIsUserCheckable );
mAttributeTable->item( i, COLUMN_IDX_EXPORT_AS_DISPLAYED_VALUE )->setCheckState( Qt::Unchecked );
}
}
if ( mAttributeTable->columnCount() == 3 )
{
mReplaceRawFieldValues->setCheckState( Qt::Unchecked );
mReplaceRawFieldValues->setEnabled( false );
}
mAttributeTableItemChangedSlotEnabled = true;
mReplaceRawFieldValuesStateChangedSlotEnabled = true;
}

0 comments on commit abd182c

Please sign in to comment.