Skip to content
Permalink
Browse files

Refactored the python init function selector

Do not rely on the presence of a dot to load
a module. Loading a module can now be achieved
through the code editor in the dialog.

* added an option to load from environment
* added a file selector to specify the file
* changed tooltips accordingly
  • Loading branch information
elpaso committed Nov 24, 2015
1 parent de3ce15 commit e5ca63deed47d80aa42c31f2b4e2ad99fa75cbaf
@@ -57,6 +57,18 @@ class QgsEditFormConfig : QObject
SuppressOff = 2 //!< Do not suppress feature form
};

/**
* The python init code source options.
*/
enum PythonInitCodeSource
{
CodeSourceNone = 0, //!< Do not use python code at all
CodeSourceFile = 1, //!< Load the python code from a file
CodeSourceDialog = 2, //!< Use the python code provided in the dialog
CodeSourceEnvironment = 3 //!< Use the python code available in the python environment
};


/**
* This is only useful in combination with EditorLayout::TabLayout.
*
@@ -208,15 +220,6 @@ class QgsEditFormConfig : QObject
void setLabelOnTop( int idx, bool onTop );











// Python stuff


@@ -237,22 +240,38 @@ class QgsEditFormConfig : QObject
void setInitFunction( const QString& function );

/**
* Get python code for edit form initialization.
* Get python code for edit form initialization from the configuration dialog.
*/
QString initCode() const;

/**
* Get python external file path for edit form initialization.
*/
QString initFilePath() const;

/**
* Set python external file path for edit form initialization.
* Make sure that you also set the appropriate function name in
* @link setInitFunction @endlink
*/
void setInitFilePath( const QString& filePath );

/**
* Get python code for edit form initialization.
* Make sure that you also set the appropriate function name in
* @link setInitFunction @endlink
*/
void setInitCode( const QString& code );

/** Return if python code shall be loaded for edit form initialization */
bool useInitCode() const;
/** Return python code source for edit form initialization
* (if it shall be loaded from a file, read from the
* provided dialog editor or just read from the environment
*/
PythonInitCodeSource initCodeSource() const;

/** Set if python code shall be used for edit form initialization */
void setUseInitCode( const bool useCode );
void setInitCodeSource( const PythonInitCodeSource initCodeSource );


/** Type of feature form pop-up suppression after feature creation (overrides app setting) */
FeatureFormSuppress suppress() const;
@@ -115,6 +115,12 @@ QgsFieldsProperties::QgsFieldsProperties( QgsVectorLayer *layer, QWidget* parent
mRelationsList->setHorizontalHeaderItem( RelFieldCol, new QTableWidgetItem( tr( "Field" ) ) );
mRelationsList->verticalHeader()->hide();

// Init function stuff
mInitCodeSourceComboBox->addItem( tr( "" ) );
mInitCodeSourceComboBox->addItem( tr( "Load from external file" ) );
mInitCodeSourceComboBox->addItem( tr( "Provide code in this dialog" ) );
mInitCodeSourceComboBox->addItem( tr( "Load from the environment" ) );

loadRelations();

updateButtons();
@@ -183,11 +189,15 @@ QTreeWidgetItem *QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeE
return newWidget;
}

void QgsFieldsProperties::setEditFormInit( const QString &editForm, const QString &editFormInit, const QString &editFormInitCode, const bool editFormInitUseCode )
void QgsFieldsProperties::setEditFormInit( const QString &editForm,
const QString &initFunction,
const QString &initCode,
const QString &initFilePath,
const QgsEditFormConfig::PythonInitCodeSource &codeSource )
{

// Python init function and code
QString code( editFormInitCode );
QString code( initCode );
if ( code.isEmpty( ) )
{
code.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
@@ -206,12 +216,11 @@ void QgsFieldsProperties::setEditFormInit( const QString &editForm, const QStrin
"\tcontrol = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );

}
leEditForm->setText( editForm );
leEditFormInitCode->setText( code );
leEditFormInit->setText( editFormInit );
leEditFormInitUseCode->setChecked( editFormInitUseCode );
// Show or hide as needed
mPythonInitCodeGroupBox->setVisible( editFormInitUseCode );
mEditFormLineEdit->setText( editForm );
mInitFilePathLineEdit->setText( initFilePath );
mInitCodeEditorPython->setText( code );
mInitFunctionLineEdit->setText( initFunction );
mInitCodeSourceComboBox->setCurrentIndex( codeSource );
}


@@ -453,9 +462,13 @@ void QgsFieldsProperties::on_mMoveUpItem_clicked()
}
}

