Skip to content

Commit

Permalink
Merge pull request #1318 from 3nids/fieldexpressionmodel
Browse files Browse the repository at this point in the history
field expression widget (with expression capability added to QgsFieldModel)
  • Loading branch information
3nids committed May 2, 2014
2 parents 1f20630 + 71727ac commit 5093394
Show file tree
Hide file tree
Showing 22 changed files with 713 additions and 403 deletions.
1 change: 1 addition & 0 deletions python/gui/gui.sip
Expand Up @@ -29,6 +29,7 @@
%Include qgsexpressionbuilderwidget.sip
%Include qgsexpressionhighlighter.sip
%Include qgsfieldcombobox.sip
%Include qgsfieldexpressionwidget.sip
%Include qgsfieldmodel.sip
%Include qgsfieldvalidator.sip
%Include qgsfiledropedit.sip
Expand Down
27 changes: 14 additions & 13 deletions python/gui/qgsfieldcombobox.sip
@@ -1,5 +1,8 @@
/**
* @brief The QgsFieldComboBox is a combo box which displays the list of fields of a given layer.
* It might be combined with a QgsMapLayerComboBox to automatically update fields according to a chosen layer.
* If expression must be used, QgsFieldExpressionWidget shall be used instead.
* @see QgsMapLayerComboBox
* @note added in 2.3
*/
class QgsFieldComboBox : QComboBox
Expand All @@ -16,25 +19,23 @@ class QgsFieldComboBox : QComboBox
*/
explicit QgsFieldComboBox( QWidget *parent /TransferThis/ = 0 );

/**
* @brief currentField returns the currently selected field
*/
//! return the currently selected field
QString currentField();

//! Returns the currently used layer
QgsVectorLayer* layer();

signals:
/**
* @brief fieldChanged the signal is emitted when the currently selected field changes
*/
//! the signal is emitted when the currently selected field changes
void fieldChanged( QString fieldName );

public slots:
/**
* @brief setLayer sets the layer of which the fields are listed
*/
//! set the layer of which the fields are listed
void setLayer( QgsVectorLayer* layer );

//! convenience slot to connect QgsMapLayerComboBox layer signal
void setLayer( QgsMapLayer* layer );
/**
* @brief setField sets the currently selected field
*/
void setField( QString fieldName );

//! setField sets the currently selected field
void setField( QString fieldName );
};
45 changes: 45 additions & 0 deletions python/gui/qgsfieldexpressionwidget.sip
@@ -0,0 +1,45 @@

class QgsFieldExpressionWidget : QWidget
{
%TypeHeaderCode
#include "qgsfieldexpressionwidget.h"
%End

public:
/**
* @brief QgsFieldExpressionWidget creates a widget with a combo box to display the fields and expression and a button to open the expression dialog
*/
explicit QgsFieldExpressionWidget( QWidget *parent /TransferThis/ = 0 );

//! define the title used in the expression dialog
void setExpressionDialogTitle( QString title );

//! set the geometry calculator used in the expression dialog
void setGeomCalculator( const QgsDistanceArea &da );

/**
* @brief currentField returns the currently selected field or expression if allowed
* @param isExpression determines if the string returned is the name of a field or an expression
*/
QString currentField( bool *isExpression = 0 );

//! Returns the currently used layer
QgsVectorLayer* layer();

signals:
//! the signal is emitted when the currently selected field changes
void fieldChanged( QString fieldName );

//! fieldChanged signal with indication of the validity of the expression
void fieldChanged( QString fieldName, bool isValid );

public slots:
//! set the layer used to display the fields and expression
void setLayer( QgsVectorLayer* layer );

//! convenience slot to connect QgsMapLayerComboBox layer signal
void setLayer( QgsMapLayer* layer );

//! sets the current field or expression in the widget
void setField( QString fieldName );
};
41 changes: 31 additions & 10 deletions python/gui/qgsfieldmodel.sip
@@ -1,41 +1,62 @@

/**
* @brief The QgsFieldModel class is a model to display the list of fields of a layer in widgets.
* If allowed, expressions might be added to the end of the model.
* It can be associated with a QgsMapLayerModel to dynamically display a layer and its fields.
* @note added in 2.3
*/

