Skip to content
Permalink
Browse files

[FEATURE] customizable attribute forms using Qt Designer dialog UIs

- add support for checkbox edit type and edit forms for vector layers
- selection of ui file in vector layer properties (general tab)
- the forms are opened when a feature is added or via identify.
  The widgets on the ui have to be named like the attribute they are supposed
  to edit or show.  If the vector layer is not in editing mode, the widgets
  will be disabled



git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@12077 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
jef
jef committed Nov 10, 2009
1 parent 64eeec8 commit 88e59f5ec918aec1afd5780e2588cbd5f3c2b5e6
@@ -1,15 +1,15 @@
SUBDIRS(plugins)
IF (WIN32)
SET(BINDINGS_CORE_LIB ${CMAKE_CURRENT_BINARY_DIR}/core/core.pyd)
SET(BINDINGS_GUI_LIB ${CMAKE_CURRENT_BINARY_DIR}/gui/gui.pyd)
SET(BINDINGS_ANALYSIS_LIB ${CMAKE_CURRENT_BINARY_DIR}/analysis/analysis.pyd)
SET(BINDINGS_CORE_LIB ${CMAKE_CURRENT_BINARY_DIR}/core/core.pyd)
SET(BINDINGS_GUI_LIB ${CMAKE_CURRENT_BINARY_DIR}/gui/gui.pyd)
SET(BINDINGS_ANALYSIS_LIB ${CMAKE_CURRENT_BINARY_DIR}/analysis/analysis.pyd)
IF (NOT MSVC)
SET(QGIS_CORE_LIB ${CMAKE_BINARY_DIR}/src/core/libqgis_core.dll)
SET(QGIS_GUI_LIB ${CMAKE_BINARY_DIR}/src/gui/libqgis_gui.dll)
SET(QGIS_ANALYSIS_LIB ${CMAKE_BINARY_DIR}/src/analysis/libqgis_analysis.dll)
ELSE (NOT MSVC)
SET(QGIS_CORE_LIB ${CMAKE_BINARY_DIR}/src/core/${CMAKE_CFG_INTDIR}/qgis_core.lib)
SET(QGIS_GUI_LIB ${CMAKE_BINARY_DIR}/src/gui/${CMAKE_CFG_INTDIR}/qgis_gui.lib)
SET(QGIS_CORE_LIB ${CMAKE_BINARY_DIR}/src/core/${CMAKE_CFG_INTDIR}/qgis_core.lib)
SET(QGIS_GUI_LIB ${CMAKE_BINARY_DIR}/src/gui/${CMAKE_CFG_INTDIR}/qgis_gui.lib)
SET(QGIS_ANALYSIS_LIB ${CMAKE_BINARY_DIR}/src/analysis/${CMAKE_CFG_INTDIR}/qgis_analysis.lib)
ENDIF (NOT MSVC)
ELSE (WIN32)
@@ -60,9 +60,9 @@ ENDIF (MSVC)
ADD_CUSTOM_COMMAND(OUTPUT ${BINDINGS_CORE_MAKEFILE} ${BINDINGS_GUI_MAKEFILE} ${BINDINGS_ANALYSIS_MAKEFILE} PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE}
ARGS ${CMAKE_CURRENT_BINARY_DIR}/configure.py ${CMAKE_CFG_INTDIR} ${EXPORT}
DEPENDS ${QGIS_CORE_LIB} ${QGIS_GUI_LIB} ${QGIS_ANALYSIS_LIB}
${CMAKE_CURRENT_BINARY_DIR}/configure.py
${CORE_SIP_FILES} ${GUI_SIP_FILES} ${ANALYSIS_SIP_FILES})
DEPENDS ${QGIS_CORE_LIB} ${QGIS_GUI_LIB} ${QGIS_ANALYSIS_LIB}
${CMAKE_CURRENT_BINARY_DIR}/configure.py
${CORE_SIP_FILES} ${GUI_SIP_FILES} ${ANALYSIS_SIP_FILES})