void QgsFieldsProperties::on_leEditFormInitUseCode_toggled( bool checked )
void QgsFieldsProperties::on_mInitCodeSourceComboBox_currentIndexChanged( int codeSource )
{
mPythonInitCodeGroupBox->setVisible( checked );
// Show or hide ui elements as needed
mInitFunctionContainer->setVisible( codeSource != QgsEditFormConfig::PythonInitCodeSource::CodeSourceNone );
mPythonInitCodeGroupBox->setVisible( codeSource == QgsEditFormConfig::PythonInitCodeSource::CodeSourceDialog );
mInitFilePathLineEdit->setVisible( codeSource == QgsEditFormConfig::PythonInitCodeSource::CodeSourceFile );
mInitFilePathLabel->setVisible( codeSource == QgsEditFormConfig::PythonInitCodeSource::CodeSourceFile );
}

void QgsFieldsProperties::attributeTypeDialog()
@@ -805,6 +818,22 @@ QgsAttributeEditorElement* QgsFieldsProperties::createAttributeEditorWidget( QTr
return widgetDef;
}


void QgsFieldsProperties::on_pbtnSelectInitFilePath_clicked()
{
QSettings myQSettings;
QString lastUsedDir = myQSettings.value( "style/lastUIDir", "." ).toString();
QString pyfilename = QFileDialog::getOpenFileName( this, tr( "Select Python file" ), lastUsedDir, tr( "Python file" ) + " (*.py)" );

if ( pyfilename.isNull() )
return;

QFileInfo fi( pyfilename );
myQSettings.setValue( "style/lastUIDir", fi.path() );
mInitFilePathLineEdit->setText( pyfilename );
}


void QgsFieldsProperties::on_pbnSelectEditForm_clicked()
{
QSettings myQSettings;
@@ -816,7 +845,7 @@ void QgsFieldsProperties::on_pbnSelectEditForm_clicked()

QFileInfo fi( uifilename );
myQSettings.setValue( "style/lastUIDir", fi.path() );
leEditForm->setText( uifilename );
mEditFormLineEdit->setText( uifilename );
}

void QgsFieldsProperties::on_mEditorLayoutComboBox_currentIndexChanged( int index )
@@ -875,10 +904,14 @@ void QgsFieldsProperties::apply()

mLayer->editFormConfig()->setLayout(( QgsEditFormConfig::EditorLayout ) mEditorLayoutComboBox->currentIndex() );
if ( mEditorLayoutComboBox->currentIndex() == QgsEditFormConfig::UiFileLayout )
mLayer->editFormConfig()->setUiForm( leEditForm->text() );
mLayer->editFormConfig()->setInitFunction( leEditFormInit->text() );
mLayer->editFormConfig()->setUseInitCode( leEditFormInitUseCode->isChecked() );
mLayer->editFormConfig()->setInitCode( leEditFormInitCode->text() );
mLayer->editFormConfig()->setUiForm( mEditFormLineEdit->text() );

// Init function configuration
mLayer->editFormConfig()->setInitFunction( mInitFunctionLineEdit->text( ) );
mLayer->editFormConfig()->setInitCode( mInitCodeEditorPython->text( ) );
mLayer->editFormConfig()->setInitFilePath( mInitFilePathLineEdit->text( ) );
mLayer->editFormConfig()->setInitCodeSource(( QgsEditFormConfig::PythonInitCodeSource )mInitCodeSourceComboBox->currentIndex() );

mLayer->editFormConfig()->setSuppress(( QgsEditFormConfig::FeatureFormSuppress )mFormSuppressCmbBx->currentIndex() );

mLayer->setExcludeAttributesWMS( excludeAttributesWMS );
@@ -163,11 +163,16 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
/**
* @brief setEditFormInit set the private ui fields
* @param editForm
* @param editFormInit
* @param editFormInitCode
* @param editFormInitUseCode
* @param initFunction
* @param initCode
* @param initFilePath
* @param codeSource
*/
void setEditFormInit( const QString &editForm, const QString &editFormInit, const QString &editFormInitCode, const bool editFormInitUseCode );
void setEditFormInit( const QString &editForm,
const QString &initFunction,
const QString &initCode,
const QString &initFilePath,
const QgsEditFormConfig::PythonInitCodeSource &codeSource );

