From 59162bc17824c9c7231e69e4932cf8d92b4ce9a9 Mon Sep 17 00:00:00 2001 From: Nathan Woodrow Date: Mon, 12 Jan 2015 18:14:30 +1000 Subject: [PATCH] [FEATURE] Function editor for expression widget. Allows for adding on the fly functions to the expression engine. Functions are saved in qgis2\python\expressions. New qgis.user module in Python. The qgis.user.expressions package points to the qgis2\python\expressions package in the users home --- python/CMakeLists.txt | 1 + python/gui/qgsexpressionbuilderwidget.sip | 20 + python/user.py | 41 + src/gui/qgsexpressionbuilderwidget.cpp | 245 +++- src/gui/qgsexpressionbuilderwidget.h | 27 + .../qgsdatadefinedsymboldialog.cpp | 1 + src/python/qgspythonutilsimpl.cpp | 11 +- src/ui/qgsexpressionbuilder.ui | 1120 +++++++++-------- 8 files changed, 899 insertions(+), 567 deletions(-) create mode 100644 python/user.py diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 75b466bd869..c0d870ba017 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -257,6 +257,7 @@ ENDIF(WITH_CUSTOM_WIDGETS) SET(PY_FILES __init__.py utils.py + user.py ) ADD_CUSTOM_TARGET(pyutils ALL) diff --git a/python/gui/qgsexpressionbuilderwidget.sip b/python/gui/qgsexpressionbuilderwidget.sip index 75a7f098825..9c4691a35e2 100644 --- a/python/gui/qgsexpressionbuilderwidget.sip +++ b/python/gui/qgsexpressionbuilderwidget.sip @@ -111,6 +111,26 @@ class QgsExpressionBuilderWidget : QWidget void loadRecent( QString key ); + /** Create a new file in the function editor + */ + void newFunctionFile( QString fileName = "scratch"); + + /** Save the current function editor text to the given file. + */ + void saveFunctionFile( QString fileName ); + + /** Load code from the given file into the function editor + */ + void loadCodeFromFile( QString path ); + + /** Load code into the function editor + */ + void loadFunctionCode( QString code ); + + /** Update the list of function files found at the given path + */ + void updateFunctionFileList( QString path ); + public slots: void currentChanged( const QModelIndex &index, const QModelIndex & ); void on_expressionTree_doubleClicked( const QModelIndex &index ); diff --git a/python/user.py b/python/user.py new file mode 100644 index 00000000000..e02c3a1c908 --- /dev/null +++ b/python/user.py @@ -0,0 +1,41 @@ +import os +import sys +import glob + +from qgis.core import QgsApplication + +def load_user_expressions(path): + """ + Load all user expressions from the given paths + """ + #Loop all py files and import them + modules = glob.glob(path + "/*.py") + names = [os.path.basename(f)[:-3] for f in modules] + for name in names: + if name == "__init__": + continue + # As user expression functions should be registed with qgsfunction + # just importing the file is enough to get it to load the functions into QGIS + __import__("expressions.{0}".format(name), locals(), globals()) + + +userpythonhome = os.path.join(QgsApplication.qgisSettingsDirPath(), "python") +expressionspath = os.path.join(userpythonhome, "expressions") +startuppy = os.path.join(userpythonhome, "startup.py") + +# exec startup script +if os.path.exists(startuppy): + execfile(startuppy, locals(), globals()) + +if not os.path.exists(expressionspath): + os.makedirs(expressionspath) + +initfile = os.path.join(expressionspath, "__init__.py") +if not os.path.exists(initfile): + open(initfile, "w").close() + +import expressions + +expressions.load = load_user_expressions +expressions.load(expressionspath) + diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index d00bb382d08..0a99b588d3f 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) : QWidget( parent ) @@ -54,71 +56,27 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent ) connect( button, SIGNAL( pressed() ), this, SLOT( operatorButtonClicked() ) ); } - // TODO Can we move this stuff to QgsExpression, like the functions? - registerItem( "Operators", "+", " + ", tr( "Addition operator" ) ); - registerItem( "Operators", "-", " - ", tr( "Subtraction operator" ) ); - registerItem( "Operators", "*", " * ", tr( "Multiplication operator" ) ); - registerItem( "Operators", "/", " / ", tr( "Division operator" ) ); - registerItem( "Operators", "%", " % ", tr( "Modulo operator" ) ); - registerItem( "Operators", "^", " ^ ", tr( "Power operator" ) ); - registerItem( "Operators", "=", " = ", tr( "Equal operator" ) ); - registerItem( "Operators", ">", " > ", tr( "Greater as operator" ) ); - registerItem( "Operators", "<", " < ", tr( "Less than operator" ) ); - registerItem( "Operators", "<>", " <> ", tr( "Unequal operator" ) ); - registerItem( "Operators", "<=", " <= ", tr( "Less or equal operator" ) ); - registerItem( "Operators", ">=", " >= ", tr( "Greater or equal operator" ) ); - registerItem( "Operators", "||", " || ", - QString( "|| %1
%2
%3:%4" ) - .arg( tr( "(String Concatenation)" ) ) - .arg( tr( "Joins two values together into a string" ) ) - .arg( tr( "Usage" ) ) - .arg( tr( "'Dia' || Diameter" ) ) ); - registerItem( "Operators", "IN", " IN " ); - registerItem( "Operators", "LIKE", " LIKE " ); - registerItem( "Operators", "ILIKE", " ILIKE " ); - registerItem( "Operators", "IS", " IS " ); - registerItem( "Operators", "OR", " OR " ); - registerItem( "Operators", "AND", " AND " ); - registerItem( "Operators", "NOT", " NOT " ); - - QString casestring = "CASE WHEN condition THEN result END"; - QString caseelsestring = "CASE WHEN condition THEN result ELSE result END"; - registerItem( "Conditionals", "CASE", casestring ); - registerItem( "Conditionals", "CASE ELSE", caseelsestring ); - - // Load the functions from the QgsExpression class - int count = QgsExpression::functionCount(); - for ( int i = 0; i < count; i++ ) - { - QgsExpression::Function* func = QgsExpression::Functions()[i]; - QString name = func->name(); - if ( name.startsWith( "_" ) ) // do not display private functions - continue; - if ( func->params() != 0 ) - name += "("; - registerItem( func->group(), func->name(), " " + name + " ", func->helptext() ); - } - - QList specials = QgsExpression::specialColumns(); - for ( int i = 0; i < specials.size(); ++i ) - { - QString name = specials[i]->name(); - registerItem( specials[i]->group(), name, " " + name + " " ); - } - txtSearchEdit->setPlaceholderText( tr( "Search" ) ); QSettings settings; splitter->restoreState( settings.value( "/windows/QgsExpressionBuilderWidget/splitter" ).toByteArray() ); -// splitter_2->restoreState( settings.value( "/windows/QgsExpressionBuilderWidget/splitter2" ).toByteArray() ); txtExpressionString->setFoldingVisible( false ); -// customFunctionBotton->setVisible( QgsPythonRunner::isValid() ); - txtPython->setVisible( false ); - cgbCustomFunction->setCollapsed( true ); - txtPython->setText( "@qgsfunction(args=-1, group='Custom')\n" - "def func(values, feature, parent):\n" - " return str(values)" ); + + updateFunctionTree(); + + if ( QgsPythonRunner::isValid() ) + { + QgsPythonRunner::eval( "qgis.user.expressionspath", mFunctionsPath ); + newFunctionFile(); + // The scratch file gets written each time the widget opens. + saveFunctionFile("scratch"); + updateFunctionFileList( mFunctionsPath ); + } + else + { + tab_2->setEnabled( false ); + } } @@ -156,6 +114,113 @@ void QgsExpressionBuilderWidget::currentChanged( const QModelIndex &index, const txtHelpText->setToolTip( txtHelpText->toPlainText() ); } +void QgsExpressionBuilderWidget::on_btnRun_pressed() +{ + saveFunctionFile( cmbFileNames->currentText() ); + runPythonCode( txtPython->text() ); +} + +void QgsExpressionBuilderWidget::runPythonCode( QString code ) +{ + if ( QgsPythonRunner::isValid() ) + { + QString pythontext = code; + QgsPythonRunner::run( pythontext ); + } + updateFunctionTree(); +} + +void QgsExpressionBuilderWidget::saveFunctionFile( QString fileName ) +{ + QDir myDir( mFunctionsPath ); + if ( !myDir.exists() ) + { + myDir.mkpath( mFunctionsPath ); + } + + if ( !fileName.endsWith( ".py" ) ) + { + fileName.append( ".py" ); + } + + fileName = mFunctionsPath + QDir::separator() + fileName; + QFile myFile( fileName ); + if ( myFile.open( QIODevice::WriteOnly | QIODevice::Text ) ) + { + QTextStream myFileStream( &myFile ); + myFileStream << txtPython->text() << endl; + myFile.close(); + } +} + +void QgsExpressionBuilderWidget::updateFunctionFileList( QString path ) +{ + mFunctionsPath = path; + QDir dir( path ); + dir.setNameFilters( QStringList() << "*.py" ); + QStringList files = dir.entryList( QDir::Files ); + cmbFileNames->clear(); + foreach ( QString name, files ) + { + QFileInfo info( mFunctionsPath + QDir::separator() + name ); + if ( info.baseName() == "__init__" ) continue; + cmbFileNames->addItem( info.baseName() ); + } +} + +void QgsExpressionBuilderWidget::newFunctionFile( QString fileName ) +{ + txtPython->setText( "from qgis.core import *\n" + "from qgis.gui import *\n\n" + "@qgsfunction(args=-1, group='Custom')\n" + "def func(values, feature, parent):\n" + " return str(values)" ); + int index = cmbFileNames->findText( fileName ); + if ( index == -1 ) + cmbFileNames->setEditText( fileName ); + else + cmbFileNames->setCurrentIndex( index ); +} + +void QgsExpressionBuilderWidget::on_btnNewFile_pressed() +{ + newFunctionFile(); +} + +void QgsExpressionBuilderWidget::on_cmbFileNames_currentIndexChanged( int index ) +{ + if ( index == -1 ) + return; + + QString path = mFunctionsPath + QDir::separator() + cmbFileNames->currentText(); + loadCodeFromFile( path ); +} + +void QgsExpressionBuilderWidget::loadCodeFromFile( QString path ) +{ + if ( !path.endsWith( ".py" ) ) + path.append( ".py" ); + + txtPython->loadScript( path ); +} + +void QgsExpressionBuilderWidget::loadFunctionCode( QString code ) +{ + txtPython->setText( code ); +} + +void QgsExpressionBuilderWidget::on_btnSaveFile_pressed() +{ + QString name = cmbFileNames->currentText(); + saveFunctionFile( name ); + int index = cmbFileNames->findText( name ); + if ( index == -1 ) + { + cmbFileNames->addItem( name ); + cmbFileNames->setCurrentIndex( cmbFileNames->count() - 1 ); + } +} + void QgsExpressionBuilderWidget::on_expressionTree_doubleClicked( const QModelIndex &index ) { QModelIndex idx = mProxyModel->mapToSource( index ); @@ -292,6 +357,63 @@ void QgsExpressionBuilderWidget::loadRecent( QString key ) } } +void QgsExpressionBuilderWidget::updateFunctionTree() +{ + mModel->clear(); + mExpressionGroups.clear(); + // TODO Can we move this stuff to QgsExpression, like the functions? + registerItem( "Operators", "+", " + ", tr( "Addition operator" ) ); + registerItem( "Operators", "-", " - ", tr( "Subtraction operator" ) ); + registerItem( "Operators", "*", " * ", tr( "Multiplication operator" ) ); + registerItem( "Operators", "/", " / ", tr( "Division operator" ) ); + registerItem( "Operators", "%", " % ", tr( "Modulo operator" ) ); + registerItem( "Operators", "^", " ^ ", tr( "Power operator" ) ); + registerItem( "Operators", "=", " = ", tr( "Equal operator" ) ); + registerItem( "Operators", ">", " > ", tr( "Greater as operator" ) ); + registerItem( "Operators", "<", " < ", tr( "Less than operator" ) ); + registerItem( "Operators", "<>", " <> ", tr( "Unequal operator" ) ); + registerItem( "Operators", "<=", " <= ", tr( "Less or equal operator" ) ); + registerItem( "Operators", ">=", " >= ", tr( "Greater or equal operator" ) ); + registerItem( "Operators", "||", " || ", + QString( "|| %1
%2
%3:%4" ) + .arg( tr( "(String Concatenation)" ) ) + .arg( tr( "Joins two values together into a string" ) ) + .arg( tr( "Usage" ) ) + .arg( tr( "'Dia' || Diameter" ) ) ); + registerItem( "Operators", "IN", " IN " ); + registerItem( "Operators", "LIKE", " LIKE " ); + registerItem( "Operators", "ILIKE", " ILIKE " ); + registerItem( "Operators", "IS", " IS " ); + registerItem( "Operators", "OR", " OR " ); + registerItem( "Operators", "AND", " AND " ); + registerItem( "Operators", "NOT", " NOT " ); + + QString casestring = "CASE WHEN condition THEN result END"; + QString caseelsestring = "CASE WHEN condition THEN result ELSE result END"; + registerItem( "Conditionals", "CASE", casestring ); + registerItem( "Conditionals", "CASE ELSE", caseelsestring ); + + // Load the functions from the QgsExpression class + int count = QgsExpression::functionCount(); + for ( int i = 0; i < count; i++ ) + { + QgsExpression::Function* func = QgsExpression::Functions()[i]; + QString name = func->name(); + if ( name.startsWith( "_" ) ) // do not display private functions + continue; + if ( func->params() != 0 ) + name += "("; + registerItem( func->group(), func->name(), " " + name + " ", func->helptext() ); + } + + QList specials = QgsExpression::specialColumns(); + for ( int i = 0; i < specials.size(); ++i ) + { + QString name = specials[i]->name(); + registerItem( specials[i]->group(), name, " " + name + " " ); + } +} + void QgsExpressionBuilderWidget::setGeomCalculator( const QgsDistanceArea & da ) { mDa = da; @@ -299,11 +421,6 @@ void QgsExpressionBuilderWidget::setGeomCalculator( const QgsDistanceArea & da ) QString QgsExpressionBuilderWidget::expressionText() { - if ( QgsPythonRunner::isValid() ) - { - QString pythontext = txtPython->text(); - QgsPythonRunner::run( pythontext ); - } return txtExpressionString->text(); } diff --git a/src/gui/qgsexpressionbuilderwidget.h b/src/gui/qgsexpressionbuilderwidget.h index 63d144ae564..a7e18138f96 100644 --- a/src/gui/qgsexpressionbuilderwidget.h +++ b/src/gui/qgsexpressionbuilderwidget.h @@ -150,8 +150,32 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp void loadRecent( QString key ); + /** Create a new file in the function editor + */ + void newFunctionFile( QString fileName = "scratch"); + + /** Save the current function editor text to the given file. + */ + void saveFunctionFile( QString fileName ); + + /** Load code from the given file into the function editor + */ + void loadCodeFromFile( QString path ); + + /** Load code into the function editor + */ + void loadFunctionCode( QString code ); + + /** Update the list of function files found at the given path + */ + void updateFunctionFileList( QString path ); + public slots: void currentChanged( const QModelIndex &index, const QModelIndex & ); + void on_btnRun_pressed(); + void on_btnNewFile_pressed(); + void on_cmbFileNames_currentIndexChanged( int index ); + void on_btnSaveFile_pressed(); void on_expressionTree_doubleClicked( const QModelIndex &index ); void on_txtExpressionString_textChanged(); void on_txtSearchEdit_textChanged(); @@ -174,9 +198,12 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp void expressionParsed( bool isValid ); private: + void runPythonCode( QString code ); + void updateFunctionTree(); void fillFieldValues( int fieldIndex, int countLimit ); QString loadFunctionHelp( QgsExpressionItem* functionName ); + QString mFunctionsPath; QgsVectorLayer *mLayer; QStandardItemModel *mModel; QgsExpressionItemSearchProxy *mProxyModel; diff --git a/src/gui/symbology-ng/qgsdatadefinedsymboldialog.cpp b/src/gui/symbology-ng/qgsdatadefinedsymboldialog.cpp index d4391baa7cc..bbd17b6a1fd 100644 --- a/src/gui/symbology-ng/qgsdatadefinedsymboldialog.cpp +++ b/src/gui/symbology-ng/qgsdatadefinedsymboldialog.cpp @@ -5,6 +5,7 @@ #include "qgslogger.h" #include +#include QgsDataDefinedSymbolDialog::QgsDataDefinedSymbolDialog( const QList< DataDefinedSymbolEntry >& entries, const QgsVectorLayer* vl, QWidget * parent, Qt::WindowFlags f ) diff --git a/src/python/qgspythonutilsimpl.cpp b/src/python/qgspythonutilsimpl.cpp index a06f95d812f..f9a07f83952 100644 --- a/src/python/qgspythonutilsimpl.cpp +++ b/src/python/qgspythonutilsimpl.cpp @@ -157,6 +157,7 @@ void QgsPythonUtilsImpl::initPython( QgisInterface* interface ) return; } + // tell the utils script where to look for the plugins runString( "qgis.utils.plugin_paths = [" + pluginpaths.join( "," ) + "]" ); runString( "qgis.utils.sys_plugin_path = \"" + pluginsPath() + "\"" ); @@ -169,8 +170,14 @@ void QgsPythonUtilsImpl::initPython( QgisInterface* interface ) // initialize 'iface' object runString( "qgis.utils.initInterface(" + QString::number(( unsigned long ) interface ) + ")" ); - QString startuppath = homePythonPath() + " + \"/startup.py\""; - runString( "if os.path.exists(" + startuppath + "): from startup import *\n" ); + // import QGIS user + error_msg = QObject::tr( "Couldn't load QGIS user." ) + "\n" + QObject::tr( "Python support will be disabled." ); + if ( !runString( "import qgis.user", error_msg ) ) + { + // Should we really bail because of this?! + exitPython(); + return; + } // release GIL! // Later on, we acquire GIL just before doing some Python calls and diff --git a/src/ui/qgsexpressionbuilder.ui b/src/ui/qgsexpressionbuilder.ui index 31199302cc9..1bb8aa79032 100644 --- a/src/ui/qgsexpressionbuilder.ui +++ b/src/ui/qgsexpressionbuilder.ui @@ -19,357 +19,37 @@ Form - - + + 0 - - - - Qt::Horizontal + + 0 + + + 0 + + + 0 + + + + + 0 - - 4 + + true - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - Expression - - - true - - - - 0 - - - - - - - - - 0 - 0 - - - - - 27 - 0 - - - - - 300 - 16777215 - - - - - 20 - 0 - - - - - 7 - 0 - - - - Qt::LeftToRight - - - false - - - - 2 - - - 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 - - - ) - - - - - - - - - - - - - 0 - 0 - - - - Output preview is generated <br> using the first feature from the layer. - - - Output preview: - - - - - - - - 0 - 0 - - - - - 50 - true - false - false - - - - Output preview is generated <br> using the first feature from the layer. - - - QFrame::NoFrame - - - QFrame::Sunken - - - 0 - - - 0 - - - - - - false - - - false - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - Custom function - - - true - - - - 0 - - - 9 - - - 0 - - - 0 - - - - mOperatorsGroupBox - - - - - - - - - - - - - Functions - - - true - - + + + Expression + + 0 - 3 + 0 0 @@ -377,165 +57,610 @@ 0 - - 6 - - + + + + Qt::Horizontal + + + 4 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + Expression + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + Output preview is generated <br> using the first feature from the layer. + + + Output preview: + + + + + + + + 0 + 0 + + + + + 50 + true + false + false + + + + Output preview is generated <br> using the first feature from the layer. + + + QFrame::NoFrame + + + QFrame::Sunken + + + 0 + + + 0 + + + + + + false + + + false + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + 0 + 0 + + + + + 27 + 0 + + + + + 300 + 16777215 + + + + + 20 + 0 + + + + + 7 + 0 + + + + Qt::LeftToRight + + + false + + + + 2 + + + 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 + + + ) + + + + + + + + + + + + + + + + + Functions + + + true + + + + 0 + + + 3 + + + 0 + + + 0 + + + 6 + + + 0 + + + + + true + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + true + + + false + + + + + + + 0 + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + false + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + QListView::ListMode + + + + + + + Values + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 5 + 20 + + + + + + + + Load values + + + + + + + all unique + + + + + + + 10 samples + + + + + + + + + + + + + + true + + + + + + + + + + + + + + Function Editor + + + 0 - - - - true - - + + 0 + + + 0 + + + 0 + + + - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - QAbstractItemView::NoEditTriggers - - - false - - - false - - - true - - - false - - - - - - - 0 - + + QLayout::SetDefaultConstraint - - 0 - - - 0 - - - 0 - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - false - - - QAbstractItemView::NoEditTriggers - - - false - - - true - - - QListView::ListMode - - - - - - - Values - - - - - - - - - - - 0 - - - - - Qt::Horizontal - - - - 5 - 20 - - - - - - - - Load values - - - - - - - all unique - - - - - - - 10 samples - - - - - - - - - - - - - + + + ... + + + + :/images/themes/default/console/iconRunScriptConsole.png:/images/themes/default/console/iconRunScriptConsole.png + + true + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 20 + 20 + + + + + + + + New + + + + :/images/themes/default/console/iconTabEditorConsole.png:/images/themes/default/console/iconTabEditorConsole.png + + + true + + + + + + + + 0 + 0 + + + + true + + + + + + + Save + + + + :/images/themes/default/console/iconSaveConsole.png:/images/themes/default/console/iconSaveConsole.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -543,9 +668,6 @@ - mLoadGroupBox - splitter - groupBox @@ -553,12 +675,6 @@ QLineEdit
qgsfilterlineedit.h
- - QgsCollapsibleGroupBox - QGroupBox -
qgscollapsiblegroupbox.h
- 1 -
QgsCodeEditorSQL QWidget @@ -572,6 +688,8 @@ 1
- + + +