Skip to content
Permalink
Browse files

Split displayField into displayExpression and mapTipTemplate (#1973)

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 committed Aug 2, 2016
1 parent 2134112 commit 2cf9243edbec4d5dce82e4e7d86437d1c306fb2c
@@ -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>
@@ -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 }.
@@ -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
@@ -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.
*/
@@ -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();
}
@@ -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" ) );
}
}
}

@@ -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" )
{
@@ -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
@@ -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;
@@ -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()
@@ -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
@@ -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() )
@@ -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();
@@ -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 */
@@ -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();
@@ -146,7 +140,7 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
*/
void updateFieldsPropertiesDialog();

protected:
private:

void saveStyleAs( StyleType styleType );

@@ -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 );
};

8 comments on commit 2cf9243

@rduivenvoorde

This comment has been minimized.

Copy link
Contributor

@rduivenvoorde rduivenvoorde replied Sep 21, 2016

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

This comment has been minimized.

Copy link
Contributor

@DelazJ DelazJ replied Nov 23, 2016

@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

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Nov 23, 2016

@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

This comment has been minimized.

Copy link
Contributor

@DelazJ DelazJ replied Nov 23, 2016

@m-kuhn

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Nov 23, 2016

Phew, glad I wrote a comprehensive commit message there 😄

@DelazJ

This comment has been minimized.

Copy link
Contributor

@DelazJ DelazJ replied Nov 23, 2016

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

@m-kuhn

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Nov 23, 2016

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

This comment has been minimized.

Copy link
Contributor

@DelazJ DelazJ replied Nov 23, 2016

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

Please sign in to comment.
You can’t perform that action at this time.