signals:
void toggleEditing();
@@ -177,9 +182,10 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
void on_mDeleteAttributeButton_clicked();
void on_mCalculateFieldButton_clicked();
void onAttributeSelectionChanged();
void on_pbtnSelectInitFilePath_clicked();
void on_pbnSelectEditForm_clicked();
void on_mEditorLayoutComboBox_currentIndexChanged( int index );
void on_leEditFormInitUseCode_toggled( bool checked );
void on_mInitCodeSourceComboBox_currentIndexChanged( int codeSource );
void attributeAdded( int idx );
void attributeDeleted( int idx );
void attributeTypeDialog();
@@ -1317,5 +1317,5 @@ void QgsVectorLayerProperties::updateVariableEditor()
void QgsVectorLayerProperties::updateFieldsPropertiesDialog()
{
QgsEditFormConfig* cfg = layer->editFormConfig();
mFieldsPropertiesDialog->setEditFormInit( cfg->uiForm(), cfg->initFunction(), cfg->initCode(), cfg->useInitCode() );
mFieldsPropertiesDialog->setEditFormInit( cfg->uiForm(), cfg->initFunction(), cfg->initCode(), cfg->initFilePath(), cfg->initCodeSource() );
}
@@ -4,8 +4,8 @@
QgsEditFormConfig::QgsEditFormConfig( QObject* parent )
: QObject( parent )
, mEditorLayout( GeneratedLayout )
, mInitCodeSource( QgsEditFormConfig::PythonInitCodeSource::CodeSourceNone )
, mFeatureFormSuppress( SuppressDefault )
, mUseInitCode( false )
{
connect( QgsProject::instance()->relationManager(), SIGNAL( relationsLoaded() ), this, SLOT( onRelationsLoaded() ) );
}
@@ -271,6 +271,7 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
Q_OBJECT

public:

/** The different types to layout the attribute editor. */
enum EditorLayout
{
@@ -308,6 +309,17 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
SuppressOff = 2 //!< Do not suppress feature form
};

/**
* The python init code source options.
*/
enum PythonInitCodeSource
{
CodeSourceNone = 0, //!< Do not use python code at all
CodeSourceFile = 1, //!< Load the python code from an external file
CodeSourceDialog = 2, //!< Use the python code provided in the dialog
CodeSourceEnvironment = 3 //!< Use the python code available in the python environment
};

/**
* This is only useful in combination with EditorLayout::TabLayout.
*
@@ -459,17 +471,7 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
void setLabelOnTop( int idx, bool onTop );











// Python stuff

// Python form init function stuff

/**
* Get python function for edit form initialization.
@@ -490,20 +492,35 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
/**
* Get python code for edit form initialization.
*/
QString initCode() const { return mEditFormInitCode; }
QString initCode() const { return mInitCode; }

/**
* Get python code for edit form initialization.
* Set python code for edit form initialization.
* Make sure that you also set the appropriate function name in
* @link setInitFunction @endlink
*/
void setInitCode( const QString& code ) { mEditFormInitCode = code; }
void setInitCode( const QString& code ) { mInitCode = code; }

/**
* Get python external file path for edit form initialization.
*/
QString initFilePath() const { return mInitFilePath; }

/** Return if python code shall be loaded for edit form initialization */
bool useInitCode() const { return mUseInitCode; }
/**
* Set python external file path for edit form initialization.
* Make sure that you also set the appropriate function name in
* @link setInitFunction @endlink
*/
void setInitFilePath( const QString& filePath ) { mInitFilePath = filePath; }

/** Return python code source for edit form initialization
* (if it shall be loaded from a file, read from the
* provided dialog editor or inherited from the environment)
*/
PythonInitCodeSource initCodeSource() const { return mInitCodeSource; }

/** Set if python code shall be used for edit form initialization */
void setUseInitCode( const bool useCode ) { mUseInitCode = useCode; }
/** Set if python code shall be used for edit form initialization and its origin */
void setInitCodeSource( const PythonInitCodeSource initCodeSource ) { mInitCodeSource = initCodeSource; }

/** Type of feature form pop-up suppression after feature creation (overrides app setting) */
FeatureFormSuppress suppress() const { return mFeatureFormSuppress; }
@@ -547,9 +564,16 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
/** Defines the default layout to use for the attribute editor (Drag and drop, UI File, Generated) */
EditorLayout mEditorLayout;

/** Init form instance */
QString mEditForm;
/** Name of the python form init function */
QString mInitFunction;
QString mEditFormInitCode;
/** Path of the python external file to be loaded */
QString mInitFilePath;
/** Choose the source of the init founction */
PythonInitCodeSource mInitCodeSource;
/** Python init code provided in the dialog */
QString mInitCode;