class QgsFieldModel : QAbstractItemModel
{
%TypeHeaderCode
#include "qgsfieldmodel.h"
%End

public:
static const int FieldNameRole;
static const int FieldIndexRole;
enum FieldRoles {
/* SIP does not accept any arithmetic" */
FieldNameRole = 33, /* return field name if index corresponds to a field */
FieldIndexRole = 34, /* return field index if index corresponds to a field */
ExpressionRole = 35, /* return field name or expression */
IsExpressionRole = 36, /* return if index corresponds to an expression */
ExpressionValidityRole = 37 /* return if expression is valid or not */
};

/**
* @brief QgsFieldModel creates a model to display the fields of a given layer
*/
explicit QgsFieldModel( QObject *parent /TransferThis/ = 0 );

/**
* @brief indexFromName returns the index corresponding to a given fieldName
*/
//! return the index corresponding to a given fieldName
QModelIndex indexFromName( QString fieldName );

//! returns the currently used layer
void setAllowExpression( bool allowExpression );
bool allowExpression();

public slots:
/**
* @brief setLayer sets the layer of whch fields are displayed
* @brief setExpression sets a single expression to be added after the fields at the end of the model
* @return the model index of the newly added expression
*/
void setLayer( QgsMapLayer *layer );
QModelIndex setExpression( QString expression );

//! remove expressions from the model
void removeExpression();

//! returns the currently used layer
QgsVectorLayer* layer();

public slots:
//! set the layer of whch fields are displayed
void setLayer( QgsVectorLayer *layer );

protected slots:
virtual void updateModel();

// QAbstractItemModel interface
public:
QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const;
QModelIndex parent( const QModelIndex &child ) const;
int rowCount( const QModelIndex &parent ) const;
int rowCount( const QModelIndex &parent = QModelIndex() ) const;
int columnCount( const QModelIndex &parent ) const;
QVariant data( const QModelIndex &index, int role ) const;

Expand Down
Expand Up @@ -14,13 +14,12 @@ class QgsCategorizedSymbolRendererV2Widget : QgsRendererV2Widget

public slots:
void changeCategorizedSymbol();
void categoryColumnChanged();
void categoryColumnChanged( QString field );
void categoriesDoubleClicked( const QModelIndex & idx );
void addCategory();
void addCategories();
void deleteCategories();
void deleteAllCategories();
void setExpression();

void rotationFieldChanged( QString fldName );
void sizeScaleFieldChanged( QString fldName );
Expand All @@ -39,9 +38,6 @@ class QgsCategorizedSymbolRendererV2Widget : QgsRendererV2Widget
// Called by virtual refreshSymbolView()
void populateCategories();

//! populate column combo
void populateColumns();

//! return row index for the currently selected category (-1 if on no selection)
int currentCategoryRow();

Expand Down
Expand Up @@ -13,8 +13,7 @@ class QgsGraduatedSymbolRendererV2Widget : QgsRendererV2Widget

public slots:
void changeGraduatedSymbol();
void graduatedColumnChanged();
void setExpression();
void graduatedColumnChanged( QString field );
void classifyGraduated();
void reapplyColorRamp();
void rangesDoubleClicked( const QModelIndex & idx );
Expand Down Expand Up @@ -45,9 +44,6 @@ class QgsGraduatedSymbolRendererV2Widget : QgsRendererV2Widget
QList<int> selectedClasses();
QgsRangeList selectedRanges();

//! populate column combos in categorized and graduated page
void populateColumns();

void changeRangeSymbol( int rangeIdx );
void changeRange( int rangeIdx );

Expand Down
63 changes: 15 additions & 48 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -58,8 +58,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
mPreviewSize = 24;

// main layer label-enabling connections
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), cboFieldName, SLOT( setEnabled( bool ) ) );
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), btnExpression, SLOT( setEnabled( bool ) ) );
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), mFieldExpressionWidget, SLOT( setEnabled( bool ) ) );
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), mLabelingFrame, SLOT( setEnabled( bool ) ) );

// connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
Expand Down Expand Up @@ -96,7 +95,6 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
connect( mLimitLabelChkBox, SIGNAL( toggled( bool ) ), mLimitLabelSpinBox, SLOT( setEnabled( bool ) ) );

connect( btnEngineSettings, SIGNAL( clicked() ), this, SLOT( showEngineConfigDialog() ) );
connect( btnExpression, SIGNAL( clicked() ), this, SLOT( showExpressionDialog() ) );

// set placement methods page based on geometry type
switch ( layer->geometryType() )
Expand All @@ -121,7 +119,14 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
mDirectSymbolsFrame->setVisible( layer->geometryType() == QGis::Line );
mMinSizeFrame->setVisible( layer->geometryType() != QGis::Point );

