Skip to content

Commit

Permalink
Split displayField into displayExpression and mapTipTemplate (#1973)
Browse files Browse the repository at this point in the history
Previously there was the expressionField (a field name or an expression)
mainly used for the feature list in the form view of the dual view.
On the other hand there was the displayField which could contain either
a simple field name or a complex HTML structure with embedded expressions.
And to know what it was you could compare it's content with the field names, if
a field name matched, you used it as a displayField (original purpose) and
if not... well, you could deal with HTML if you had a use for it.

The main problem is that there are two different usages for this kind of
thing

 * plain text identifier (field or expression)
 * pretty, rich text feature info

This commit cleans up with this. You want rich text and a lot of info:
go for mapTipTemplate.
You want a plain text string to identify features: go for
the displayExpression.
  • Loading branch information
m-kuhn authored Aug 2, 2016
1 parent 2134112 commit 2cf9243
Show file tree
Hide file tree
Showing 14 changed files with 365 additions and 483 deletions.
2 changes: 2 additions & 0 deletions doc/api_break.dox
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ plugins calling this method will need to be updated.</li>
setExcludeAttributesWms()</li>
<li>excludeAttributesWFS() and setExcludeAttributesWFS() have been renamed to excludeAttributesWfs() and
setExcludeAttributesWfs()</li>
<li>The displayField property has been separated from the mapTip. For a plain text short title use the
displayExpression instead. For the map tip use mapTipTemplate() instead.</li>
<li>changeGeometry() now accepts a geometry reference, not a pointer.</li>
<li>The geometryChanged() signal now uses a const QgsGeometry reference.</li>
<li>The deprecated removePolygonIntersections has been removed.</li>
Expand Down
45 changes: 40 additions & 5 deletions python/core/qgsvectorlayer.sip
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,14 @@ class QgsVectorLayer : QgsMapLayer
/** Returns a comment for the data in the layer */
QString dataComment() const;

/** Set the primary display field to be used in the identify results dialog */
void setDisplayField( const QString& fldName = "" );

/** Returns the primary display field name used in the identify results dialog */
const QString displayField() const;
/**
* This is a shorthand for accessing the displayExpression if it is a simple field.
* If the displayExpression is more complex than a simple field, a null string will
* be returned.
*
* @see displayExpression
*/
QString displayField() const;

/** Set the preview expression, used to create a human readable preview string.
* Used e.g. in the attribute table feature list. Uses { @link QgsExpression }.
Expand Down Expand Up @@ -1414,6 +1417,24 @@ class QgsVectorLayer : QgsMapLayer
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& attributeTableConfig );

/**
* The mapTip is a pretty, html representation for feature information.
*
* It may also contain embedded expressions.
*
* @note added in 3.0
*/
QString mapTipTemplate() const;

/**
* The mapTip is a pretty, html representation for feature information.
*
* It may also contain embedded expressions.
*
* @note added in 3.0
*/
void setMapTipTemplate( const QString& mapTipTemplate );

public slots:
/**
* Select feature by its ID
Expand Down Expand Up @@ -1675,6 +1696,20 @@ class QgsVectorLayer : QgsMapLayer
*/
void writeCustomSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage ) const;

/**
* Emitted when the map tip changes
*
* @note added in 3.0
*/
void mapTipTemplateChanged();

/**
* Emitted when the display expression changes
*
* @note added in 3.0
*/
void displayExpressionChanged();

/**
* Signals an error related to this vector layer.
*/
Expand Down
33 changes: 12 additions & 21 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3150,6 +3150,8 @@ void QgisApp::createMapTips()
connect( mpMapTipsTimer, SIGNAL( timeout() ), this, SLOT( showMapTip() ) );
// set the interval to 0.850 seconds - timer will be started next time the mouse moves
mpMapTipsTimer->setInterval( 850 );
mpMapTipsTimer->setSingleShot( true );

