From e2725a9ec503ab57172407c2db7881a14ae65abd Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Mon, 9 Mar 2020 04:42:34 +0200 Subject: [PATCH 1/8] Add the ability to edit user expressions --- .../qgsexpressionbuilderwidget.sip.in | 8 +++++ src/gui/qgsexpressionbuilderwidget.cpp | 30 +++++++++++++++++-- src/gui/qgsexpressionbuilderwidget.h | 7 +++++ src/ui/qgsexpressionbuilder.ui | 13 ++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in b/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in index 3e1d5052dce1..490668d6c9cb 100644 --- a/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in +++ b/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in @@ -379,6 +379,14 @@ Removes the selected expression from the stored user expressions, the selected expression must be a user stored expression. .. versionadded:: 3.12 +%End + + void editSelectedUserExpression(); +%Docstring +Edits the selected expression from the stored user expressions, +the selected expression must be a user stored expression. + +.. versionadded:: 3.14 %End const QList findExpressions( const QString &label ); diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index c7163f344e1b..5c546dba340f 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -61,6 +61,7 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) connect( lblPreview, &QLabel::linkActivated, this, &QgsExpressionBuilderWidget::lblPreview_linkActivated ); connect( mValuesListView, &QListView::doubleClicked, this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked ); connect( btnSaveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::storeCurrentUserExpression ); + connect( btnEditExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::editSelectedUserExpression ); connect( btnRemoveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::removeSelectedUserExpression ); connect( btnClearEditor, &QPushButton::pressed, txtExpressionString, &QgsCodeEditorExpression::clear ); @@ -83,6 +84,7 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) // Set icons for tool buttons btnSaveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSave.svg" ) ) ); + btnEditExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionToggleEditing.svg" ) ) ); btnRemoveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionDeleteSelected.svg" ) ) ); btnClearEditor->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileNew.svg" ) ) ); @@ -267,8 +269,10 @@ void QgsExpressionBuilderWidget::currentChanged( const QModelIndex &index, const QString help = loadFunctionHelp( item ); txtHelpText->setText( help ); - btnRemoveExpression->setEnabled( item->parent() && - item->parent()->text() == mUserExpressionsGroupName ); + bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName; + + btnRemoveExpression->setEnabled( isUserExpression ); + btnEditExpression->setEnabled( isUserExpression ); } @@ -1349,6 +1353,28 @@ void QgsExpressionBuilderWidget::storeCurrentUserExpression() } } +void QgsExpressionBuilderWidget::editSelectedUserExpression() +{ + // Get the item + QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); + QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + if ( !item ) + return; + + // Don't handle remove if we are on a header node or the parent + // is not the user group + if ( item->getItemType() == QgsExpressionItem::Header || + ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) ) + return; + + QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), item->getHelpText(), QStringList() }; + + if ( dlg.exec() == QDialog::DialogCode::Accepted ) + { + saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() ); + } +} + void QgsExpressionBuilderWidget::removeSelectedUserExpression() { diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index 6789d41c5468..bc4879055cec 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -380,6 +380,13 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp */ void removeSelectedUserExpression( ); + /** + * Edits the selected expression from the stored user expressions, + * the selected expression must be a user stored expression. + * \since QGIS 3.14 + */ + void editSelectedUserExpression(); + /** * Returns the list of expression items matching a \a label. * \since QGIS 3.12 diff --git a/src/ui/qgsexpressionbuilder.ui b/src/ui/qgsexpressionbuilder.ui index ddd0c09b744d..99b3fae26500 100644 --- a/src/ui/qgsexpressionbuilder.ui +++ b/src/ui/qgsexpressionbuilder.ui @@ -356,6 +356,19 @@ + + + + false + + + Edit selected expression from user expressions + + + Edit + + + From 0a68ef84e6a4ab719018a4894a73ba4abd1bd5e9 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 11 Mar 2020 01:37:28 +0200 Subject: [PATCH 2/8] Add ability to edit, import and export user expressions Fixes #34690 --- src/gui/qgsexpressionbuilderwidget.cpp | 296 ++++++++++++++++++++++++- src/gui/qgsexpressionbuilderwidget.h | 42 ++++ src/gui/qgsexpressionstoredialog.h | 2 +- src/ui/qgsexpressionbuilder.ui | 28 +++ 4 files changed, 366 insertions(+), 2 deletions(-) diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index 5c546dba340f..47376aed1051 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -43,6 +43,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) : QWidget( parent ) @@ -63,6 +69,8 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) connect( btnSaveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::storeCurrentUserExpression ); connect( btnEditExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::editSelectedUserExpression ); connect( btnRemoveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::removeSelectedUserExpression ); + connect( mActionImportUserExpressions, &QAction::triggered, this, &QgsExpressionBuilderWidget::importUserExpressions_pressed ); + connect( mActionExportUserExpressions, &QAction::triggered, this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed ); connect( btnClearEditor, &QPushButton::pressed, txtExpressionString, &QgsCodeEditorExpression::clear ); txtHelpText->setOpenExternalLinks( true ); @@ -86,6 +94,7 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) btnSaveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSave.svg" ) ) ); btnEditExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionToggleEditing.svg" ) ) ); btnRemoveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionDeleteSelected.svg" ) ) ); + btnImportExportExpressions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSaveAs.svg" ) ) ); btnClearEditor->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileNew.svg" ) ) ); expressionTree->setContextMenuPolicy( Qt::CustomContextMenu ); @@ -643,6 +652,7 @@ void QgsExpressionBuilderWidget::loadRecent( const QString &collection ) } } +// this is potentially very slow if there are thousands of user expressions, everytime entire cleanup and load void QgsExpressionBuilderWidget::loadUserExpressions( ) { // Cleanup @@ -1367,7 +1377,7 @@ void QgsExpressionBuilderWidget::editSelectedUserExpression() ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) ) return; - QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), item->getHelpText(), QStringList() }; + QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), item->getHelpText() }; if ( dlg.exec() == QDialog::DialogCode::Accepted ) { @@ -1399,6 +1409,290 @@ void QgsExpressionBuilderWidget::removeSelectedUserExpression() } +void QgsExpressionBuilderWidget::exportUserExpressions_pressed() +{ + QgsSettings settings; + QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString(); + QString saveFileName = QFileDialog::getSaveFileName( + this, + tr( "Save user expressions" ), + lastSaveDir, + tr( "User expressions" ) + " (*.json)" ); + + if ( saveFileName.isEmpty() ) + return; + + QFileInfo saveFileInfo( saveFileName ); + + if ( saveFileInfo.suffix().isEmpty() ) + { + QString saveFileNameWithSuffix = saveFileName.append( ".json" ); + saveFileInfo = QFileInfo( saveFileNameWithSuffix ); + } + + settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App ); + + QJsonDocument *exportJson = exportUserExpressions(); + QFile jsonFile( saveFileName ); + + if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) ) + QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) ); + + if ( ! jsonFile.write( exportJson->toJson() ) ) + QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) ); + else + jsonFile.close(); + + delete exportJson; +} + + +QJsonDocument *QgsExpressionBuilderWidget::exportUserExpressions() +{ + const QString group = QStringLiteral( "user" ); + QgsSettings settings; + QJsonArray exportList; + QJsonObject exportObject + { + {"qgis_version", Qgis::version()}, + {"exported_at", QDateTime::currentDateTime().toString( Qt::ISODate )}, + {"author", QgsApplication::userFullName()}, + {"expressions", exportList} + }; + + settings.beginGroup( group, QgsSettings::Section::Expressions ); + + mUserExpressionLabels = settings.childGroups(); + + for ( const QString &label : qgis::as_const( mUserExpressionLabels ) ) + { + settings.beginGroup( label ); + + const QString expression = settings.value( QStringLiteral( "expression" ) ).toString(); + const QString helpText = settings.value( QStringLiteral( "helpText" ) ).toString(); + const QJsonObject expressionObject + { + {"name", label}, + {"type", "expression"}, + {"expression", expression}, + {"group", group}, + {"description", helpText} + }; + exportList.push_back( expressionObject ); + + settings.endGroup(); + } + + exportObject["expressions"] = exportList; + QJsonDocument *exportJson = new QJsonDocument( exportObject ); + + return exportJson; +} + +void QgsExpressionBuilderWidget::importUserExpressions_pressed() +{ + QgsSettings settings; + QString lastSaveDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString(); + QString loadFileName = QFileDialog::getOpenFileName( + this, + tr( "Save user expressions" ), + lastSaveDir, + tr( "User expressions" ) + " (*.json)" ); + + if ( loadFileName.isEmpty() ) + return; + + QFileInfo saveFileInfo( loadFileName ); + + settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App ); + + QFile jsonFile( loadFileName ); + + if ( !jsonFile.open( QFile::ReadOnly ) ) + QMessageBox::warning( this, tr( "Import user expressions" ), tr( "Error while reading the expressions file." ) ); + + QTextStream jsonStream( &jsonFile ); + QString jsonString = jsonFile.readAll(); + jsonFile.close(); + + QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() ); + + if ( importJson.isNull() ) + { + QMessageBox::warning( this, tr( "Import user expressions" ), tr( "Error while reading the expressions file." ) ); + return; + } + + loadExpressionsFromJson( importJson ); +} + +void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &expressionsDocument ) +{ + // if the root of the json document is not an object, it means it's a wrong file + if ( ! expressionsDocument.isObject() ) + return; + + QJsonObject expressionsObject = expressionsDocument.object(); + + // validate json for manadatory fields + if ( ! expressionsObject["qgis_version"].isString() + || ! expressionsObject["exported_at"].isString() + || ! expressionsObject["author"].isString() + || ! expressionsObject["expressions"].isArray() ) + return; + + // validate versions + QVersionNumber qgisJsonVersion = QVersionNumber::fromString( expressionsObject["qgis_version"].toString() ); + QVersionNumber qgisVersion = QVersionNumber::fromString( Qgis::version() ); + + // if the expressions are from newer version of QGIS, we ask the user to confirm + // they want to proceed + if ( qgisJsonVersion > qgisVersion ) + { + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No; + switch ( QMessageBox::question( this, + tr( "QGIS Version Mismatch" ), + tr( "The imported expressions are from newer version of QGIS (%1) " + "and some of the expression might not work the current version (%2). " + "Do you want to continue?" ).arg( qgisJsonVersion.toString(), qgisVersion.toString() ), buttons ) ) + { + case QMessageBox::No: + return; + + case QMessageBox::Yes: + break; + + default: + break; + } + } + + // we store the number of + QStringList skippedExpressions; + bool isApplyToAll = false; + bool isOkToOverwrite = false; + + QgsSettings settings; + settings.beginGroup( QStringLiteral( "user" ), QgsSettings::Section::Expressions ); + mUserExpressionLabels = settings.childGroups(); + + for ( const QJsonValue &expressionValue : expressionsObject["expressions"].toArray() ) + { + QgsLogger::warning( "" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); + // validate the type of the array element, can be anything + if ( ! expressionValue.isObject() ) + { + // try to stringify and put and indicator what happened + skippedExpressions.append( expressionValue.toString() ); + continue; + } + + QJsonObject expressionObj = expressionValue.toObject(); + + // make sure the required keys are the correct types + if ( ! expressionObj["name"].isString() + || ! expressionObj["type"].isString() + || ! expressionObj["expression"].isString() + || ! expressionObj["group"].isString() + || ! expressionObj["description"].isString() ) + { + // try to stringify and put an indicator what happened. Try to stringify the name, if fails, go with the expression. + if ( ! expressionObj["name"].toString().isEmpty() ) + skippedExpressions.append( expressionObj["name"].toString() ); + else + skippedExpressions.append( expressionObj["expression"].toString() ); + + continue; + } + + // we want to import only items of type expression for now + if ( expressionObj["type"].toString() != "expression" ) + { + skippedExpressions.append( expressionObj["name"].toString() ); + continue; + } + + // we want to import only items of type expression for now + if ( expressionObj["group"].toString() != "user" ) + { + skippedExpressions.append( expressionObj["name"].toString() ); + continue; + } + + QString label = expressionObj["name"].toString(); + QString expression = expressionObj["expression"].toString(); + QString helpText = expressionObj["description"].toString(); + + // make sure they have valid name + if ( label.contains( "\\" ) || label.contains( "/" ) ) + { + QgsLogger::warning( "" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); + skippedExpressions.append( expressionObj["name"].toString() ); + continue; + } + + if ( mUserExpressionLabels.contains( label ) ) + { + if ( ! isApplyToAll ) + showMessageBoxConfirmExpressionOverwrite( isApplyToAll, isOkToOverwrite, label, expression, expression ); + + if ( isOkToOverwrite ) + saveToUserExpressions( label, expression, helpText ); + else + { + skippedExpressions.append( label ); + continue; + } + } + else + { + saveToUserExpressions( label, expression, helpText ); + } + } + + QgsLogger::warning( "" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); + loadUserExpressions( ); +} + +void QgsExpressionBuilderWidget::showMessageBoxConfirmExpressionOverwrite( + bool &isApplyToAll, + bool &isOkToOverwrite, + const QString &label, + QString &oldExpression, + QString &newExpression ) +{ + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll; + switch ( QMessageBox::question( this, + tr( "Expression override" ), + tr( "The expression with label '%1' was already defined." + "The old expression \"%2\" will be overriden by \"%3\"." + "Are you sure you want to overwrite the expression?" ).arg( label, oldExpression, newExpression ), buttons ) ) + { + case QMessageBox::NoToAll: + isApplyToAll = true; + isOkToOverwrite = false; + break; + + case QMessageBox::No: + isApplyToAll = false; + isOkToOverwrite = false; + break; + + case QMessageBox::YesToAll: + isApplyToAll = true; + isOkToOverwrite = true; + break; + + case QMessageBox::Yes: + isApplyToAll = false; + isOkToOverwrite = true; + break; + + default: + break; + } +} + const QList QgsExpressionBuilderWidget::findExpressions( const QString &label ) { QList result; diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index bc4879055cec..2e318df4619f 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -387,6 +387,48 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp */ void editSelectedUserExpression(); + /** + * Display a file dialog to choose where to store the exported expressions JSON file + * and saves them to the selected destination. + * \since QGIS 3.14 + */ + void exportUserExpressions_pressed(); + + /** + * Create the expressions JSON document storing all the user expressions to be exported. + * \since QGIS 3.14 + * \returns the created expressions JSON file + */ + QJsonDocument *exportUserExpressions(); + + /** + * Display a file dialog to choose where to load the expression JSON file from + * and adds them to user expressions group. + * \since QGIS 3.14 + */ + void importUserExpressions_pressed(); + + /** + * Load and permanently store the expressions from the expressions JSON document. + * \since QGIS 3.14 + * \param expressionsDocument the parsed expressions JSON file + */ + void loadExpressionsFromJson( const QJsonDocument &expressionsDocument ); + + /** + * Display a message box to ask the user what to do when an expression + * with the same \a label already exists. Answering "Yes" will replace + * the old expression with the one from the file, while "No" will keep + * the old expression. + * \since QGIS 3.14 + * \param isApplyToAll whether the decision of the user should be applied to any future label collision + * \param isOkToOverwrite whether to overwrite the old expression with the new one in case of label collision + * \param label the label of the expression + * \param oldExpression the old expression for a given label + * \param newExpression the new expression for a given label + */ + void showMessageBoxConfirmExpressionOverwrite( bool &isApplyToAll, bool &isOkToOverwrite, const QString &label, QString &oldExpression, QString &newExpression ); + /** * Returns the list of expression items matching a \a label. * \since QGIS 3.12 diff --git a/src/gui/qgsexpressionstoredialog.h b/src/gui/qgsexpressionstoredialog.h index 3d210640fe57..811087c8f257 100644 --- a/src/gui/qgsexpressionstoredialog.h +++ b/src/gui/qgsexpressionstoredialog.h @@ -39,7 +39,7 @@ class GUI_EXPORT QgsExpressionStoreDialog : public QDialog, private Ui::QgsExpre QgsExpressionStoreDialog( const QString &label, const QString &expression, const QString &helpText, - const QStringList &existingLabels, + const QStringList &existingLabels = QStringList(), QWidget *parent = nullptr ); /** diff --git a/src/ui/qgsexpressionbuilder.ui b/src/ui/qgsexpressionbuilder.ui index 99b3fae26500..a2182d2849c7 100644 --- a/src/ui/qgsexpressionbuilder.ui +++ b/src/ui/qgsexpressionbuilder.ui @@ -330,6 +330,21 @@ 0 + + + + Import/Export User Expressions + + + Import/Export + + + QToolButton::InstantPopup + + + + + @@ -946,6 +961,19 @@ Saved scripts are auto loaded on QGIS startup. + + + Import user expressions + + + + + Export user expressions + + + Export user expressions + + From 368c297e583fd65442c6c4df9d66ed75e979415a Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 11 Mar 2020 20:12:17 +0200 Subject: [PATCH 3/8] Cleanup code, fix indentation, add "skipped expressions" dialog --- src/gui/qgsexpressionbuilderwidget.cpp | 58 ++++++++++++++++---------- src/gui/qgsexpressionbuilderwidget.h | 6 +-- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index 47376aed1051..d65f11102a08 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -652,7 +652,7 @@ void QgsExpressionBuilderWidget::loadRecent( const QString &collection ) } } -// this is potentially very slow if there are thousands of user expressions, everytime entire cleanup and load +// this is potentially very slow if there are thousands of user expressions, every time entire cleanup and load void QgsExpressionBuilderWidget::loadUserExpressions( ) { // Cleanup @@ -1568,7 +1568,7 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e } // we store the number of - QStringList skippedExpressions; + QStringList skippedExpressionLabels; bool isApplyToAll = false; bool isOkToOverwrite = false; @@ -1578,12 +1578,11 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e for ( const QJsonValue &expressionValue : expressionsObject["expressions"].toArray() ) { - QgsLogger::warning( "" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); // validate the type of the array element, can be anything if ( ! expressionValue.isObject() ) { // try to stringify and put and indicator what happened - skippedExpressions.append( expressionValue.toString() ); + skippedExpressionLabels.append( expressionValue.toString() ); continue; } @@ -1598,9 +1597,9 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e { // try to stringify and put an indicator what happened. Try to stringify the name, if fails, go with the expression. if ( ! expressionObj["name"].toString().isEmpty() ) - skippedExpressions.append( expressionObj["name"].toString() ); + skippedExpressionLabels.append( expressionObj["name"].toString() ); else - skippedExpressions.append( expressionObj["expression"].toString() ); + skippedExpressionLabels.append( expressionObj["expression"].toString() ); continue; } @@ -1608,39 +1607,43 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e // we want to import only items of type expression for now if ( expressionObj["type"].toString() != "expression" ) { - skippedExpressions.append( expressionObj["name"].toString() ); + skippedExpressionLabels.append( expressionObj["name"].toString() ); continue; } // we want to import only items of type expression for now if ( expressionObj["group"].toString() != "user" ) { - skippedExpressions.append( expressionObj["name"].toString() ); + skippedExpressionLabels.append( expressionObj["name"].toString() ); continue; } - QString label = expressionObj["name"].toString(); - QString expression = expressionObj["expression"].toString(); - QString helpText = expressionObj["description"].toString(); + const QString label = expressionObj["name"].toString(); + const QString expression = expressionObj["expression"].toString(); + const QString helpText = expressionObj["description"].toString(); // make sure they have valid name if ( label.contains( "\\" ) || label.contains( "/" ) ) { - QgsLogger::warning( "" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); - skippedExpressions.append( expressionObj["name"].toString() ); + skippedExpressionLabels.append( expressionObj["name"].toString() ); continue; } - if ( mUserExpressionLabels.contains( label ) ) + settings.beginGroup( label ); + const QString oldExpression = settings.value( QStringLiteral( "expression" ) ).toString(); + settings.endGroup(); + + // TODO would be nice to skip the cases when labels and expressions match + if ( mUserExpressionLabels.contains( label ) && expression != oldExpression ) { if ( ! isApplyToAll ) - showMessageBoxConfirmExpressionOverwrite( isApplyToAll, isOkToOverwrite, label, expression, expression ); + showMessageBoxConfirmExpressionOverwrite( isApplyToAll, isOkToOverwrite, label, oldExpression, expression ); if ( isOkToOverwrite ) saveToUserExpressions( label, expression, helpText ); else { - skippedExpressions.append( label ); + skippedExpressionLabels.append( label ); continue; } } @@ -1650,22 +1653,35 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e } } - QgsLogger::warning( "" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); loadUserExpressions( ); + + QgsLogger::warning( QString::number( skippedExpressionLabels.count() ) + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); + + if ( ! skippedExpressionLabels.isEmpty() ) + { + QStringList skippedExpressionLabelsQuoted; + for ( const QString &skippedExpressionLabel : skippedExpressionLabels ) + skippedExpressionLabelsQuoted.append( QString( "'%1'" ).arg( skippedExpressionLabel ) ); + + QMessageBox::information( this, + tr( "Skipped expressions" ), + QString( "%1\n%2" ).arg( tr( "The following expressions have been skipped:" ), + skippedExpressionLabelsQuoted.join( ", " ) ) ); + } } void QgsExpressionBuilderWidget::showMessageBoxConfirmExpressionOverwrite( bool &isApplyToAll, bool &isOkToOverwrite, const QString &label, - QString &oldExpression, - QString &newExpression ) + const QString &oldExpression, + const QString &newExpression ) { QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll; switch ( QMessageBox::question( this, - tr( "Expression override" ), + tr( "Expression overwrite" ), tr( "The expression with label '%1' was already defined." - "The old expression \"%2\" will be overriden by \"%3\"." + "The old expression \"%2\" will be overwritten by \"%3\"." "Are you sure you want to overwrite the expression?" ).arg( label, oldExpression, newExpression ), buttons ) ) { case QMessageBox::NoToAll: diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index 2e318df4619f..2b3a4f1a9e89 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -396,8 +396,8 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp /** * Create the expressions JSON document storing all the user expressions to be exported. - * \since QGIS 3.14 * \returns the created expressions JSON file + * \since QGIS 3.14 */ QJsonDocument *exportUserExpressions(); @@ -410,8 +410,8 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp /** * Load and permanently store the expressions from the expressions JSON document. - * \since QGIS 3.14 * \param expressionsDocument the parsed expressions JSON file + * \since QGIS 3.14 */ void loadExpressionsFromJson( const QJsonDocument &expressionsDocument ); @@ -427,7 +427,7 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp * \param oldExpression the old expression for a given label * \param newExpression the new expression for a given label */ - void showMessageBoxConfirmExpressionOverwrite( bool &isApplyToAll, bool &isOkToOverwrite, const QString &label, QString &oldExpression, QString &newExpression ); + void showMessageBoxConfirmExpressionOverwrite( bool &isApplyToAll, bool &isOkToOverwrite, const QString &label, const QString &oldExpression, const QString &newExpression ); /** * Returns the list of expression items matching a \a label. From dd9f39757326cf98b67fa1535c7ba7fd90709fa9 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Wed, 11 Mar 2020 23:19:54 +0200 Subject: [PATCH 4/8] Change titles on file dialogs too --- src/gui/qgsexpressionbuilderwidget.cpp | 28 +++++++++++--------------- src/gui/qgsexpressionbuilderwidget.h | 2 +- src/ui/qgsexpressionbuilder.ui | 7 ++----- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index d65f11102a08..7fe190909569 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -1415,7 +1415,7 @@ void QgsExpressionBuilderWidget::exportUserExpressions_pressed() QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString(); QString saveFileName = QFileDialog::getSaveFileName( this, - tr( "Save user expressions" ), + tr( "Export User Expressions" ), lastSaveDir, tr( "User expressions" ) + " (*.json)" ); @@ -1432,7 +1432,7 @@ void QgsExpressionBuilderWidget::exportUserExpressions_pressed() settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App ); - QJsonDocument *exportJson = exportUserExpressions(); + std::unique_ptr< QJsonDocument > exportJson = exportUserExpressions(); QFile jsonFile( saveFileName ); if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) ) @@ -1442,12 +1442,10 @@ void QgsExpressionBuilderWidget::exportUserExpressions_pressed() QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) ); else jsonFile.close(); - - delete exportJson; } -QJsonDocument *QgsExpressionBuilderWidget::exportUserExpressions() +std::unique_ptr< QJsonDocument > QgsExpressionBuilderWidget::exportUserExpressions() { const QString group = QStringLiteral( "user" ); QgsSettings settings; @@ -1484,7 +1482,7 @@ QJsonDocument *QgsExpressionBuilderWidget::exportUserExpressions() } exportObject["expressions"] = exportList; - QJsonDocument *exportJson = new QJsonDocument( exportObject ); + std::unique_ptr< QJsonDocument > exportJson = qgis::make_unique< QJsonDocument >( exportObject ); return exportJson; } @@ -1495,21 +1493,21 @@ void QgsExpressionBuilderWidget::importUserExpressions_pressed() QString lastSaveDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString(); QString loadFileName = QFileDialog::getOpenFileName( this, - tr( "Save user expressions" ), + tr( "Import User Expressions" ), lastSaveDir, tr( "User expressions" ) + " (*.json)" ); if ( loadFileName.isEmpty() ) return; - QFileInfo saveFileInfo( loadFileName ); + QFileInfo loadFileInfo( loadFileName ); - settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App ); + settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), loadFileInfo.absolutePath(), QgsSettings::App ); QFile jsonFile( loadFileName ); if ( !jsonFile.open( QFile::ReadOnly ) ) - QMessageBox::warning( this, tr( "Import user expressions" ), tr( "Error while reading the expressions file." ) ); + QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) ); QTextStream jsonStream( &jsonFile ); QString jsonString = jsonFile.readAll(); @@ -1519,7 +1517,7 @@ void QgsExpressionBuilderWidget::importUserExpressions_pressed() if ( importJson.isNull() ) { - QMessageBox::warning( this, tr( "Import user expressions" ), tr( "Error while reading the expressions file." ) ); + QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) ); return; } @@ -1554,7 +1552,7 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e tr( "QGIS Version Mismatch" ), tr( "The imported expressions are from newer version of QGIS (%1) " "and some of the expression might not work the current version (%2). " - "Do you want to continue?" ).arg( qgisJsonVersion.toString(), qgisVersion.toString() ), buttons ) ) + "Are you sure you want to continue?" ).arg( qgisJsonVersion.toString(), qgisVersion.toString() ), buttons ) ) { case QMessageBox::No: return; @@ -1655,8 +1653,6 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e loadUserExpressions( ); - QgsLogger::warning( QString::number( skippedExpressionLabels.count() ) + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) ); - if ( ! skippedExpressionLabels.isEmpty() ) { QStringList skippedExpressionLabelsQuoted; @@ -1664,7 +1660,7 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e skippedExpressionLabelsQuoted.append( QString( "'%1'" ).arg( skippedExpressionLabel ) ); QMessageBox::information( this, - tr( "Skipped expressions" ), + tr( "Skipped Expression Imports" ), QString( "%1\n%2" ).arg( tr( "The following expressions have been skipped:" ), skippedExpressionLabelsQuoted.join( ", " ) ) ); } @@ -1679,7 +1675,7 @@ void QgsExpressionBuilderWidget::showMessageBoxConfirmExpressionOverwrite( { QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll; switch ( QMessageBox::question( this, - tr( "Expression overwrite" ), + tr( "Expression Overwrite" ), tr( "The expression with label '%1' was already defined." "The old expression \"%2\" will be overwritten by \"%3\"." "Are you sure you want to overwrite the expression?" ).arg( label, oldExpression, newExpression ), buttons ) ) diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index 2b3a4f1a9e89..c57c32a2a05b 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -399,7 +399,7 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp * \returns the created expressions JSON file * \since QGIS 3.14 */ - QJsonDocument *exportUserExpressions(); + std::unique_ptr< QJsonDocument > exportUserExpressions(); /** * Display a file dialog to choose where to load the expression JSON file from diff --git a/src/ui/qgsexpressionbuilder.ui b/src/ui/qgsexpressionbuilder.ui index a2182d2849c7..81ae11bad395 100644 --- a/src/ui/qgsexpressionbuilder.ui +++ b/src/ui/qgsexpressionbuilder.ui @@ -963,15 +963,12 @@ Saved scripts are auto loaded on QGIS startup. - Import user expressions + Import User Expressions - Export user expressions - - - Export user expressions + Export User Expressions From bc23d86253ac34f95b9042e1551c086ab7a28239 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 12 Mar 2020 12:15:32 +0200 Subject: [PATCH 5/8] Change indentation; Sipified; Fix class modifiers --- .../qgsexpressionbuilderwidget.sip.in | 18 ++++++ .../qgsexpressionstoredialog.sip.in | 2 +- src/gui/qgsexpressionbuilderwidget.cpp | 8 +-- src/gui/qgsexpressionbuilderwidget.h | 56 +++++++++---------- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in b/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in index 490668d6c9cb..b56b9bc6dcb4 100644 --- a/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in +++ b/python/gui/auto_generated/qgsexpressionbuilderwidget.sip.in @@ -386,6 +386,24 @@ the selected expression must be a user stored expression. Edits the selected expression from the stored user expressions, the selected expression must be a user stored expression. +.. versionadded:: 3.14 +%End + + QJsonDocument exportUserExpressions(); +%Docstring +Create the expressions JSON document storing all the user expressions to be exported. + +:return: the created expressions JSON file + +.. versionadded:: 3.14 +%End + + void loadExpressionsFromJson( const QJsonDocument &expressionsDocument ); +%Docstring +Load and permanently store the expressions from the expressions JSON document. + +:param expressionsDocument: the parsed expressions JSON file + .. versionadded:: 3.14 %End diff --git a/python/gui/auto_generated/qgsexpressionstoredialog.sip.in b/python/gui/auto_generated/qgsexpressionstoredialog.sip.in index 0a2112dc8b62..1ca660d6a2f8 100644 --- a/python/gui/auto_generated/qgsexpressionstoredialog.sip.in +++ b/python/gui/auto_generated/qgsexpressionstoredialog.sip.in @@ -26,7 +26,7 @@ A generic dialog for editing expression text, label and help text. QgsExpressionStoreDialog( const QString &label, const QString &expression, const QString &helpText, - const QStringList &existingLabels, + const QStringList &existingLabels = QStringList(), QWidget *parent = 0 ); %Docstring Creates a QgsExpressionStoreDialog with given ``label``, ``expression`` and ``helpText``. diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index 7fe190909569..270551808f29 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -1432,20 +1432,20 @@ void QgsExpressionBuilderWidget::exportUserExpressions_pressed() settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App ); - std::unique_ptr< QJsonDocument > exportJson = exportUserExpressions(); + QJsonDocument exportJson = exportUserExpressions(); QFile jsonFile( saveFileName ); if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) ) QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) ); - if ( ! jsonFile.write( exportJson->toJson() ) ) + if ( ! jsonFile.write( exportJson.toJson() ) ) QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) ); else jsonFile.close(); } -std::unique_ptr< QJsonDocument > QgsExpressionBuilderWidget::exportUserExpressions() +QJsonDocument QgsExpressionBuilderWidget::exportUserExpressions() { const QString group = QStringLiteral( "user" ); QgsSettings settings; @@ -1482,7 +1482,7 @@ std::unique_ptr< QJsonDocument > QgsExpressionBuilderWidget::exportUserExpressio } exportObject["expressions"] = exportList; - std::unique_ptr< QJsonDocument > exportJson = qgis::make_unique< QJsonDocument >( exportObject ); + QJsonDocument exportJson = QJsonDocument( exportObject ); return exportJson; } diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index c57c32a2a05b..f23ce35e139b 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -387,26 +387,12 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp */ void editSelectedUserExpression(); - /** - * Display a file dialog to choose where to store the exported expressions JSON file - * and saves them to the selected destination. - * \since QGIS 3.14 - */ - void exportUserExpressions_pressed(); - /** * Create the expressions JSON document storing all the user expressions to be exported. * \returns the created expressions JSON file * \since QGIS 3.14 */ - std::unique_ptr< QJsonDocument > exportUserExpressions(); - - /** - * Display a file dialog to choose where to load the expression JSON file from - * and adds them to user expressions group. - * \since QGIS 3.14 - */ - void importUserExpressions_pressed(); + QJsonDocument exportUserExpressions(); /** * Load and permanently store the expressions from the expressions JSON document. @@ -415,20 +401,6 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp */ void loadExpressionsFromJson( const QJsonDocument &expressionsDocument ); - /** - * Display a message box to ask the user what to do when an expression - * with the same \a label already exists. Answering "Yes" will replace - * the old expression with the one from the file, while "No" will keep - * the old expression. - * \since QGIS 3.14 - * \param isApplyToAll whether the decision of the user should be applied to any future label collision - * \param isOkToOverwrite whether to overwrite the old expression with the new one in case of label collision - * \param label the label of the expression - * \param oldExpression the old expression for a given label - * \param newExpression the new expression for a given label - */ - void showMessageBoxConfirmExpressionOverwrite( bool &isApplyToAll, bool &isOkToOverwrite, const QString &label, const QString &oldExpression, const QString &newExpression ); - /** * Returns the list of expression items matching a \a label. * \since QGIS 3.12 @@ -444,6 +416,18 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp void operatorButtonClicked(); void btnRun_pressed(); void btnNewFile_pressed(); + /** + * Display a file dialog to choose where to store the exported expressions JSON file + * and saves them to the selected destination. + * \since QGIS 3.14 + */ + void exportUserExpressions_pressed(); + /** + * Display a file dialog to choose where to load the expression JSON file from + * and adds them to user expressions group. + * \since QGIS 3.14 + */ + void importUserExpressions_pressed(); void cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem ); void expressionTree_doubleClicked( const QModelIndex &index ); void txtExpressionString_textChanged(); @@ -565,6 +549,20 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp void loadFieldsAndValues( const QMap &fieldValues ); + /** + * Display a message box to ask the user what to do when an expression + * with the same \a label already exists. Answering "Yes" will replace + * the old expression with the one from the file, while "No" will keep + * the old expression. + * \param isApplyToAll whether the decision of the user should be applied to any future label collision + * \param isOkToOverwrite whether to overwrite the old expression with the new one in case of label collision + * \param label the label of the expression + * \param oldExpression the old expression for a given label + * \param newExpression the new expression for a given label + * \since QGIS 3.14 + */ + void showMessageBoxConfirmExpressionOverwrite( bool &isApplyToAll, bool &isOkToOverwrite, const QString &label, const QString &oldExpression, const QString &newExpression ); + bool mAutoSave = true; QString mFunctionsPath; QgsVectorLayer *mLayer = nullptr; From 585fa90ed3be4ba9c00b7592e831d60c7ed6f8fe Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 12 Mar 2020 13:11:17 +0200 Subject: [PATCH 6/8] Fixes based on the feedback: - dynamic_cast -> static_cast - QString -> QStringLiteral --- src/gui/qgsexpressionbuilderwidget.cpp | 34 +++++++++++++------------- src/gui/qgsexpressionbuilderwidget.h | 2 ++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index 270551808f29..c7a3a10b0155 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -258,7 +258,7 @@ void QgsExpressionBuilderWidget::currentChanged( const QModelIndex &index, const // Get the item QModelIndex idx = mProxyModel->mapToSource( index ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); if ( !item ) return; @@ -355,8 +355,8 @@ void QgsExpressionBuilderWidget::updateFunctionFileList( const QString &path ) { // Create default sample entry. newFunctionFile( "default" ); - txtPython->setText( QString( "'''\n#Sample custom function file\n " - "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) ); + txtPython->setText( QStringLiteral( "'''\n#Sample custom function file\n " + "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) ); saveFunctionFile( "default" ); } } @@ -416,7 +416,7 @@ void QgsExpressionBuilderWidget::loadFunctionCode( const QString &code ) void QgsExpressionBuilderWidget::expressionTree_doubleClicked( const QModelIndex &index ) { QModelIndex idx = mProxyModel->mapToSource( index ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); if ( !item ) return; @@ -1250,7 +1250,7 @@ void QgsExpressionBuilderWidget::showContextMenu( QPoint pt ) { QModelIndex idx = expressionTree->indexAt( pt ); idx = mProxyModel->mapToSource( idx ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); if ( !item ) return; @@ -1272,7 +1272,7 @@ void QgsExpressionBuilderWidget::showContextMenu( QPoint pt ) void QgsExpressionBuilderWidget::loadSampleValues() { QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); // TODO We should really return a error the user of the widget that // the there is no layer set. if ( !mLayer || !item ) @@ -1285,7 +1285,7 @@ void QgsExpressionBuilderWidget::loadSampleValues() void QgsExpressionBuilderWidget::loadAllValues() { QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); // TODO We should really return a error the user of the widget that // the there is no layer set. if ( !mLayer || !item ) @@ -1298,7 +1298,7 @@ void QgsExpressionBuilderWidget::loadAllValues() void QgsExpressionBuilderWidget::loadSampleUsedValues() { QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); // TODO We should really return a error the user of the widget that // the there is no layer set. if ( !mLayer || !item ) @@ -1311,7 +1311,7 @@ void QgsExpressionBuilderWidget::loadSampleUsedValues() void QgsExpressionBuilderWidget::loadAllUsedValues() { QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); // TODO We should really return a error the user of the widget that // the there is no layer set. if ( !mLayer || !item ) @@ -1367,7 +1367,7 @@ void QgsExpressionBuilderWidget::editSelectedUserExpression() { // Get the item QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); if ( !item ) return; @@ -1390,7 +1390,7 @@ void QgsExpressionBuilderWidget::removeSelectedUserExpression() // Get the item QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() ); - QgsExpressionItem *item = dynamic_cast( mModel->itemFromIndex( idx ) ); + QgsExpressionItem *item = static_cast( mModel->itemFromIndex( idx ) ); if ( !item ) return; @@ -1603,14 +1603,14 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e } // we want to import only items of type expression for now - if ( expressionObj["type"].toString() != "expression" ) + if ( expressionObj["type"].toString() != QStringLiteral( "expression" ) ) { skippedExpressionLabels.append( expressionObj["name"].toString() ); continue; } // we want to import only items of type expression for now - if ( expressionObj["group"].toString() != "user" ) + if ( expressionObj["group"].toString() != QStringLiteral( "user" ) ) { skippedExpressionLabels.append( expressionObj["name"].toString() ); continue; @@ -1621,7 +1621,7 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e const QString helpText = expressionObj["description"].toString(); // make sure they have valid name - if ( label.contains( "\\" ) || label.contains( "/" ) ) + if ( label.contains( "\\" ) || label.contains( '/' ) ) { skippedExpressionLabels.append( expressionObj["name"].toString() ); continue; @@ -1657,11 +1657,11 @@ void QgsExpressionBuilderWidget::loadExpressionsFromJson( const QJsonDocument &e { QStringList skippedExpressionLabelsQuoted; for ( const QString &skippedExpressionLabel : skippedExpressionLabels ) - skippedExpressionLabelsQuoted.append( QString( "'%1'" ).arg( skippedExpressionLabel ) ); + skippedExpressionLabelsQuoted.append( QStringLiteral( "'%1'" ).arg( skippedExpressionLabel ) ); QMessageBox::information( this, tr( "Skipped Expression Imports" ), - QString( "%1\n%2" ).arg( tr( "The following expressions have been skipped:" ), + QStringLiteral( "%1\n%2" ).arg( tr( "The following expressions have been skipped:" ), skippedExpressionLabelsQuoted.join( ", " ) ) ); } } @@ -1711,7 +1711,7 @@ const QList QgsExpressionBuilderWidget::findExpressions( co const QList found { mModel->findItems( label, Qt::MatchFlag::MatchRecursive ) }; for ( const auto &item : qgis::as_const( found ) ) { - result.push_back( dynamic_cast( item ) ); + result.push_back( static_cast( item ) ); } return result; } diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index f23ce35e139b..61937c095f38 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -416,12 +416,14 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp void operatorButtonClicked(); void btnRun_pressed(); void btnNewFile_pressed(); + /** * Display a file dialog to choose where to store the exported expressions JSON file * and saves them to the selected destination. * \since QGIS 3.14 */ void exportUserExpressions_pressed(); + /** * Display a file dialog to choose where to load the expression JSON file from * and adds them to user expressions group. From f246a480dcfcff13399475ac92fecbb5d08797a1 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Mon, 16 Mar 2020 03:23:12 +0200 Subject: [PATCH 7/8] Split button toolbars into two rows --- src/gui/qgsexpressionbuilderwidget.cpp | 15 +- src/ui/qgsexpressionbuilder.ui | 444 +++++++++++++------------ 2 files changed, 232 insertions(+), 227 deletions(-) diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index c7a3a10b0155..f798afc2cb7f 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -69,8 +69,8 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) connect( btnSaveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::storeCurrentUserExpression ); connect( btnEditExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::editSelectedUserExpression ); connect( btnRemoveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::removeSelectedUserExpression ); - connect( mActionImportUserExpressions, &QAction::triggered, this, &QgsExpressionBuilderWidget::importUserExpressions_pressed ); - connect( mActionExportUserExpressions, &QAction::triggered, this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed ); + connect( btnImportExpressions, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::importUserExpressions_pressed ); + connect( btnExportExpressions, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed ); connect( btnClearEditor, &QPushButton::pressed, txtExpressionString, &QgsCodeEditorExpression::clear ); txtHelpText->setOpenExternalLinks( true ); @@ -94,7 +94,8 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) btnSaveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSave.svg" ) ) ); btnEditExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionToggleEditing.svg" ) ) ); btnRemoveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionDeleteSelected.svg" ) ) ); - btnImportExportExpressions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSaveAs.svg" ) ) ); + btnExportExpressions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSharingExport.svg" ) ) ); + btnImportExpressions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSharingImport.svg" ) ) ); btnClearEditor->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileNew.svg" ) ) ); expressionTree->setContextMenuPolicy( Qt::CustomContextMenu ); @@ -1377,7 +1378,9 @@ void QgsExpressionBuilderWidget::editSelectedUserExpression() ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) ) return; - QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), item->getHelpText() }; + QgsSettings settings; + QString helpText = settings.value( QStringLiteral( "user/%1/helpText" ).arg( item->text() ), "", QgsSettings::Section::Expressions ).toString(); + QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), helpText }; if ( dlg.exec() == QDialog::DialogCode::Accepted ) { @@ -1490,11 +1493,11 @@ QJsonDocument QgsExpressionBuilderWidget::exportUserExpressions() void QgsExpressionBuilderWidget::importUserExpressions_pressed() { QgsSettings settings; - QString lastSaveDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString(); + QString lastImportDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString(); QString loadFileName = QFileDialog::getOpenFileName( this, tr( "Import User Expressions" ), - lastSaveDir, + lastImportDir, tr( "User expressions" ) + " (*.json)" ); if ( loadFileName.isEmpty() ) diff --git a/src/ui/qgsexpressionbuilder.ui b/src/ui/qgsexpressionbuilder.ui index 81ae11bad395..b6b19fa151ba 100644 --- a/src/ui/qgsexpressionbuilder.ui +++ b/src/ui/qgsexpressionbuilder.ui @@ -99,197 +99,9 @@ 0 - + - - - - - - 0 - 0 - - - - - 27 - 0 - - - - - 300 - 16777215 - - - - - 20 - 0 - - - - - 7 - 0 - - - - Qt::LeftToRight - - - false - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Equal operator - - - = - - - - - - - - 0 - 0 - - - - Addition operator - - - + - - - - - - - Subtraction operator - - - - - - - - - - - Division operator - - - / - - - - - - - Multiplication operator - - - * - - - - - - - Power operator - - - ^ - - - - - - - String Concatenation - - - || - - - - - - - - 0 - 0 - - - - - 0 - 10 - - - - Open Bracket - - - ( - - - - - - - Close Bracket - - - ) - - - - - - - New Line - - - '\n' - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + @@ -330,21 +142,6 @@ 0 - - - - Import/Export User Expressions - - - Import/Export - - - QToolButton::InstantPopup - - - - - @@ -371,6 +168,13 @@ + + + + Qt::Vertical + + + @@ -397,6 +201,26 @@ + + + + Import User Expressions + + + Import + + + + + + + Export User Expressions + + + Export + + + @@ -405,6 +229,194 @@ + + + + + 0 + 0 + + + + + 27 + 0 + + + + + 300 + 16777215 + + + + + 20 + 0 + + + + + 7 + 0 + + + + Qt::LeftToRight + + + false + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Equal operator + + + = + + + + + + + + 0 + 0 + + + + Addition operator + + + + + + + + + + + Subtraction operator + + + - + + + + + + + Division operator + + + / + + + + + + + Multiplication operator + + + * + + + + + + + Power operator + + + ^ + + + + + + + String Concatenation + + + || + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + Open Bracket + + + ( + + + + + + + Close Bracket + + + ) + + + + + + + New Line + + + '\n' + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -961,18 +973,14 @@ Saved scripts are auto loaded on QGIS startup. - - - Import User Expressions - - - - - Export User Expressions - - + + QgsCollapsibleGroupBox + QGroupBox +
qgscollapsiblegroupbox.h
+ 1 +
QgsFilterLineEdit QLineEdit @@ -990,12 +998,6 @@ Saved scripts are auto loaded on QGIS startup.
qgscodeeditorpython.h
1
- - QgsCollapsibleGroupBox - QGroupBox -
qgscollapsiblegroupbox.h
- 1 -
From 1010280cf63fde92f2d7e66c5309b22c3dcb7802 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Tue, 17 Mar 2020 08:49:03 +0200 Subject: [PATCH 8/8] Move the top toolbar buttons to the left --- src/ui/qgsexpressionbuilder.ui | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/ui/qgsexpressionbuilder.ui b/src/ui/qgsexpressionbuilder.ui index b6b19fa151ba..bd8bd2bfe46d 100644 --- a/src/ui/qgsexpressionbuilder.ui +++ b/src/ui/qgsexpressionbuilder.ui @@ -101,20 +101,7 @@ - - - - - Qt::Horizontal - - - - 40 - 20 - - - - + @@ -168,13 +155,6 @@ - - - - Qt::Vertical - - - @@ -221,6 +201,13 @@ + + + + Qt::Horizontal + + +