From c7ffdfa5e991743e862f10ae2dbec2c05c4b795c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 19 Jul 2016 11:59:44 +1000 Subject: [PATCH] Make sure variable editor widgets always show current variables On behalf of Faunalia, sponsored by ENEL --- python/core/composer/qgscomposition.sip | 5 +++++ python/core/qgsapplication.sip | 16 ++++++++++++++ python/core/qgsproject.sip | 12 +++++++++++ src/app/composer/qgscomposeritemwidget.cpp | 24 ++++++++++++++++----- src/app/composer/qgscomposeritemwidget.h | 3 +-- src/app/composer/qgscompositionwidget.cpp | 25 ++++++++++++++++------ src/app/composer/qgscompositionwidget.h | 2 ++ src/app/qgsoptions.cpp | 6 ++++++ src/app/qgsprojectproperties.cpp | 1 + src/core/composer/qgscomposition.cpp | 5 +++++ src/core/composer/qgscomposition.h | 5 +++++ src/core/qgsapplication.cpp | 5 +++++ src/core/qgsapplication.h | 16 ++++++++++++++ src/core/qgsproject.cpp | 5 +++++ src/core/qgsproject.h | 12 +++++++++++ src/gui/qgsvariableeditorwidget.h | 14 ++++++------ tests/src/core/testqgsapplication.cpp | 1 + tests/src/core/testqgscomposition.cpp | 15 +++++++++++++ tests/src/core/testqgsproject.cpp | 8 +++++++ 19 files changed, 161 insertions(+), 19 deletions(-) diff --git a/python/core/composer/qgscomposition.sip b/python/core/composer/qgscomposition.sip index 9977f310f5a..4fc3b4b6edd 100644 --- a/python/core/composer/qgscomposition.sip +++ b/python/core/composer/qgscomposition.sip @@ -900,4 +900,9 @@ class QgsComposition : QGraphicsScene /** Is emitted when the composition has an updated status bar message for the composer window*/ void statusMsgChanged( QString message ); + + /** Emitted whenever the expression variables stored in the composition have been changed. + * @note added in QGIS 3.0 + */ + void variablesChanged(); }; diff --git a/python/core/qgsapplication.sip b/python/core/qgsapplication.sip index 40ead0b2a77..be66870d819 100644 --- a/python/core/qgsapplication.sip +++ b/python/core/qgsapplication.sip @@ -379,7 +379,23 @@ static void qtgui_UpdatePyArgv(PyObject *argvlist, int argc, char **argv) bool x11EventFilter ( XEvent * event ); %End + public slots: + + /** Causes the application instance to emit the settingsChanged() signal. This should + * be called whenever global, application-wide settings are altered to advise listeners + * that they may need to update their state. + * @see settingsChanged() + * @note added in QGIS 3.0 + */ + void emitSettingsChanged(); + signals: //! @note not available in python bindings // void preNotify( QObject * receiver, QEvent * event, bool * done ); + + /** Emitted whenever any global, application-wide settings are changed. + * @note added in QGIS 3.0 + * @see emitSettingsChanged() + */ + void settingsChanged(); }; diff --git a/python/core/qgsproject.sip b/python/core/qgsproject.sip index 757841ef4fa..c71b114ad19 100644 --- a/python/core/qgsproject.sip +++ b/python/core/qgsproject.sip @@ -397,6 +397,11 @@ class QgsProject : QObject //! Emitted when the home path of the project changes void homePathChanged(); + /** Emitted whenever the expression variables stored in the project have been changed. + * @note added in QGIS 3.0 + */ + void variablesChanged(); + public slots: /** @@ -408,6 +413,13 @@ class QgsProject : QObject */ void setDirty( bool b = true ); + /** Causes the project to emit the variablesChanged() signal. This should + * be called whenever expression variables related to the project are changed. + * @see variablesChanged() + * @note added in QGIS 3.0 + */ + void emitVariablesChanged(); + private: QgsProject(); // private 'cause it's a singleton diff --git a/src/app/composer/qgscomposeritemwidget.cpp b/src/app/composer/qgscomposeritemwidget.cpp index f0f8a5cfad6..09c012e9b58 100644 --- a/src/app/composer/qgscomposeritemwidget.cpp +++ b/src/app/composer/qgscomposeritemwidget.cpp @@ -23,6 +23,7 @@ #include "qgspoint.h" #include "qgsdatadefinedbutton.h" #include "qgsexpressioncontext.h" +#include "qgsproject.h" #include #include @@ -109,6 +110,14 @@ QgsVectorLayer* QgsComposerItemBaseWidget::atlasCoverageLayer() const //QgsComposerItemWidget +void QgsComposerItemWidget::updateVariables() +{ + QgsExpressionContext* context = mItem->createExpressionContext(); + mVariableEditor->setContext( context ); + mVariableEditor->setEditableScopeIndex( context->scopeCount() - 1 ); + delete context; +} + QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* item ) : QgsComposerItemBaseWidget( parent, item ) , mItem( item ) @@ -141,12 +150,17 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* connect( mTransparencySlider, SIGNAL( valueChanged( int ) ), mTransparencySpnBx, SLOT( setValue( int ) ) ); - QgsExpressionContext* context = mItem->createExpressionContext(); - mVariableEditor->setContext( context ); - mVariableEditor->setEditableScopeIndex( context->scopeCount() - 1 ); - delete context; - + updateVariables(); connect( mVariableEditor, SIGNAL( scopeChanged() ), this, SLOT( variablesChanged() ) ); + // listen out for variable edits + QgsApplication* app = qobject_cast( QgsApplication::instance() ); + if ( app ) + { + connect( app, SIGNAL( settingsChanged() ), this, SLOT( updateVariables() ) ); + } + connect( QgsProject::instance(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) ); + if ( mItem->composition() ) + connect( mItem->composition(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) ); //connect atlas signals to data defined buttons QgsAtlasComposition* atlas = atlasComposition(); diff --git a/src/app/composer/qgscomposeritemwidget.h b/src/app/composer/qgscomposeritemwidget.h index f5e884c9271..10ea98bcf4e 100644 --- a/src/app/composer/qgscomposeritemwidget.h +++ b/src/app/composer/qgscomposeritemwidget.h @@ -142,8 +142,7 @@ class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo private slots: void variablesChanged(); - - + void updateVariables(); }; #endif //QGSCOMPOSERITEMWIDGET_H diff --git a/src/app/composer/qgscompositionwidget.cpp b/src/app/composer/qgscompositionwidget.cpp index 74207717d31..c6b4d97cf38 100644 --- a/src/app/composer/qgscompositionwidget.cpp +++ b/src/app/composer/qgscompositionwidget.cpp @@ -23,6 +23,7 @@ #include "qgssymbolv2selectordialog.h" #include "qgssymbollayerv2utils.h" #include "qgsexpressioncontext.h" +#include "qgsproject.h" #include #include #include //for screen resolution @@ -45,13 +46,15 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c ) //read with/height from composition and find suitable entries to display displayCompositionWidthHeight(); - mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() ); - mVariableEditor->context()->appendScope( QgsExpressionContextUtils::projectScope() ); - mVariableEditor->context()->appendScope( QgsExpressionContextUtils::compositionScope( mComposition ) ); - mVariableEditor->reloadContext(); - mVariableEditor->setEditableScopeIndex( 2 ); - + updateVariables(); connect( mVariableEditor, SIGNAL( scopeChanged() ), this, SLOT( variablesChanged() ) ); + // listen out for variable edits + QgsApplication* app = qobject_cast( QgsApplication::instance() ); + if ( app ) + { + connect( app, SIGNAL( settingsChanged() ), this, SLOT( updateVariables() ) ); + } + connect( QgsProject::instance(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) ); if ( mComposition ) { @@ -209,6 +212,16 @@ void QgsCompositionWidget::resizeMarginsChanged() mLeftMarginSpinBox->value() ); } +void QgsCompositionWidget::updateVariables() +{ + QgsExpressionContext context; + context << QgsExpressionContextUtils::globalScope() + << QgsExpressionContextUtils::projectScope() + << QgsExpressionContextUtils::compositionScope( mComposition ); + mVariableEditor->setContext( &context ); + mVariableEditor->setEditableScopeIndex( 2 ); +} + void QgsCompositionWidget::setDataDefinedProperty( const QgsDataDefinedButton* ddBtn, QgsComposerObject::DataDefinedProperty property ) { if ( !mComposition ) diff --git a/src/app/composer/qgscompositionwidget.h b/src/app/composer/qgscompositionwidget.h index e957cf954d7..62f01ed8ee8 100644 --- a/src/app/composer/qgscompositionwidget.h +++ b/src/app/composer/qgscompositionwidget.h @@ -83,6 +83,8 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase void resizeMarginsChanged(); + void updateVariables(); + private: QgsComposition* mComposition; QMap mPaperMap; diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 74a2c0980f7..e04aa4e1135 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -1481,6 +1481,12 @@ void QgsOptions::saveOptions() } saveDefaultDatumTransformations(); + + QgsApplication* app = qobject_cast( QgsApplication::instance() ); + if ( app ) + { + app->emitSettingsChanged(); + } } void QgsOptions::rejectOptions() diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index 00f32d9f5d5..ac6d19df7c9 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -1187,6 +1187,7 @@ void QgsProjectProperties::apply() //save variables QgsExpressionContextUtils::setProjectVariables( mVariableEditor->variablesInActiveScope() ); + QgsProject::instance()->emitVariablesChanged(); emit refresh(); } diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index 17d351cdcd4..d867632bed0 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -1069,6 +1069,8 @@ bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocu updateBounds(); + emit variablesChanged(); + return true; } @@ -3506,6 +3508,9 @@ void QgsComposition::setDataDefinedProperty( const QgsComposerObject::DataDefine void QgsComposition::setCustomProperty( const QString& key, const QVariant& value ) { mCustomProperties.setValue( key, value ); + + if ( key.startsWith( "variable" ) ) + emit variablesChanged(); } QVariant QgsComposition::customProperty( const QString& key, const QVariant& defaultValue ) const diff --git a/src/core/composer/qgscomposition.h b/src/core/composer/qgscomposition.h index ec9165062b4..1eed72cf4f5 100644 --- a/src/core/composer/qgscomposition.h +++ b/src/core/composer/qgscomposition.h @@ -1146,6 +1146,11 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene /** Is emitted when the composition has an updated status bar message for the composer window*/ void statusMsgChanged( const QString& message ); + /** Emitted whenever the expression variables stored in the composition have been changed. + * @note added in QGIS 3.0 + */ + void variablesChanged(); + friend class QgsComposerObject; //for accessing dataDefinedEvaluate, readDataDefinedPropertyMap and writeDataDefinedPropertyMap friend class QgsComposerModel; //for accessing updateZValues (should not be public) friend class TestQgsComposition; diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 0c94a52619c..420dfe25ea3 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -1371,3 +1371,8 @@ void QgsApplication::setMaxThreads( int maxThreads ) QgsDebugMsg( QString( "set QThreadPool max thread count to %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) ); } +void QgsApplication::emitSettingsChanged() +{ + emit settingsChanged(); +} + diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index 36cc6a12329..9b42aeffa71 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -364,10 +364,26 @@ class CORE_EXPORT QgsApplication : public QApplication } #endif + public slots: + + /** Causes the application instance to emit the settingsChanged() signal. This should + * be called whenever global, application-wide settings are altered to advise listeners + * that they may need to update their state. + * @see settingsChanged() + * @note added in QGIS 3.0 + */ + void emitSettingsChanged(); + signals: //! @note not available in python bindings void preNotify( QObject * receiver, QEvent * event, bool * done ); + /** Emitted whenever any global, application-wide settings are changed. + * @note added in QGIS 3.0 + * @see emitSettingsChanged() + */ + void settingsChanged(); + private: static void copyPath( const QString& src, const QString& dst ); static QObject* ABISYM( mFileOpenEventReceiver ); diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 495e494ed0b..2713c8057ba 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -412,6 +412,11 @@ void QgsProject::setDirty( bool b ) imp_->dirty = b; } +void QgsProject::emitVariablesChanged() +{ + emit variablesChanged(); +} + void QgsProject::setFileName( const QString& name ) { if ( name == imp_->file.fileName() ) diff --git a/src/core/qgsproject.h b/src/core/qgsproject.h index c6dd8765687..c133a455fe1 100644 --- a/src/core/qgsproject.h +++ b/src/core/qgsproject.h @@ -462,6 +462,11 @@ class CORE_EXPORT QgsProject : public QObject //! Emitted when the home path of the project changes void homePathChanged(); + /** Emitted whenever the expression variables stored in the project have been changed. + * @note added in QGIS 3.0 + */ + void variablesChanged(); + public slots: /** @@ -473,6 +478,13 @@ class CORE_EXPORT QgsProject : public QObject */ void setDirty( bool b = true ); + /** Causes the project to emit the variablesChanged() signal. This should + * be called whenever expression variables related to the project are changed. + * @see variablesChanged() + * @note added in QGIS 3.0 + */ + void emitVariablesChanged(); + private slots: void onMapLayersAdded( const QList& layers ); void cleanTransactionGroups( bool force = false ); diff --git a/src/gui/qgsvariableeditorwidget.h b/src/gui/qgsvariableeditorwidget.h index 951c82b302b..58d9c0bf21d 100644 --- a/src/gui/qgsvariableeditorwidget.h +++ b/src/gui/qgsvariableeditorwidget.h @@ -65,12 +65,6 @@ class GUI_EXPORT QgsVariableEditorWidget : public QWidget */ QgsExpressionContext* context() const { return mContext.data(); } - /** Reloads all scopes from the editor's current context. This method should be called - * after adding or removing scopes from the attached context. - * @see context() - */ - void reloadContext(); - /** Sets the editable scope for the widget. Only variables from the editable scope can * be modified by users. * @param scopeIndex index of current editable scope. Set to -1 to disable @@ -107,6 +101,14 @@ class GUI_EXPORT QgsVariableEditorWidget : public QWidget */ QgsStringMap variablesInActiveScope() const; + public slots: + + /** Reloads all scopes from the editor's current context. This method should be called + * after adding or removing scopes from the attached context. + * @see context() + */ + void reloadContext(); + signals: /** Emitted when the user has modified a scope using the widget. diff --git a/tests/src/core/testqgsapplication.cpp b/tests/src/core/testqgsapplication.cpp index ab742599b11..35a127569c8 100644 --- a/tests/src/core/testqgsapplication.cpp +++ b/tests/src/core/testqgsapplication.cpp @@ -83,6 +83,7 @@ void TestQgsApplication::platformName() QCOMPARE( QgsApplication::platform(), QString( "desktop" ) ); } + void TestQgsApplication::checkPaths() { QString myPath = QgsApplication::authorsFilePath(); diff --git a/tests/src/core/testqgscomposition.cpp b/tests/src/core/testqgscomposition.cpp index c58efd630f7..71fd1956b43 100644 --- a/tests/src/core/testqgscomposition.cpp +++ b/tests/src/core/testqgscomposition.cpp @@ -53,6 +53,7 @@ class TestQgsComposition : public QObject void resizeToContentsMargin(); void resizeToContentsMultiPage(); void georeference(); + void variablesEdited(); private: QgsComposition *mComposition; @@ -583,5 +584,19 @@ void TestQgsComposition::georeference() delete composition; } +void TestQgsComposition::variablesEdited() +{ + QgsMapSettings ms; + QgsComposition c( ms ); + QSignalSpy spyVariablesChanged( &c, SIGNAL( variablesChanged() ) ); + + c.setCustomProperty( "not a variable", "1" ); + QVERIFY( spyVariablesChanged.count() == 0 ); + c.setCustomProperty( "variableNames", "1" ); + QVERIFY( spyVariablesChanged.count() == 1 ); + c.setCustomProperty( "variableValues", "1" ); + QVERIFY( spyVariablesChanged.count() == 2 ); +} + QTEST_MAIN( TestQgsComposition ) #include "testqgscomposition.moc" diff --git a/tests/src/core/testqgsproject.cpp b/tests/src/core/testqgsproject.cpp index d8a7414709a..7a8b3c7f604 100644 --- a/tests/src/core/testqgsproject.cpp +++ b/tests/src/core/testqgsproject.cpp @@ -31,6 +31,7 @@ class TestQgsProject : public QObject void testReadPath(); void testProjectUnits(); + void variablesChanged(); }; void TestQgsProject::init() @@ -124,6 +125,13 @@ void TestQgsProject::testProjectUnits() QCOMPARE( prj->areaUnits(), QgsUnitTypes::Acres ); } +void TestQgsProject::variablesChanged() +{ + QSignalSpy spyVariablesChanged( QgsProject::instance(), SIGNAL( variablesChanged() ) ); + QgsProject::instance()->emitVariablesChanged(); + QVERIFY( spyVariablesChanged.count() == 1 ); +} + QTEST_MAIN( TestQgsProject ) #include "testqgsproject.moc"