populateFieldNames(); // this is just for label text combo box
// field combo and expression button
mFieldExpressionWidget->setLayer( mLayer );
QgsDistanceArea myDa;
myDa.setSourceCrs( mLayer->crs().srsid() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
mFieldExpressionWidget->setGeomCalculator( myDa );

populateFontCapitalsComboBox();

// set up quadrant offset button group
Expand Down Expand Up @@ -234,15 +239,11 @@ void QgsLabelingGui::init()

// enable/disable main options based upon whether layer is being labeled
chkEnableLabeling->setChecked( lyr.enabled );
cboFieldName->setEnabled( chkEnableLabeling->isChecked() );
btnExpression->setEnabled( chkEnableLabeling->isChecked() );
mFieldExpressionWidget->setEnabled( chkEnableLabeling->isChecked() );
mLabelingFrame->setEnabled( chkEnableLabeling->isChecked() );

// add the current expression to the bottom of the list
if ( lyr.isExpression && !lyr.fieldName.isEmpty() )
cboFieldName->addItem( lyr.fieldName );

cboFieldName->setCurrentIndex( cboFieldName->findText( lyr.fieldName ) );
// set the current field or add the current expression to the bottom of the list
mFieldExpressionWidget->setField( lyr.fieldName );

// populate placement options
int distUnitIndex = lyr.distInMapUnits ? 1 : 0;
Expand Down Expand Up @@ -509,10 +510,9 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()

lyr.enabled = chkEnableLabeling->isChecked();

lyr.fieldName = cboFieldName->currentText();
// Check if we are an expression. Also treats expressions with just a column name as non expressions,
// this saves time later so we don't have to parse the expression tree.
lyr.isExpression = mLayer->fieldNameIndex( lyr.fieldName ) == -1 && !lyr.fieldName.isEmpty();
bool isExpression;
lyr.fieldName = mFieldExpressionWidget->currentField( &isExpression );
lyr.isExpression = isExpression;

lyr.dist = 0;
lyr.placementFlags = 0;
Expand Down Expand Up @@ -775,15 +775,6 @@ void QgsLabelingGui::setDataDefinedProperty( const QgsDataDefinedButton* ddBtn,
lyr.setDataDefinedProperty( p, map.value( "active" ).toInt(), map.value( "useexpr" ).toInt(), map.value( "expression" ), map.value( "field" ) );
}

void QgsLabelingGui::populateFieldNames()
{
const QgsFields& fields = mLayer->pendingFields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
cboFieldName->addItem( fields[idx].name() );
}
}

void QgsLabelingGui::populateDataDefinedButtons( QgsPalLayerSettings& s )
{
// don't register enable/disable siblings, since visual feedback from data defined buttons should be enough,
Expand Down Expand Up @@ -1175,30 +1166,6 @@ void QgsLabelingGui::showEngineConfigDialog()
dlg.exec();
}

void QgsLabelingGui::showExpressionDialog()
{
QgsExpressionBuilderDialog dlg( mLayer, cboFieldName->currentText() , this );
dlg.setWindowTitle( tr( "Expression based label" ) );

QgsDistanceArea myDa;
myDa.setSourceCrs( mLayer->crs().srsid() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
dlg.setGeomCalculator( myDa );

if ( dlg.exec() == QDialog::Accepted )
{
QString expression = dlg.expressionText();
//Only add the expression if the user has entered some text.
if ( !expression.isEmpty() )
{
cboFieldName->addItem( expression );
cboFieldName->setCurrentIndex( cboFieldName->count() - 1 );
}
}
activateWindow(); // set focus back parent
}

void QgsLabelingGui::syncDefinedCheckboxFrame( QgsDataDefinedButton* ddBtn, QCheckBox* chkBx, QFrame* f )
{
if ( ddBtn->isActive() && !chkBx->isChecked() )
Expand Down
1 change: 0 additions & 1 deletion src/app/qgslabelinggui.h
Expand Up @@ -45,7 +45,6 @@ class APP_EXPORT QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void apply();
void changeTextColor( const QColor &color );
void showEngineConfigDialog();
void showExpressionDialog();
void changeBufferColor( const QColor &color );

void updateUi();
Expand Down
3 changes: 3 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -89,6 +89,7 @@ qgsexpressionselectiondialog.cpp
qgsextentgroupbox.cpp
qgsfeatureselectiondlg.cpp
qgsfieldcombobox.cpp
qgsfieldexpressionwidget.cpp
qgsfieldmodel.cpp
qgsfieldvalidator.cpp
qgsfiledropedit.cpp
Expand Down Expand Up @@ -233,6 +234,7 @@ qgsexpressionselectiondialog.h
qgsextentgroupbox.h
qgsfeatureselectiondlg.h
qgsfieldcombobox.h
qgsfieldexpressionwidget.h
qgsfieldmodel.h
qgsfieldvalidator.h
qgsfilterlineedit.h
Expand Down Expand Up @@ -303,6 +305,7 @@ qgsexpressionhighlighter.h
qgsexpressionselectiondialog.h
qgsfeatureselectiondlg.h
qgsfieldcombobox.h
qgsfieldexpressionwidget.h
qgsfieldmodel.h
qgsfieldvalidator.h
qgsfiledropedit.h
Expand Down
14 changes: 14 additions & 0 deletions src/gui/qgsfieldcombobox.cpp
Expand Up @@ -27,10 +27,24 @@ QgsFieldComboBox::QgsFieldComboBox( QWidget *parent ) :
}

void QgsFieldComboBox::setLayer( QgsMapLayer *layer )
{
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
if ( vl )
{
setLayer( vl );
}
}

void QgsFieldComboBox::setLayer( QgsVectorLayer *layer )
{
mFieldModel->setLayer( layer );
}

QgsVectorLayer *QgsFieldComboBox::layer()
{
return mFieldModel->layer();
}

void QgsFieldComboBox::setField( QString fieldName )
{
QModelIndex idx = mFieldModel->indexFromName( fieldName );
Expand Down

0 comments on commit 5093394

Please sign in to comment.