# Step 3: run make in core and gui subdirs
ADD_CUSTOM_COMMAND(OUTPUT ${BINDINGS_CORE_LIB} PRE_LINK
@@ -13,9 +13,10 @@ public:
Classification,
EditRange,
SliderRange
CheckBox, /* @note added in 1.4 */
FileName,
Enumeration,
Immutable
Enumeration, /* @note added in 1.4 */
Immutable /* @note added in 1.4 */
};

struct RangeData {
@@ -130,7 +131,7 @@ public:
/** Write the symbology for the layer into the docment provided.
* @param QDomNode the node that will have the style element added to it.
* @param QDomDocument the document that will have the QDomNode added.
* @param errorMessage reference to string that will be updated with any error messages
* @param errorMessage reference to string that will be updated with any error messages
* @return true in case of success.
*/
bool writeSymbology(QDomNode&, QDomDocument& doc, QString& errorMessage) const;
@@ -154,7 +155,7 @@ public:
* @param subset The subset string. This may be the where clause of a sql statement
* or other defintion string specific to the underlying dataprovider
* and data store.
* @return true, when setting the string was successful, false otherwise (added in 1.4)
* @return true, when setting the string was successful, false otherwise (@note added in 1.4)
*/
virtual bool setSubsetString(QString subset);

@@ -387,6 +388,27 @@ public:
/**set edit type*/
void setEditType(int idx, EditType edit);

/** set string representing 'true' for a checkbox
@note added in 1.4
*/
void setCheckedState( int idx, QString checked, QString notChecked );

/** return string representing 'true' for a checkbox
@note added in 1.4
*/
// FIXME: need SIP binding for QPair<QString, QString>
// QPair<QString, QString> checkedState( int idx );

/** get edit form
@note added in 1.4
*/
QString editForm();

/** set edit form
@note added in 1.4
*/
void setEditForm( QString ui );

/**access value map*/
QMap<QString, QVariant> &valueMap(int idx);

@@ -52,7 +52,6 @@ bool QgsOverlayAnalyzer::intersection( QgsVectorLayer* layerA, QgsVectorLayer* l

QgsVectorFileWriter vWriter( shapefileName, dpA->encoding(), fieldsA, outputType, &crs );
QgsFeature currentFeature;
QgsGeometry* dissolveGeometry; //dissolve geometry (if dissolve enabled)
QgsSpatialIndex index;

//take only selection
@@ -251,6 +251,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR} composer legend attributetable
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/../ui
${QT_QTUITOOLS_INCLUDE_DIR}
../core
../core/composer ../core/raster ../core/renderer ../core/symbology
../gui
@@ -295,6 +296,7 @@ TARGET_LINK_LIBRARIES(qgis
${QT_QTSVG_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${QT_QTSQL_LIBRARY}
${QT_QTUITOOLS_LIBRARY}
#should only be needed for win
${QT_QTMAIN_LIBRARY}
qgis_core
@@ -62,7 +62,7 @@ QWidget *QgsAttributeTableDelegate::createEditor(
if ( vl == NULL )
return NULL;

QWidget *widget = QgsAttributeEditor::createAttributeEditor( parent, vl, fieldIdx( index ), index.model()->data( index, Qt::EditRole ) );
QWidget *widget = QgsAttributeEditor::createAttributeEditor( parent, 0, vl, fieldIdx( index ), index.model()->data( index, Qt::EditRole ) );

return widget;
}
@@ -30,82 +30,168 @@
#include <QLabel>
#include <QFrame>
#include <QScrollArea>
#include <QFile>
#include <QDialogButtonBox>
#include <QUiLoader>
#include <QDialog>
#include <QVBoxLayout>

QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature )
: QDialog(),
: mDialog( 0 ),
mSettingsPath( "/Windows/AttributeDialog/" ),
mLayer( vl ),
mpFeature( thepFeature )
{
setupUi( this );
if ( mpFeature == NULL || vl->dataProvider() == NULL )
return;

const QgsFieldMap &theFieldMap = vl->pendingFields();

if ( theFieldMap.isEmpty() )
return;

QgsAttributeMap myAttributes = mpFeature->attributeMap();
//
//Set up dynamic inside a scroll box
//
QVBoxLayout * mypOuterLayout = new QVBoxLayout();
mypOuterLayout->setContentsMargins( 0, 0, 0, 0 );
//transfers layout ownership so no need to call delete
mFrame->setLayout( mypOuterLayout );
QScrollArea * mypScrollArea = new QScrollArea();
//transfers scroll area ownership so no need to call delete
mypOuterLayout->addWidget( mypScrollArea );
QFrame * mypInnerFrame = new QFrame();
mypInnerFrame->setFrameShape( QFrame::NoFrame );
mypInnerFrame->setFrameShadow( QFrame::Plain );
//transfers frame ownership so no need to call delete
mypScrollArea->setWidget( mypInnerFrame );
mypScrollArea->setWidgetResizable( true );
QGridLayout * mypInnerLayout = new QGridLayout( mypInnerFrame );

int index = 0;
for ( QgsAttributeMap::const_iterator it = myAttributes.begin();
it != myAttributes.end();
++it )
{
const QgsField &field = theFieldMap[it.key()];

//show attribute alias if available
QString myFieldName = vl->attributeDisplayName( it.key() );
int myFieldType = field.type();
QDialogButtonBox *buttonBox = NULL;

QWidget *myWidget = QgsAttributeEditor::createAttributeEditor( 0, vl, it.key(), it.value() );
if ( !myWidget )
continue;
if ( !vl->editForm().isEmpty() )
{
QFile file( vl->editForm() );
file.open( QFile::ReadOnly );
QUiLoader loader;
QWidget *myWidget = loader.load( &file, NULL );
file.close();

mDialog = qobject_cast<QDialog*>( myWidget );
buttonBox = myWidget->findChild<QDialogButtonBox*>();
}

QLabel * mypLabel = new QLabel();
mypInnerLayout->addWidget( mypLabel, index, 0 );
if ( myFieldType == QVariant::Int )
if ( !mDialog )
{
mDialog = new QDialog();

QGridLayout *gridLayout;
QFrame *mFrame;

if ( mDialog->objectName().isEmpty() )
mDialog->setObjectName( QString::fromUtf8( "QgsAttributeDialogBase" ) );

mDialog->resize( 447, 343 );
gridLayout = new QGridLayout( mDialog );
gridLayout->setSpacing( 6 );
gridLayout->setMargin( 11 );
gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) );
mFrame = new QFrame( mDialog );
mFrame->setObjectName( QString::fromUtf8( "mFrame" ) );
mFrame->setFrameShape( QFrame::StyledPanel );
mFrame->setFrameShadow( QFrame::Raised );

gridLayout->addWidget( mFrame, 0, 0, 1, 1 );

buttonBox = new QDialogButtonBox( mDialog );
buttonBox->setObjectName( QString::fromUtf8( "buttonBox" ) );
gridLayout->addWidget( buttonBox, 2, 0, 1, 1 );

//
//Set up dynamic inside a scroll box
//
QVBoxLayout * mypOuterLayout = new QVBoxLayout();
mypOuterLayout->setContentsMargins( 0, 0, 0, 0 );
//transfers layout ownership so no need to call delete

mFrame->setLayout( mypOuterLayout );
QScrollArea * mypScrollArea = new QScrollArea();
//transfers scroll area ownership so no need to call delete
mypOuterLayout->addWidget( mypScrollArea );
QFrame *mypInnerFrame = new QFrame();
mypInnerFrame->setFrameShape( QFrame::NoFrame );
mypInnerFrame->setFrameShadow( QFrame::Plain );
//transfers frame ownership so no need to call delete
mypScrollArea->setWidget( mypInnerFrame );
mypScrollArea->setWidgetResizable( true );
QGridLayout * mypInnerLayout = new QGridLayout( mypInnerFrame );

int index = 0;
for ( QgsAttributeMap::const_iterator it = myAttributes.begin(); it != myAttributes.end(); ++it )
{
mypLabel->setText( myFieldName + tr( " (int)" ) );
const QgsField &field = theFieldMap[it.key()];

//show attribute alias if available
QString myFieldName = vl->attributeDisplayName( it.key() );
int myFieldType = field.type();

QWidget *myWidget = QgsAttributeEditor::createAttributeEditor( 0, 0, vl, it.key(), it.value() );
if ( !myWidget )
continue;

QLabel * mypLabel = new QLabel();
mypInnerLayout->addWidget( mypLabel, index, 0 );
if ( myFieldType == QVariant::Int )
{
mypLabel->setText( myFieldName + tr( " (int)" ) );
}
else if ( myFieldType == QVariant::Double )
{
mypLabel->setText( myFieldName + tr( " (dbl)" ) );
}
else //string
{
//any special behaviour for string goes here
mypLabel->setText( myFieldName + tr( " (txt)" ) );
}

myWidget->setEnabled( vl->isEditable() );

mypInnerLayout->addWidget( myWidget, index, 1 );
mpIndizes << it.key();
mpWidgets << myWidget;
++index;
}
else if ( myFieldType == QVariant::Double )
// Set focus to first widget in list, to help entering data without moving the mouse.
if ( mpWidgets.size() > 0 )
{
mypLabel->setText( myFieldName + tr( " (dbl)" ) );
mpWidgets.first()->setFocus( Qt::OtherFocusReason );
}
else //string
}
else
{
for ( QgsAttributeMap::const_iterator it = myAttributes.begin(); it != myAttributes.end(); ++it )
{
//any special behaviour for string goes here
mypLabel->setText( myFieldName + tr( " (txt)" ) );
}
const QgsField &field = theFieldMap[it.key()];

QWidget *myWidget = mDialog->findChild<QWidget*>( field.name() );
if ( !myWidget )
continue;

QgsAttributeEditor::createAttributeEditor( mDialog, myWidget, vl, it.key(), it.value() );

mypInnerLayout->addWidget( myWidget, index, 1 );
mpIndizes << it.key();
mpWidgets << myWidget;
++index;
myWidget->setEnabled( vl->isEditable() );

mpIndizes << it.key();
mpWidgets << myWidget;
}
}
// Set focus to first widget in list, to help entering data without moving the mouse.
if ( mpWidgets.size() > 0 )

if ( buttonBox )
{
mpWidgets.first()->setFocus( Qt::OtherFocusReason );
buttonBox->clear();

if( vl->isEditable() )
{
buttonBox->setStandardButtons( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
connect( buttonBox, SIGNAL( accepted() ), mDialog, SLOT( accept() ) );
connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
}
else
{
buttonBox->setStandardButtons( QDialogButtonBox::Cancel );
}

connect( buttonBox, SIGNAL( rejected() ), mDialog, SLOT( reject() ) );
connect( buttonBox, SIGNAL( rejected() ), this, SLOT( rejected() ) );
}

QMetaObject::connectSlotsByName( mDialog );

restoreGeometry();
}

@@ -117,12 +203,13 @@ QgsAttributeDialog::~QgsAttributeDialog()

void QgsAttributeDialog::accept()
{
if ( !mLayer->isEditable() )
return;

//write the new values back to the feature
QgsAttributeMap myAttributes = mpFeature->attributeMap();
int myIndex = 0;
for ( QgsAttributeMap::const_iterator it = myAttributes.begin();
it != myAttributes.end();
++it )
for ( QgsAttributeMap::const_iterator it = myAttributes.begin(); it != myAttributes.end(); ++it )
{
QVariant value;

@@ -132,17 +219,21 @@ void QgsAttributeDialog::accept()

++myIndex;
}
QDialog::accept();
}

int QgsAttributeDialog::exec()
{
return mDialog->exec();
}

void QgsAttributeDialog::saveGeometry()
{
QSettings settings;
settings.setValue( mSettingsPath + "geometry", QDialog::saveGeometry() );
settings.setValue( mSettingsPath + "geometry", mDialog->saveGeometry() );
}

void QgsAttributeDialog::restoreGeometry()
{
QSettings settings;
QDialog::restoreGeometry( settings.value( mSettingsPath + "geometry" ).toByteArray() );
mDialog->restoreGeometry( settings.value( mSettingsPath + "geometry" ).toByteArray() );
}

0 comments on commit 88e59f5

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