/** Type of feature form suppression after feature creation */
FeatureFormSuppress mFeatureFormSuppress;
@@ -1786,10 +1786,16 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
mEditFormConfig->setInitCode( editFormInitCodeNode.toElement().text() );
}

QDomNode editFormInitUseCodeNode = node.namedItem( "editforminitusecode" );
if ( !editFormInitCodeNode.isNull() || ( !editFormInitNode.isNull() && !editFormInitNode.toElement().text().isEmpty() ) )
QDomNode editFormInitCodeSourceNode = node.namedItem( "editforminitcodesource" );
if ( !editFormInitCodeSourceNode.isNull() || ( !editFormInitCodeSourceNode.isNull() && !editFormInitCodeSourceNode.toElement().text().isEmpty() ) )
{
mEditFormConfig->setUseInitCode( editFormInitUseCodeNode.toElement().text().toInt() );
mEditFormConfig->setInitCodeSource(( QgsEditFormConfig::PythonInitCodeSource ) editFormInitCodeSourceNode.toElement().text().toInt() );
}

QDomNode editFormInitFilePathNode = node.namedItem( "editforminitfilepath" );
if ( !editFormInitFilePathNode.isNull() || ( !editFormInitFilePathNode.isNull() && !editFormInitFilePathNode.toElement().text().isEmpty() ) )
{
mEditFormConfig->setInitFilePath( editFormInitFilePathNode.toElement().text() );
}

QDomNode fFSuppNode = node.namedItem( "featformsuppress" );
@@ -2054,9 +2060,14 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
efiField.appendChild( doc.createTextNode( mEditFormConfig->initFunction() ) );
node.appendChild( efiField );

QDomElement efiucField = doc.createElement( "editforminitusecode" );
efiucField.appendChild( doc.createTextNode( mEditFormConfig->useInitCode() ? "1" : "0" ) );
node.appendChild( efiucField );
QDomElement eficsField = doc.createElement( "editforminitcodesource" );
eficsField.appendChild( doc.createTextNode( QString::number( mEditFormConfig->initCodeSource() ) ) );
node.appendChild( eficsField );

QDomElement efifpField = doc.createElement( "editforminitfilepath" );
efifpField.appendChild( doc.createTextNode( mEditFormConfig->initFilePath() ) );
node.appendChild( efifpField );


QDomElement eficField = doc.createElement( "editforminitcode" );
eficField.appendChild( doc.createCDATASection( mEditFormConfig->initCode() ) );

5 comments on commit e5ca63d

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Dec 14, 2015

this causes some troubles with existing projects.
In projects where I had some external python file referenced, it was chosen "provide code in this dialog" with just an unfinished line in the editor.

Of course, this causes python error.

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Dec 14, 2015

@elpaso shall I open an issue or will you take care of it soon?

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Dec 14, 2015

Also, a code which was ok before doesn't seem to work anymore:


from PyQt4 import uic
from PyQt4.QtGui import QComboBox, QWidget

subForms = None

def formOpen(dialog, layer, feature):

    typeSelector = dialog.findChild(QComboBox, 'installation_type')
    typeSelector.currentIndexChanged.connect(typeSelection)

    global subForms
    subForms = []

    for i in range(0, typeSelector.count()):
        widgetName = typeSelector.itemData(i)
        childWidget = dialog.findChild(QWidget, widgetName)
        subForms.append(childWidget)

    typeSelection(typeSelector.currentIndex())


def typeSelection( index ):
    for i, widget in enumerate(subForms):
        widget.setVisible( i == index )

produces this error:

Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'formOpen' is not defined

am I doing anything wrong?

@elpaso

This comment has been minimized.

Copy link
Contributor Author

@elpaso elpaso replied Dec 14, 2015

@3nids please open an issue with a small test project and I'll take care of it ASAP.

I don't understand your second comment: if that is related or not with the first.

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Dec 14, 2015

Regarding the code, it's just that providing the same python file as external code (similarly as in 2.12) doesn't work anymore, QGIS throws the error above. I am not sure if something changed, it's just that this code used to work and not anymore.

So the 2 problems are:

  1. Losing the layer configuration see http://hub.qgis.org/issues/13984
  2. Changes that made the code above not working anymore or am I doing something wrong?
Please sign in to comment.
You can’t perform that action at this time.