// Create the maptips object
mpMaptip = new QgsMapTip();
}
Expand Down Expand Up @@ -10158,33 +10160,22 @@ void QgisApp::removeMapToolMessage()
// Show the maptip using tooltip
void QgisApp::showMapTip()
{
// Stop the timer while we look for a maptip
mpMapTipsTimer->stop();
QPoint myPointerPos = mMapCanvas->mouseLastXY();

// Only show tooltip if the mouse is over the canvas
if ( mMapCanvas->underMouse() )
// Make sure there is an active layer before proceeding
QgsMapLayer* mypLayer = mMapCanvas->currentLayer();
if ( mypLayer )
{
QPoint myPointerPos = mMapCanvas->mouseLastXY();

// Make sure there is an active layer before proceeding
QgsMapLayer* mypLayer = mMapCanvas->currentLayer();
if ( mypLayer )
//QgsDebugMsg("Current layer for maptip display is: " + mypLayer->source());
// only process vector layers
if ( mypLayer->type() == QgsMapLayer::VectorLayer )
{
//QgsDebugMsg("Current layer for maptip display is: " + mypLayer->source());
// only process vector layers
if ( mypLayer->type() == QgsMapLayer::VectorLayer )
// Show the maptip if the maptips button is depressed
if ( mMapTipsVisible )
{
// Show the maptip if the maptips button is depressed
if ( mMapTipsVisible )
{
mpMaptip->showMapTip( mypLayer, mLastMapPosition, myPointerPos, mMapCanvas );
}
mpMaptip->showMapTip( mypLayer, mLastMapPosition, myPointerPos, mMapCanvas );
}
}
else
{
showStatusMessage( tr( "Maptips require an active layer" ) );
}
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/app/qgsidentifyresultsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,10 +473,11 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat
const QgsFields &fields = vlayer->fields();
QgsAttributes attrs = f.attributes();
bool featureLabeled = false;

for ( int i = 0; i < attrs.count(); ++i )
{
if ( i >= fields.count() )
continue;
break;

if ( vlayer->editFormConfig()->widgetType( i ) == "Hidden" )
{
Expand Down Expand Up @@ -524,8 +525,14 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat

if ( !featureLabeled )
{
featItem->setText( 0, tr( "feature id" ) );
featItem->setText( 1, QString::number( f.id() ) );
featItem->setText( 0, tr( "Title" ) );
QgsExpressionContext context;
context << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope()
<< QgsExpressionContextUtils::layerScope( vlayer );
context.setFeature( f );

featItem->setText( 1, QgsExpression( vlayer->displayExpression() ).evaluate( &context ).toString() );
}

// table
Expand Down
95 changes: 26 additions & 69 deletions src/app/qgsvectorlayerproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,18 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(

connect( mOptionsStackedWidget, SIGNAL( currentChanged( int ) ), this, SLOT( mOptionsStackedWidget_CurrentChanged( int ) ) );

fieldComboBox->setLayer( lyr );
displayFieldComboBox->setLayer( lyr );
connect( insertFieldButton, SIGNAL( clicked() ), this, SLOT( insertField() ) );
connect( insertExpressionButton, SIGNAL( clicked() ), this, SLOT( insertExpression() ) );

// connections for Map Tip display
connect( htmlRadio, SIGNAL( toggled( bool ) ), htmlMapTip, SLOT( setEnabled( bool ) ) );
connect( htmlRadio, SIGNAL( toggled( bool ) ), insertFieldButton, SLOT( setEnabled( bool ) ) );
connect( htmlRadio, SIGNAL( toggled( bool ) ), fieldComboBox, SLOT( setEnabled( bool ) ) );
connect( htmlRadio, SIGNAL( toggled( bool ) ), insertExpressionButton, SLOT( setEnabled( bool ) ) );
connect( fieldComboRadio, SIGNAL( toggled( bool ) ), displayFieldComboBox, SLOT( setEnabled( bool ) ) );
mContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope()
<< QgsExpressionContextUtils::atlasScope( nullptr )
<< QgsExpressionContextUtils::mapSettingsScope( QgisApp::instance()->mapCanvas()->mapSettings() )
<< QgsExpressionContextUtils::layerScope( mLayer );

mMapTipExpressionFieldWidget->setLayer( lyr );
mMapTipExpressionFieldWidget->registerGetExpressionContextCallback( &_getExpressionContext, this );
mDisplayExpressionWidget->setLayer( lyr );
mDisplayExpressionWidget->registerGetExpressionContextCallback( &_getExpressionContext, this );

connect( mInsertExpressionButton, SIGNAL( clicked() ), this, SLOT( insertFieldOrExpression() ) );

if ( !mLayer )
return;
Expand Down Expand Up @@ -326,57 +327,15 @@ void QgsVectorLayerProperties::addPropertiesPageFactory( QgsMapLayerConfigWidget
mOptionsStackedWidget->addWidget( page );
}

void QgsVectorLayerProperties::insertField()
void QgsVectorLayerProperties::insertFieldOrExpression()
{
// Convert the selected field to an expression and
// insert it into the action at the cursor position
QString field = "[% \"";
field += fieldComboBox->currentField();
field += "\" %]";
htmlMapTip->insertPlainText( field );
}

void QgsVectorLayerProperties::insertExpression()
{
QString selText = htmlMapTip->textCursor().selectedText();

// edit the selected expression if there's one
if ( selText.startsWith( "[%" ) && selText.endsWith( "%]" ) )
selText = selText.mid( 2, selText.size() - 4 );

// display the expression builder
QgsExpressionContext context;
context << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope()
<< QgsExpressionContextUtils::atlasScope( nullptr )
<< QgsExpressionContextUtils::mapSettingsScope( QgisApp::instance()->mapCanvas()->mapSettings() )
<< QgsExpressionContextUtils::layerScope( mLayer );

QgsExpressionBuilderDialog dlg( mLayer, selText.replace( QChar::ParagraphSeparator, '\n' ), this, "generic", context );
dlg.setWindowTitle( tr( "Insert expression" ) );
if ( dlg.exec() == QDialog::Accepted )
{
QString expression = dlg.expressionBuilder()->expressionText();
//Only add the expression if the user has entered some text.
if ( !expression.isEmpty() )
{
htmlMapTip->insertPlainText( "[%" + expression + "%]" );
}
}
}
QString expression = "[% \"";
expression += mMapTipExpressionFieldWidget->asExpression();
expression += "\" %]";

void QgsVectorLayerProperties::setDisplayField( const QString& name )
{
if ( mLayer->fields().fieldNameIndex( name ) == -1 )
{
htmlRadio->setChecked( true );
htmlMapTip->setPlainText( name );
}
else
{
fieldComboRadio->setChecked( true );
displayFieldComboBox->setField( name );
}
mMapTipWidget->insertText( expression );
}

//! @note in raster props, this method is called sync()
Expand All @@ -403,7 +362,8 @@ void QgsVectorLayerProperties::syncToLayer()
txtSubsetSQL->setEnabled( false );
setPbnQueryBuilderEnabled();

setDisplayField( mLayer->displayField() );
mMapTipWidget->setText( mLayer->mapTipTemplate() );
mDisplayExpressionWidget->setField( mLayer->displayExpression() );

// set up the scale based layer visibility stuff....
mScaleRangeWidget->setScaleRange( 1.0 / mLayer->maximumScale(), 1.0 / mLayer->minimumScale() ); // caution: layer uses scale denoms, widget uses true scales
Expand Down Expand Up @@ -513,16 +473,8 @@ void QgsVectorLayerProperties::apply()
}
}

// update the display field
if ( htmlRadio->isChecked() )
{
mLayer->setDisplayField( htmlMapTip->toPlainText() );
}

if ( fieldComboRadio->isChecked() )
{
mLayer->setDisplayField( displayFieldComboBox->currentField() );
}
mLayer->setDisplayExpression( mDisplayExpressionWidget->currentField() );
mLayer->setMapTipTemplate( mMapTipWidget->text() );

mLayer->actions()->clearActions();
Q_FOREACH ( const QgsAction& action, mActionDialog->actions() )
Expand Down Expand Up @@ -1214,6 +1166,11 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorJoinInfo& joi
mJoinTreeWidget->setCurrentItem( joinItem );
}

QgsExpressionContext QgsVectorLayerProperties::_getExpressionContext( const void* context )
{
return static_cast<const QgsVectorLayerProperties*>( context )->mContext;
}

void QgsVectorLayerProperties::openPanel( QgsPanelWidget *panel )
{
QDialog* dlg = new QDialog();
Expand Down
14 changes: 6 additions & 8 deletions src/app/qgsvectorlayerproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
QString displayName();
void setRendererDirty( bool ) {}

/** Sets the attribute that is used in the Identify Results dialog box*/
void setDisplayField( const QString& name );

/** Adds an attribute to the table (but does not commit it yet)
@param field the field to add
@return false in case of a name conflict, true in case of success */
Expand All @@ -75,10 +72,7 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private

public slots:

/** Insert a field in the expression text for the map tip **/
void insertField();

void insertExpression();
void insertFieldOrExpression();

/** Reset to original (vector layer) values */
void syncToLayer();
Expand Down Expand Up @@ -146,7 +140,7 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
*/
void updateFieldsPropertiesDialog();

protected:
private:

void saveStyleAs( StyleType styleType );

Expand Down Expand Up @@ -195,6 +189,10 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
/** Adds a new join to mJoinTreeWidget*/
void addJoinToTreeWidget( const QgsVectorJoinInfo& join , const int insertIndex = -1 );

QgsExpressionContext mContext;

static QgsExpressionContext _getExpressionContext( const void* context );

private slots:
void openPanel( QgsPanelWidget* panel );
};
Expand Down
Loading

8 comments on commit 2cf9243

@rduivenvoorde
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm, looking into the source of maptips because I have some troubles on Windows with it.

But: so if I want to use expressions, I use.. displayExpression... huh oh no: mapTipTemplate :-)

what about mapTipTemplate (or mapTipExpression) and maptipField (just because we can as long as there is not a QGIS3)...

@DelazJ
Copy link
Contributor

@DelazJ DelazJ commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-kuhn i just wonder if these changes should not be mentioned in doc (worth a ticket). Afaics, (at least) the GUI, hence the UX, has evolved

@m-kuhn
Copy link
Member Author

@m-kuhn m-kuhn commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rduivenvoorde

both support expressions:

  • one generates rich text (mapTipTemplate)
  • the other one is plain text (displayExpression)

@DelazJ, I also think they sure deserve docs

@DelazJ
Copy link
Contributor

@DelazJ DelazJ commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-kuhn
Copy link
Member Author

@m-kuhn m-kuhn commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phew, glad I wrote a comprehensive commit message there 😄

@DelazJ
Copy link
Contributor

@DelazJ DelazJ commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep on writing comprehensive commit message (and a bit less developer/designer-oriented 😉 ). It's appreciable

@m-kuhn
Copy link
Member Author

@m-kuhn m-kuhn commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this commit in particular, the impact for developers was bigger than for the enduser.

But I'll think of your statement in the next commit message I write 👍

@DelazJ
Copy link
Contributor

@DelazJ DelazJ commented on 2cf9243 Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, reason why I was in doubt about its documentation until I see the dialog in qt.

Please sign in to comment.