[quick] [feature] Feature attribute panel

Allow to show feature attribute form and edit/save the modification
of the attributes. Support most frequent QGIS edit widgets such
as text edit, value map or external resource (photo capture)
This commit is contained in:
Peter Petrik 2018-05-31 09:59:54 +02:00
parent 5b655b3548
commit 4ca91b2858
47 changed files with 3413 additions and 230 deletions

View File

@ -13,6 +13,9 @@ QGIS Quick consists of a Qt plugin that provides the QML components and of a sha
\subsection qgsquick_overview_widgets QML Classes
\subsubsection qgsquick_overview_widgets_mapcanvas MapCanvas
\subsubsection qgsquick_overview_widgets_featureform FeatureForm
A form listing attributes of a given feature. It supports basic edit field widgets for types such as edit text, map value,
check box, date/time picker or external resource (photo capture).
\subsubsection qgsquick_overview_widgets_positionmarker PositionMarker
The element refers to current position according gps location device connected to it. It holds information about longitude, latitude, altitude,
direction of the movement and accuracy of the signal. See also QgsQuickPositionKit.

View File

@ -1,6 +1,11 @@
############################################################
# sources
SET(QGIS_QUICK_GUI_MOC_HDRS
attributes/qgsquickattributeformmodel.h
attributes/qgsquickattributeformmodelbase.h
attributes/qgsquickattributemodel.h
attributes/qgsquicksubmodel.h
qgsquickfeaturelayerpair.h
qgsquickcoordinatetransformer.h
qgsquickfeaturehighlight.h
@ -20,6 +25,11 @@ SET(QGIS_QUICK_GUI_HDRS
)
SET(QGIS_QUICK_GUI_SRC
attributes/qgsquickattributeformmodel.cpp
attributes/qgsquickattributeformmodelbase.cpp
attributes/qgsquickattributemodel.cpp
attributes/qgsquicksubmodel.cpp
qgsquickfeaturelayerpair.cpp
qgsquickcoordinatetransformer.cpp
qgsquickfeaturehighlight.cpp
@ -37,6 +47,7 @@ SET(QGIS_QUICK_GUI_SRC
INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/attributes
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src/core

View File

@ -0,0 +1,72 @@
/***************************************************************************
qgsquickattributeformmodel.cpp
--------------------------------------
Date : 22.9.2016
Copyright : (C) 2016 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsquickattributeformmodel.h"
#include "qgsquickattributeformmodelbase.h"
QgsQuickAttributeFormModel::QgsQuickAttributeFormModel( QObject *parent )
: QSortFilterProxyModel( parent )
, mSourceModel( new QgsQuickAttributeFormModelBase( this ) )
{
setSourceModel( mSourceModel );
connect( mSourceModel, &QgsQuickAttributeFormModelBase::hasTabsChanged, this, &QgsQuickAttributeFormModel::hasTabsChanged );
connect( mSourceModel, &QgsQuickAttributeFormModelBase::attributeModelChanged, this, &QgsQuickAttributeFormModel::attributeModelChanged );
connect( mSourceModel, &QgsQuickAttributeFormModelBase::constraintsValidChanged, this, &QgsQuickAttributeFormModel::constraintsValidChanged );
}
bool QgsQuickAttributeFormModel::hasTabs() const
{
return mSourceModel->hasTabs();
}
void QgsQuickAttributeFormModel::setHasTabs( bool hasTabs )
{
mSourceModel->setHasTabs( hasTabs );
}
QgsQuickAttributeModel *QgsQuickAttributeFormModel::attributeModel() const
{
return mSourceModel->attributeModel();
}
void QgsQuickAttributeFormModel::setAttributeModel( QgsQuickAttributeModel *attributeModel )
{
mSourceModel->setAttributeModel( attributeModel );
}
bool QgsQuickAttributeFormModel::constraintsValid() const
{
return mSourceModel->constraintsValid();
}
void QgsQuickAttributeFormModel::save()
{
mSourceModel->save();
}
void QgsQuickAttributeFormModel::create()
{
mSourceModel->create();
}
QVariant QgsQuickAttributeFormModel::attribute( const QString &name ) const
{
return mSourceModel->attribute( name );
}
bool QgsQuickAttributeFormModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
{
return mSourceModel->data( mSourceModel->index( source_row, 0, source_parent ), CurrentlyVisible ).toBool();
}

View File

@ -0,0 +1,122 @@
/***************************************************************************
qgsquickattributeformmodel.h
--------------------------------------
Date : 22.9.2016
Copyright : (C) 2016 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSQUICKATTRIBUTEFORMMODEL_H
#define QGSQUICKATTRIBUTEFORMMODEL_H
#include <QSortFilterProxyModel>
#include "qgis_quick.h"
class QgsQuickAttributeFormModelBase;
class QgsQuickAttributeModel;
class QVariant;
/**
* \ingroup quick
* This is a model implementation for attribute form of a feature from a vector layer.
*
* The model is based on vector layer's edit form config (QgsEditFormConfig). It supports
* auto-generated editor layouts and "tab" layout (layout defined with groups and tabs).
* The form layout gets flattened into a list, each row has a bunch of roles with values
* extracted from the edit form config.
*
* It also adds filtering of attribute (attributes may be visible or hidden based on expressions).
*
* \note QML Type: AttributeFormModel
*
* \since QGIS 3.4
*/
class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel
{
Q_OBJECT
//! Feature model with attributes
Q_PROPERTY( QgsQuickAttributeModel *attributeModel READ attributeModel WRITE setAttributeModel NOTIFY attributeModelChanged )
//! Whether use tabs layout
Q_PROPERTY( bool hasTabs READ hasTabs WRITE setHasTabs NOTIFY hasTabsChanged )
//! Returns true if all constraints defined on fields are satisfied with the current attribute values
Q_PROPERTY( bool constraintsValid READ constraintsValid NOTIFY constraintsValidChanged )
public:
//! Feature fields's roles
enum FeatureRoles
{
ElementType = Qt::UserRole + 1, //!< User role used to identify either "field" or "container" type of item
Name, //!< Field Name
AttributeValue, //!< Field Value
AttributeEditable, //!< Whether is field editable
EditorWidget, //!< Widget type to represent the data (text field, value map, ...)
EditorWidgetConfig, //!< Widget configuration
RememberValue, //!< Remember value (default value for next feature)
Field, //!< Field
FieldIndex, //!< Index
Group, //!< Group
AttributeEditorElement, //!< Attribute editor element
CurrentlyVisible, //!< Field visible
ConstraintValid, //!< Contraint valid
ConstraintDescription //!< Contraint description
};
Q_ENUM( FeatureRoles )
//! Create new attribute form model
QgsQuickAttributeFormModel( QObject *parent = nullptr );
//! \copydoc QgsQuickAttributeFormModel::hasTabs
bool hasTabs() const;
//! \copydoc QgsQuickAttributeFormModel::hasTabs
void setHasTabs( bool hasTabs );
//! \copydoc QgsQuickAttributeFormModel::attributeModel
QgsQuickAttributeModel *attributeModel() const;
//! \copydoc QgsQuickAttributeFormModel::attributeModel
void setAttributeModel( QgsQuickAttributeModel *attributeModel );
//! \copydoc QgsQuickAttributeFormModel::constraintsValid
bool constraintsValid() const;
//! Updates QgsFeature based on changes
Q_INVOKABLE void save();
//! Creates new QgsFeature
Q_INVOKABLE void create();
//! Returns attribute value with name
Q_INVOKABLE QVariant attribute( const QString &name ) const;
signals:
//! \copydoc QgsQuickAttributeFormModel::attributeModel
void attributeModelChanged();
//! \copydoc QgsQuickAttributeFormModel::hasTabs
void hasTabsChanged();
//! \copydoc QgsQuickAttributeFormModel::constraintsValid
void constraintsValidChanged();
protected:
virtual bool filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const override;
private:
QgsQuickAttributeFormModelBase *mSourceModel; //not owned
};
#endif // QGSQUICKATTRIBUTEFORMMODEL_H

View File

@ -0,0 +1,383 @@
/***************************************************************************
qgsquickattributemodelbase.cpp
--------------------------------------
Date : 16.8.2016
Copyright : (C) 2016 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgseditorwidgetsetup.h"
#include "qgsvectorlayer.h"
#include "qgsquickattributeformmodelbase.h"
#include "qgsquickattributeformmodel.h"
/// @cond PRIVATE
QgsQuickAttributeFormModelBase::QgsQuickAttributeFormModelBase( QObject *parent )
: QStandardItemModel( 0, 1, parent )
{
}
QHash<int, QByteArray> QgsQuickAttributeFormModelBase::roleNames() const
{
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
roles[QgsQuickAttributeFormModel::ElementType] = QByteArray( "Type" );
roles[QgsQuickAttributeFormModel::Name] = QByteArray( "Name" );
roles[QgsQuickAttributeFormModel::AttributeValue] = QByteArray( "AttributeValue" );
roles[QgsQuickAttributeFormModel::AttributeEditable] = QByteArray( "AttributeEditable" );
roles[QgsQuickAttributeFormModel::EditorWidget] = QByteArray( "EditorWidget" );
roles[QgsQuickAttributeFormModel::EditorWidgetConfig] = QByteArray( "EditorWidgetConfig" );
roles[QgsQuickAttributeFormModel::RememberValue] = QByteArray( "RememberValue" );
roles[QgsQuickAttributeFormModel::Field] = QByteArray( "Field" );
roles[QgsQuickAttributeFormModel::Group] = QByteArray( "Group" );
roles[QgsQuickAttributeFormModel::ConstraintValid] = QByteArray( "ConstraintValid" );
roles[QgsQuickAttributeFormModel::ConstraintDescription] = QByteArray( "ConstraintDescription" );
return roles;
}
bool QgsQuickAttributeFormModelBase::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( data( index, role ) != value )
{
switch ( role )
{
case QgsQuickAttributeFormModel::RememberValue:
{
QStandardItem *item = itemFromIndex( index );
int fieldIndex = item->data( QgsQuickAttributeFormModel::FieldIndex ).toInt();
mAttributeModel->setData( mAttributeModel->index( fieldIndex ), value, QgsQuickAttributeModel::RememberAttribute );
item->setData( value, QgsQuickAttributeFormModel::RememberValue );
break;
}
case QgsQuickAttributeFormModel::AttributeValue:
{
QStandardItem *item = itemFromIndex( index );
int fieldIndex = item->data( QgsQuickAttributeFormModel::FieldIndex ).toInt();
bool changed = mAttributeModel->setData( mAttributeModel->index( fieldIndex ), value, QgsQuickAttributeModel::AttributeValue );
if ( changed )
{
item->setData( value, QgsQuickAttributeFormModel::AttributeValue );
emit dataChanged( index, index, QVector<int>() << role );
}
updateVisibility( fieldIndex );
return changed;
break;
}
}
}
return false;
}
QgsQuickAttributeModel *QgsQuickAttributeFormModelBase::attributeModel() const
{
return mAttributeModel;
}
void QgsQuickAttributeFormModelBase::setAttributeModel( QgsQuickAttributeModel *attributeModel )
{
if ( mAttributeModel == attributeModel )
return;
if ( mAttributeModel )
{
disconnect( mAttributeModel, &QgsQuickAttributeModel::layerChanged, this, &QgsQuickAttributeFormModelBase::onLayerChanged );
disconnect( mAttributeModel, &QgsQuickAttributeModel::featureChanged, this, &QgsQuickAttributeFormModelBase::onFeatureChanged );
disconnect( mAttributeModel, &QgsQuickAttributeModel::modelReset, this, &QgsQuickAttributeFormModelBase::onFeatureChanged );
}
mAttributeModel = attributeModel;
if ( mAttributeModel )
{
connect( mAttributeModel, &QgsQuickAttributeModel::layerChanged, this, &QgsQuickAttributeFormModelBase::onLayerChanged );
connect( mAttributeModel, &QgsQuickAttributeModel::featureChanged, this, &QgsQuickAttributeFormModelBase::onFeatureChanged );
connect( mAttributeModel, &QgsQuickAttributeModel::modelReset, this, &QgsQuickAttributeFormModelBase::onFeatureChanged );
}
emit attributeModelChanged();
}
void QgsQuickAttributeFormModelBase::onLayerChanged()
{
clear();
mLayer = mAttributeModel->featureLayerPair().layer();
mVisibilityExpressions.clear();
mConstraints.clear();
if ( mLayer )
{
QgsAttributeEditorContainer *root;
mTemporaryContainer = nullptr;
if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::TabLayout )
{
root = mLayer->editFormConfig().invisibleRootContainer();
}
else
{
root = generateRootContainer(); //#spellok
mTemporaryContainer.reset( root );
}
setHasTabs( !root->children().isEmpty() && QgsAttributeEditorElement::AeTypeContainer == root->children().first()->type() );
invisibleRootItem()->setColumnCount( 1 );
if ( mHasTabs )
{
for ( QgsAttributeEditorElement *element : root->children() )
{
if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
{
QgsAttributeEditorContainer *container = static_cast<QgsAttributeEditorContainer *>( element );
QStandardItem *item = new QStandardItem();
item->setData( element->name(), QgsQuickAttributeFormModel::Name );
item->setData( QStringLiteral( "container" ), QgsQuickAttributeFormModel::ElementType );
item->setData( true, QgsQuickAttributeFormModel::CurrentlyVisible );
invisibleRootItem()->appendRow( item );
if ( container->visibilityExpression().enabled() )
{
mVisibilityExpressions.append( qMakePair( container->visibilityExpression().data(), QVector<QStandardItem *>() << item ) );
}
QVector<QStandardItem *> dummy;
flatten( container, item, QString(), dummy );
}
}
}
else
{
QVector<QStandardItem *> dummy;
flatten( invisibleRootContainer(), invisibleRootItem(), QString(), dummy );
}
mExpressionContext = mLayer->createExpressionContext();
}
}
void QgsQuickAttributeFormModelBase::onFeatureChanged()
{
for ( int i = 0 ; i < invisibleRootItem()->rowCount(); ++i )
{
updateAttributeValue( invisibleRootItem()->child( i ) );
}
updateVisibility();
}
QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::generateRootContainer() const //#spellok
{
QgsAttributeEditorContainer *root = new QgsAttributeEditorContainer( QString(), nullptr );
QgsFields fields = mLayer->fields();
for ( int i = 0; i < fields.size(); ++i )
{
if ( fields.at( i ).editorWidgetSetup().type() != QStringLiteral( "Hidden" ) )
{
QgsAttributeEditorField *field = new QgsAttributeEditorField( fields.at( i ).name(), i, root );
root->addChildElement( field );
}
}
return root;
}
QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::invisibleRootContainer() const
{
return mTemporaryContainer ? mTemporaryContainer.get() : mLayer->editFormConfig().invisibleRootContainer();
}
void QgsQuickAttributeFormModelBase::updateAttributeValue( QStandardItem *item )
{
if ( item->data( QgsQuickAttributeFormModel::ElementType ) == QStringLiteral( "field" ) )
{
item->setData( mAttributeModel->featureLayerPair().feature().attribute( item->data( QgsQuickAttributeFormModel::FieldIndex ).toInt() ), QgsQuickAttributeFormModel::AttributeValue );
}
else
{
for ( int i = 0; i < item->rowCount(); ++i )
{
updateAttributeValue( item->child( i ) );
}
}
}
void QgsQuickAttributeFormModelBase::flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &parentVisibilityExpressions, QVector<QStandardItem *> &items )
{
for ( QgsAttributeEditorElement *element : container->children() )
{
switch ( element->type() )
{
case QgsAttributeEditorElement::AeTypeContainer:
{
QString visibilityExpression = parentVisibilityExpressions;
QgsAttributeEditorContainer *container = static_cast<QgsAttributeEditorContainer *>( element );
if ( container->visibilityExpression().enabled() )
{
if ( visibilityExpression.isNull() )
visibilityExpression = container->visibilityExpression().data().expression();
else
visibilityExpression += " AND " + container->visibilityExpression().data().expression();
}
QVector<QStandardItem *> newItems;
flatten( container, parent, visibilityExpression, newItems );
if ( !visibilityExpression.isEmpty() )
mVisibilityExpressions.append( qMakePair( QgsExpression( visibilityExpression ), newItems ) );
break;
}
case QgsAttributeEditorElement::AeTypeField:
{
QgsAttributeEditorField *editorField = static_cast<QgsAttributeEditorField *>( element );
int fieldIndex = editorField->idx();
if ( fieldIndex < 0 || fieldIndex >= mLayer->fields().size() )
continue;
QgsField field = mLayer->fields().at( fieldIndex );
QStandardItem *item = new QStandardItem();
item->setData( mLayer->attributeDisplayName( fieldIndex ), QgsQuickAttributeFormModel::Name );
item->setData( mAttributeModel->featureLayerPair().feature().attribute( fieldIndex ), QgsQuickAttributeFormModel::AttributeValue );
item->setData( !mLayer->editFormConfig().readOnly( fieldIndex ), QgsQuickAttributeFormModel::AttributeEditable );
QgsEditorWidgetSetup setup = mLayer->editorWidgetSetup( fieldIndex );
item->setData( setup.type(), QgsQuickAttributeFormModel::EditorWidget );
item->setData( setup.config(), QgsQuickAttributeFormModel::EditorWidgetConfig );
item->setData( mAttributeModel->rememberedAttributes().at( fieldIndex ) ? Qt::Checked : Qt::Unchecked, QgsQuickAttributeFormModel::RememberValue );
item->setData( mLayer->fields().at( fieldIndex ), QgsQuickAttributeFormModel::Field );
item->setData( QStringLiteral( "field" ), QgsQuickAttributeFormModel::ElementType );
item->setData( fieldIndex, QgsQuickAttributeFormModel::FieldIndex );
item->setData( container->isGroupBox() ? container->name() : QString(), QgsQuickAttributeFormModel::Group );
item->setData( true, QgsQuickAttributeFormModel::CurrentlyVisible );
item->setData( true, QgsQuickAttributeFormModel::ConstraintValid );
item->setData( field.constraints().constraintDescription(), QgsQuickAttributeFormModel::ConstraintDescription );
if ( !field.constraints().constraintExpression().isEmpty() )
{
mConstraints.insert( item, field.constraints().constraintExpression() );
}
items.append( item );
parent->appendRow( item );
break;
}
case QgsAttributeEditorElement::AeTypeRelation:
// todo
break;
case QgsAttributeEditorElement::AeTypeInvalid:
// todo
break;
}
}
}
void QgsQuickAttributeFormModelBase::updateVisibility( int fieldIndex )
{
QgsFields fields = mAttributeModel->featureLayerPair().feature().fields();
mExpressionContext.setFields( fields );
mExpressionContext.setFeature( mAttributeModel->featureLayerPair().feature() );
for ( const VisibilityExpression &it : mVisibilityExpressions )
{
if ( fieldIndex == -1 || it.first.referencedAttributeIndexes( fields ).contains( fieldIndex ) )
{
QgsExpression exp = it.first;
exp.prepare( &mExpressionContext );
bool visible = exp.evaluate( &mExpressionContext ).toInt();
for ( QStandardItem *item : it.second )
{
if ( item->data( QgsQuickAttributeFormModel::CurrentlyVisible ).toBool() != visible )
{
item->setData( visible, QgsQuickAttributeFormModel::CurrentlyVisible );
}
}
}
}
bool allConstraintsValid = true;
QMap<QStandardItem *, QgsExpression>::ConstIterator constraintIterator( mConstraints.constBegin() );
for ( ; constraintIterator != mConstraints.constEnd(); ++constraintIterator )
{
QStandardItem *item = constraintIterator.key();
QgsExpression exp = constraintIterator.value();
exp.prepare( &mExpressionContext );
bool constraintSatisfied = exp.evaluate( &mExpressionContext ).toBool();
if ( constraintSatisfied != item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() )
{
item->setData( constraintSatisfied, QgsQuickAttributeFormModel::ConstraintValid );
}
if ( !item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() )
{
allConstraintsValid = false;
}
}
setConstraintsValid( allConstraintsValid );
}
bool QgsQuickAttributeFormModelBase::constraintsValid() const
{
return mConstraintsValid;
}
QVariant QgsQuickAttributeFormModelBase::attribute( const QString &name ) const
{
if ( !mLayer )
return QVariant();
int idx = mLayer->fields().indexOf( name );
return mAttributeModel->featureLayerPair().feature().attribute( idx );
}
void QgsQuickAttributeFormModelBase::setConstraintsValid( bool constraintsValid )
{
if ( constraintsValid == mConstraintsValid )
return;
mConstraintsValid = constraintsValid;
emit constraintsValidChanged();
}
bool QgsQuickAttributeFormModelBase::hasTabs() const
{
return mHasTabs;
}
void QgsQuickAttributeFormModelBase::setHasTabs( bool hasTabs )
{
if ( hasTabs == mHasTabs )
return;
mHasTabs = hasTabs;
emit hasTabsChanged();
}
void QgsQuickAttributeFormModelBase::save()
{
mAttributeModel->save();
}
void QgsQuickAttributeFormModelBase::create()
{
mAttributeModel->create();
}
/// @endcond

View File

@ -0,0 +1,132 @@
/***************************************************************************
qgsquickattributemodelbase.h
--------------------------------------
Date : 16.8.2016
Copyright : (C) 2016 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSQUICKATTRIBUTEFORMMODELBASE_H
#define QGSQUICKATTRIBUTEFORMMODELBASE_H
/// @cond PRIVATE
//
// W A R N I N G
// -------------
//
// This file is not part of the QGIS API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
#include <QStandardItemModel>
#include <QStack>
#include "qgseditformconfig.h"
#include "qgsexpressioncontext.h"
#include "qgis_quick.h"
#include "qgsquickattributemodel.h"
class QVariant;
/**
* \ingroup quick
* This is an internal (implementation) class used as the source model for QgsQuickAttributeFormModel.
*
* \sa QgsQuickAttributeFormModel
*
* \since QGIS 3.4
*/
class QgsQuickAttributeFormModelBase : public QStandardItemModel
{
Q_OBJECT
//! Feature model with attributes
Q_PROPERTY( QgsQuickAttributeModel *attributeModel READ attributeModel WRITE setAttributeModel NOTIFY attributeModelChanged )
//! Whether use tabs layout
Q_PROPERTY( bool hasTabs READ hasTabs WRITE setHasTabs NOTIFY hasTabsChanged )
//! Returns true if all constraints defined on fields are satisfied with the current attribute values
Q_PROPERTY( bool constraintsValid READ constraintsValid NOTIFY constraintsValidChanged )
public:
//! Constructor
explicit QgsQuickAttributeFormModelBase( QObject *parent = nullptr );
//! Destructor
~QgsQuickAttributeFormModelBase() = default;
QHash<int, QByteArray> roleNames() const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;
//! \copydoc QgsQuickAttributeFormModelBase::attributeModel
QgsQuickAttributeModel *attributeModel() const;
//! \copydoc QgsQuickAttributeFormModelBase::attributeModel
void setAttributeModel( QgsQuickAttributeModel *attributeModel );
//! \copydoc QgsQuickAttributeFormModelBase::hasTabs
bool hasTabs() const;
//! \copydoc QgsQuickAttributeFormModelBase::hasTabs
void setHasTabs( bool hasTabs );
//! Saves changes
void save();
//! Creates a new feature
void create();
//! \copydoc QgsQuickAttributeFormModelBase::constraintsValid
bool constraintsValid() const;
/**
* Gets the value of attribute of the feature in the model
*
* \param name QString name of the wanted attribute
*/
QVariant attribute( const QString &name ) const;
signals:
//! \copydoc QgsQuickAttributeFormModelBase::attributeModel
void attributeModelChanged();
//! \copydoc QgsQuickAttributeFormModelBase::hasTabs
void hasTabsChanged();
//! \copydoc QgsQuickAttributeFormModelBase::constraintsValid
void constraintsValidChanged();
private slots:
void onFeatureChanged();
void onLayerChanged();
private:
QgsAttributeEditorContainer *generateRootContainer() const; //#spellok
QgsAttributeEditorContainer *invisibleRootContainer() const;
void updateAttributeValue( QStandardItem *item );
void flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &parentVisibilityExpressions, QVector<QStandardItem *> &items );
void updateVisibility( int fieldIndex = -1 );
void setConstraintsValid( bool constraintsValid );
QgsQuickAttributeModel *mAttributeModel = nullptr; // not owned
QgsVectorLayer *mLayer = nullptr; // not owned
std::unique_ptr<QgsAttributeEditorContainer> mTemporaryContainer;
bool mHasTabs = false;
typedef QPair<QgsExpression, QVector<QStandardItem *> > VisibilityExpression;
QList<VisibilityExpression> mVisibilityExpressions;
QMap<QStandardItem *, QgsExpression> mConstraints;
QgsExpressionContext mExpressionContext;
bool mConstraintsValid = false;
};
/// @endcond
#endif // QGSQUICKATTRIBUTEFORMMODELBASE_H

View File

@ -0,0 +1,327 @@
/***************************************************************************
qgsquickattributemodel.cpp
--------------------------------------
Date : 10.12.2014
Copyright : (C) 2014 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgis.h"
#include "qgsmessagelog.h"
#include "qgsvectorlayer.h"
#include "qgsquickattributemodel.h"
QgsQuickAttributeModel::QgsQuickAttributeModel( QObject *parent )
: QAbstractListModel( parent )
{
connect( this, &QgsQuickAttributeModel::modelReset, this, &QgsQuickAttributeModel::featureLayerPairChanged );
connect( this, &QgsQuickAttributeModel::featureChanged, this, &QgsQuickAttributeModel::featureLayerPairChanged );
connect( this, &QgsQuickAttributeModel::layerChanged, this, &QgsQuickAttributeModel::featureLayerPairChanged );
}
QgsQuickFeatureLayerPair QgsQuickAttributeModel::featureLayerPair() const
{
return mFeatureLayerPair;
}
void QgsQuickAttributeModel::setFeatureLayerPair( const QgsQuickFeatureLayerPair &pair )
{
setVectorLayer( pair.layer() );
setFeature( pair.feature() );
}
void QgsQuickAttributeModel::setVectorLayer( QgsVectorLayer *layer )
{
if ( mFeatureLayerPair.layer() == layer )
return;
beginResetModel();
mFeatureLayerPair = QgsQuickFeatureLayerPair( mFeatureLayerPair.feature(), layer );
if ( mFeatureLayerPair.layer() )
{
mRememberedAttributes.resize( mFeatureLayerPair.layer()->fields().size() );
mRememberedAttributes.fill( false );
}
else
{
mRememberedAttributes.clear();
}
endResetModel();
emit layerChanged();
}
void QgsQuickAttributeModel::setFeature( const QgsFeature &feature )
{
if ( mFeatureLayerPair.feature() == feature )
return;
beginResetModel();
mFeatureLayerPair = QgsQuickFeatureLayerPair( feature, mFeatureLayerPair.layer() );
endResetModel();
emit featureChanged();
}
QHash<int, QByteArray> QgsQuickAttributeModel::roleNames() const
{
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles[AttributeName] = QByteArrayLiteral( "AttributeName" );
roles[AttributeValue] = QByteArrayLiteral( "AttributeValue" );
roles[Field] = QByteArrayLiteral( "Field" );
roles[RememberAttribute] = QByteArrayLiteral( "RememberAttribute" );
return roles;
}
int QgsQuickAttributeModel::rowCount( const QModelIndex &parent ) const
{
if ( parent.isValid() )
return 0;
else
return mFeatureLayerPair.feature().attributes().count();
}
QVariant QgsQuickAttributeModel::data( const QModelIndex &index, int role ) const
{
switch ( role )
{
case AttributeName:
return mFeatureLayerPair.layer()->attributeDisplayName( index.row() );
break;
case AttributeValue:
return mFeatureLayerPair.feature().attribute( index.row() );
break;
case Field:
return mFeatureLayerPair.layer()->fields().at( index.row() );
break;
case RememberAttribute:
return mRememberedAttributes.at( index.row() );
break;
}
return QVariant();
}
bool QgsQuickAttributeModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( data( index, role ) == value )
return true;
switch ( role )
{
case AttributeValue:
{
QVariant val( value );
QgsField fld = mFeatureLayerPair.feature().fields().at( index.row() );
if ( !fld.convertCompatible( val ) )
{
QgsMessageLog::logMessage( tr( "Value \"%1\" %4 could not be converted to a compatible value for field %2(%3)." ).arg( value.toString(), fld.name(), fld.typeName(), value.isNull() ? "NULL" : "NOT NULL" ) );
return false;
}
bool success = mFeatureLayerPair.featureRef().setAttribute( index.row(), val );
if ( success )
emit dataChanged( index, index, QVector<int>() << role );
return success;
break;
}
case RememberAttribute:
{
mRememberedAttributes[ index.row() ] = value.toBool();
emit dataChanged( index, index, QVector<int>() << role );
break;
}
}
return false;
}
bool QgsQuickAttributeModel::save()
{
if ( !mFeatureLayerPair.layer() )
return false;
bool rv = true;
if ( !startEditing() )
{
rv = false;
}
QgsFeature feat = mFeatureLayerPair.feature();
if ( !mFeatureLayerPair.layer()->updateFeature( feat ) )
QgsMessageLog::logMessage( tr( "Cannot update feature" ),
QStringLiteral( "QgsQuick" ),
Qgis::Warning );
rv = commit();
if ( rv )
{
QgsFeature feat;
if ( mFeatureLayerPair.layer()->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureLayerPair.feature().id() ) ).nextFeature( feat ) )
setFeature( feat );
else
QgsMessageLog::logMessage( tr( "Feature %1 could not be fetched after commit" ).arg( mFeatureLayerPair.feature().id() ),
QStringLiteral( "QgsQuick" ),
Qgis::Warning );
}
return rv;
}
bool QgsQuickAttributeModel::deleteFeature()
{
if ( !mFeatureLayerPair.layer() )
return false;
bool rv = true;
if ( !startEditing() )
{
rv = false;
}
if ( !mFeatureLayerPair.layer()->deleteFeature( mFeatureLayerPair.feature().id() ) )
QgsMessageLog::logMessage( tr( "Cannot delete feature" ),
QStringLiteral( "QgsQuick" ),
Qgis::Warning );
rv = commit();
return rv;
}
void QgsQuickAttributeModel::reset()
{
if ( !mFeatureLayerPair.layer() )
return;
mFeatureLayerPair.layer()->rollBack();
}
bool QgsQuickAttributeModel::suppressFeatureForm() const
{
if ( !mFeatureLayerPair.layer() )
return false;
return mFeatureLayerPair.layer()->editFormConfig().suppress();
}
void QgsQuickAttributeModel::resetAttributes()
{
if ( !mFeatureLayerPair.layer() )
return;
QgsExpressionContext expressionContext = mFeatureLayerPair.layer()->createExpressionContext();
expressionContext.setFeature( mFeatureLayerPair.feature() );
QgsFields fields = mFeatureLayerPair.layer()->fields();
beginResetModel();
for ( int i = 0; i < fields.count(); ++i )
{
if ( !mRememberedAttributes.at( i ) )
{
if ( !fields.at( i ).defaultValueDefinition().expression().isEmpty() )
{
QgsExpression exp( fields.at( i ).defaultValueDefinition().expression() );
exp.prepare( &expressionContext );
if ( exp.hasParserError() )
QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has parser error: %3" ).arg(
mFeatureLayerPair.layer()->name(),
fields.at( i ).name(),
exp.parserErrorString() ),
QStringLiteral( "QgsQuick" ),
Qgis::Warning );
QVariant value = exp.evaluate( &expressionContext );
if ( exp.hasEvalError() )
QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has evaluation error: %3" ).arg(
mFeatureLayerPair.layer()->name(),
fields.at( i ).name(),
exp.evalErrorString() ),
QStringLiteral( "QgsQuick" ),
Qgis::Warning );
mFeatureLayerPair.feature().setAttribute( i, value );
}
else
{
mFeatureLayerPair.feature().setAttribute( i, QVariant() );
}
}
}
endResetModel();
}
void QgsQuickAttributeModel::create()
{
if ( !mFeatureLayerPair.layer() )
return;
startEditing();
QgsFeature feat = mFeatureLayerPair.feature();
if ( !mFeatureLayerPair.layer()->addFeature( feat ) )
{
QgsMessageLog::logMessage( tr( "Feature could not be added" ),
QStringLiteral( "QgsQuick" ),
Qgis::Critical );
}
commit();
}
bool QgsQuickAttributeModel::commit()
{
if ( !mFeatureLayerPair.layer()->commitChanges() )
{
QgsMessageLog::logMessage( tr( "Could not save changes. Rolling back." ),
QStringLiteral( "QgsQuick" ),
Qgis::Critical );
mFeatureLayerPair.layer()->rollBack();
return false;
}
else
{
return true;
}
}
bool QgsQuickAttributeModel::startEditing()
{
// Already an edit session active
if ( mFeatureLayerPair.layer()->editBuffer() )
return true;
if ( !mFeatureLayerPair.layer()->startEditing() )
{
QgsMessageLog::logMessage( tr( "Cannot start editing" ),
QStringLiteral( "QgsQuick" ),
Qgis::Warning );
return false;
}
else
{
return true;
}
}
QVector<bool> QgsQuickAttributeModel::rememberedAttributes() const
{
return mRememberedAttributes;
}

View File

@ -0,0 +1,153 @@
/***************************************************************************
QgsQuickAttributeModel.h
--------------------------------------
Date : 10.12.2014
Copyright : (C) 2014 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSQUICKATTRIBUTEMODEL_H
#define QGSQUICKATTRIBUTEMODEL_H
#include <QAbstractListModel>
#include <QVector>
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgis_quick.h"
#include "qgsquickfeaturelayerpair.h"
/**
* \ingroup quick
*
* Basic item model for attributes of QgsFeature associated
* from feature layer pair. Each attribute of the feature
* gets a row in the model. Also supports CRUD operations
* related to layer and feature pair.
*
* On top of the QgsFeature attibutes, support for "remember"
* attribute is added. Remember attribute is used for
* quick addition of the multiple features to the same layer.
* A new feature can use "remembered" attribute values from
* the last feature added.
*
*
* \note QML Type: AttributeModel
*
* \since QGIS 3.4
*/
class QUICK_EXPORT QgsQuickAttributeModel : public QAbstractListModel
{
Q_OBJECT
/**
* QgsQuickFeatureLayerPair for the model. Input for attributes model.
*/
Q_PROPERTY( QgsQuickFeatureLayerPair featureLayerPair READ featureLayerPair WRITE setFeatureLayerPair NOTIFY featureLayerPairChanged )
/**
* Feature roles enum.
*/
Q_ENUMS( FeatureRoles )
public:
//! Feature roles
enum FeatureRoles
{
AttributeName = Qt::UserRole + 1, //!< Attribute's display name (the original field name or a custom alias)
AttributeValue, //!< Value of the feature's attribute
Field, //!< Field definition (QgsField)
RememberAttribute //!< Remember attribute value for next feature
};
//! Creates a new feature attribute model
explicit QgsQuickAttributeModel( QObject *parent = nullptr );
/**
* Creates a new feature attribute model
* \param feat Feature set to model
* \param parent Parent object.
*/
explicit QgsQuickAttributeModel( const QgsFeature &feat, QObject *parent = nullptr );
QHash<int, QByteArray> roleNames() const override;
int rowCount( const QModelIndex &parent ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;
/**
* Commits the edit buffer of this layer.
* May change in the future to only commit the changes buffered in this model.
*
* @return Success of the operation.
*/
Q_INVOKABLE bool save();
/**
* Deletes the current feature from the layer and commit the changes.
* @return Success of the operation.
*/
Q_INVOKABLE bool deleteFeature();
/**
* Resets the feature to the original values and dismiss any buffered edits.
*/
Q_INVOKABLE void reset();
//! Adds feature from featureLayerPair to the layer
Q_INVOKABLE void create();
/**
* Suppress layer's QgsEditFormConfig
*
* \sa QgsEditFormConfig::suppress
*/
Q_INVOKABLE bool suppressFeatureForm() const;
//! Resets remembered attributes
Q_INVOKABLE virtual void resetAttributes();
//! Gets remembered attributes
QVector<bool> rememberedAttributes() const;
//!\copydoc QgsQuickAttributeModel::featureLayerPair
QgsQuickFeatureLayerPair featureLayerPair() const;
//!\copydoc QgsQuickAttributeModel::featureLayerPair
void setFeatureLayerPair( const QgsQuickFeatureLayerPair &pair );
public slots:
signals:
//! Feature or layer changed in feature layer pair
void featureLayerPairChanged();
//! Feature updated, layer is the same
void featureChanged();
//! Layer changed, feature is the same
void layerChanged();
protected:
//! Commits model changes
bool commit();
//! Starts editing model
bool startEditing();
QgsQuickFeatureLayerPair mFeatureLayerPair;
QVector<bool> mRememberedAttributes;
private:
void setFeature( const QgsFeature &feature );
void setVectorLayer( QgsVectorLayer *layer );
};
#endif // QGSQUICKATTRIBUTEMODEL_H

View File

@ -0,0 +1,183 @@
/***************************************************************************
qgsquicksubmodel.cpp
--------------------------------------
Date : 16.9.2016
Copyright : (C) 2016 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsquicksubmodel.h"
QgsQuickSubModel::QgsQuickSubModel( QObject *parent )
: QAbstractItemModel( parent )
{
}
QModelIndex QgsQuickSubModel::index( int row, int column, const QModelIndex &parent ) const
{
if ( !mModel )
return mRootIndex;
QModelIndex sourceIndex = mModel->index( row, column, parent.isValid() ? mapToSource( parent ) : static_cast<QModelIndex>( mRootIndex ) );
return mapFromSource( sourceIndex );
}
QModelIndex QgsQuickSubModel::parent( const QModelIndex &child ) const
{
if ( !mModel )
return mRootIndex;
QModelIndex idx = mModel->parent( child );
if ( idx == mRootIndex )
return QModelIndex();
else
return mapFromSource( idx );
}
int QgsQuickSubModel::rowCount( const QModelIndex &parent ) const
{
if ( !mModel )
return 0;
return mModel->rowCount( parent.isValid() ? mapToSource( parent ) : static_cast<QModelIndex>( mRootIndex ) );
}
int QgsQuickSubModel::columnCount( const QModelIndex &parent ) const
{
if ( !mModel )
return 0;
return mModel->columnCount( parent.isValid() ? mapToSource( parent ) : static_cast<QModelIndex>( mRootIndex ) );
}
QVariant QgsQuickSubModel::data( const QModelIndex &index, int role ) const
{
if ( !mModel )
return QVariant();
return mModel->data( mapToSource( index ), role );
}
bool QgsQuickSubModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( !mModel )
return false;
return mModel->setData( mapToSource( index ), value, role );
}
QHash<int, QByteArray> QgsQuickSubModel::roleNames() const
{
if ( !mModel )
return QHash<int, QByteArray>();
return mModel->roleNames();
}
QModelIndex QgsQuickSubModel::rootIndex() const
{
return mRootIndex;
}
void QgsQuickSubModel::setRootIndex( const QModelIndex &rootIndex )
{
if ( rootIndex == mRootIndex )
return;
beginResetModel();
mRootIndex = rootIndex;
endResetModel();
emit rootIndexChanged();
}
QAbstractItemModel *QgsQuickSubModel::model() const
{
return mModel;
}
void QgsQuickSubModel::setModel( QAbstractItemModel *model )
{
if ( model == mModel )
return;
if ( model )
{
connect( model, &QAbstractItemModel::rowsAboutToBeInserted, this, &QgsQuickSubModel::onRowsAboutToBeInserted );
connect( model, &QAbstractItemModel::rowsInserted, this, &QgsQuickSubModel::onRowsInserted );
connect( model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &QgsQuickSubModel::onRowsAboutToBeRemoved );
connect( model, &QAbstractItemModel::rowsRemoved, this, &QgsQuickSubModel::onRowsRemoved );
connect( model, &QAbstractItemModel::modelAboutToBeReset, this, &QgsQuickSubModel::onModelAboutToBeReset );
connect( model, &QAbstractItemModel::modelReset, this, &QAbstractItemModel::modelReset );
connect( model, &QAbstractItemModel::dataChanged, this, &QgsQuickSubModel::onDataChanged );
}
mModel = model;
emit modelChanged();
}
void QgsQuickSubModel::onRowsAboutToBeInserted( const QModelIndex &parent, int first, int last )
{
emit beginInsertRows( mapFromSource( parent ), first, last );
}
void QgsQuickSubModel::onRowsInserted( const QModelIndex &parent, int first, int last )
{
Q_UNUSED( parent )
Q_UNUSED( first )
Q_UNUSED( last )
emit endInsertRows();
}
void QgsQuickSubModel::onRowsAboutToBeRemoved( const QModelIndex &parent, int first, int last )
{
emit beginRemoveRows( mapFromSource( parent ), first, last );
}
void QgsQuickSubModel::onRowsRemoved( const QModelIndex &parent, int first, int last )
{
Q_UNUSED( parent )
Q_UNUSED( first )
Q_UNUSED( last )
emit endRemoveRows();
}
void QgsQuickSubModel::onModelAboutToBeReset()
{
mMappings.clear();
}
void QgsQuickSubModel::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
{
emit dataChanged( mapFromSource( topLeft ), mapFromSource( bottomRight ), roles );
}
QModelIndex QgsQuickSubModel::mapFromSource( const QModelIndex &sourceIndex ) const
{
if ( sourceIndex == mRootIndex || !sourceIndex.isValid() )
return QModelIndex();
if ( !mMappings.contains( sourceIndex.internalId() ) )
{
mMappings.insert( sourceIndex.internalId(), sourceIndex.parent() );
}
return createIndex( sourceIndex.row(), sourceIndex.column(), sourceIndex.internalId() );
}
QModelIndex QgsQuickSubModel::mapToSource( const QModelIndex &index ) const
{
if ( !index.isValid() )
return mRootIndex;
if ( !mModel )
return mRootIndex;
return mModel->index( index.row(), index.column(), mMappings.find( index.internalId() ).value() );
}

View File

@ -0,0 +1,93 @@
/***************************************************************************
qgsquicksubmodel.h
--------------------------------------
Date : 16.9.2016
Copyright : (C) 2016 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSQUICKSUBMODEL_H
#define QGSQUICKSUBMODEL_H
#include "qgis_quick.h"
#include "qgsquickattributeformmodel.h"
#include <QAbstractItemModel>
/**
* \ingroup quick
*
* Helper class for submodels (e.g. tabs within feature model).
*
* It uses internal mapping from internal indexes to indexes in the parent model.
*
* \note QML Type: SubModel
*
* \since QGIS 3.4
*/
class QUICK_EXPORT QgsQuickSubModel : public QAbstractItemModel
{
Q_OBJECT
//! Parent model (e.g QgsQuickAttributeFormModel)
Q_PROPERTY( QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged )
//! Root index of parent model
Q_PROPERTY( QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged )
public:
//! Creates new sub model
QgsQuickSubModel( QObject *parent = nullptr );
QModelIndex index( int row, int column, const QModelIndex &parent ) const override;
QModelIndex parent( const QModelIndex &child ) const override;
int rowCount( const QModelIndex &parent ) const override;
int columnCount( const QModelIndex &parent ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;
QHash<int, QByteArray> roleNames() const override;
//! \copydoc QgsQuickSubModel::model
QAbstractItemModel *model() const;
//! \copydoc QgsQuickSubModel::model
void setModel( QAbstractItemModel *model );
//! \copydoc QgsQuickSubModel::rootIndex
QModelIndex rootIndex() const;
//! \copydoc QgsQuickSubModel::rootIndex
void setRootIndex( const QModelIndex &rootIndex );
signals:
//! \copydoc QgsQuickSubModel::model
void modelChanged();
//! \copydoc QgsQuickSubModel::rootIndex
void rootIndexChanged();
private slots:
void onRowsAboutToBeInserted( const QModelIndex &parent, int first, int last );
void onRowsInserted( const QModelIndex &parent, int first, int last );
void onRowsAboutToBeRemoved( const QModelIndex &parent, int first, int last );
void onRowsRemoved( const QModelIndex &parent, int first, int last );
void onModelAboutToBeReset();
void onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>() );
private:
QModelIndex mapFromSource( const QModelIndex &sourceIndex ) const;
QModelIndex mapToSource( const QModelIndex &index ) const;
QAbstractItemModel *mModel = nullptr; // not owned
QPersistentModelIndex mRootIndex;
// Map internal id to parent index
mutable QHash<qintptr, QModelIndex> mMappings;
};
#endif // QGSQUICKSUBMODEL_H

View File

@ -0,0 +1,5 @@
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0zm0 0h24v24H0zm21 19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2" fill="none"/>
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M21 5v6.59l-3-3.01-4 4.01-4-4-4 4-3-3.01V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2zm-3 6.42l3 3.01V19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2v-6.58l3 2.99 4-4 4 4 4-3.99z"/>
</svg>

After

Width:  |  Height:  |  Size: 447 B

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_camera_alt_border_24dp.svg">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1016"
id="namedview10"
showgrid="true"
showguides="false"
inkscape:zoom="19.666667"
inkscape:cx="11.698998"
inkscape:cy="13.796445"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<path
inkscape:connector-curvature="0"
d="m 8.4,0 -2.196,2.4444444 -3.804,0 c -1.32,0 -2.4,1.1 -2.4,2.4444444 L 0,19.555556 C 0,20.9 1.08,22 2.4,22 l 19.2,0 C 22.92,22 24,20.9 24,19.555556 L 24,4.8888888 C 24,3.5444444 22.92,2.4444444 21.6,2.4444444 l -3.804,0 L 15.6,0 Z"
id="path6-7"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="ccssssssssccc" />
<circle
cx="12"
cy="12"
r="3.2"
id="circle4" />
<path
d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
id="path6"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path8" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,4 @@
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>

After

Width:  |  Height:  |  Size: 213 B

View File

@ -0,0 +1,4 @@
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 265 B

View File

@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 265 B

View File

@ -0,0 +1,5 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_photo_notavailable_white_48dp.svg">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1321"
inkscape:window-height="729"
id="namedview8"
showgrid="false"
inkscape:zoom="1.7383042"
inkscape:cx="34.371391"
inkscape:cy="-23.586747"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M0 0h48v48H0z"
fill="none"
id="path4" />
<path
d="M 36.449219 42 L 30.449219 36 L 10 36 L 17 27 L 22 33.009766 L 24.386719 29.9375 L 6 11.550781 L 6 38 C 6 40.21 7.79 42 10 42 L 36.449219 42 z "
id="path4149" />
<path
d="M 42 37.449219 L 42 10 C 42 7.79 40.21 6 38 6 L 10.550781 6 L 28.802734 24.251953 L 29 24 L 30.347656 25.796875 L 42 37.449219 z "
id="path4184" />
<path
inkscape:connector-curvature="0"
d="M 5,10.55 37.450002,43 l 2.55,-2.55 L 7.55,8.0000004 Z"
id="path6-3"
sodipodi:nodetypes="ccccc" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

View File

@ -1,5 +1,13 @@
<RCC>
<qresource prefix="/">
<file>ic_broken_image_black.svg</file>
<file>ic_camera_alt_border.svg</file>
<file>ic_check_black.svg</file>
<file>ic_clear_black.svg</file>
<file>ic_clear_white.svg</file>
<file>ic_delete_forever_white.svg</file>
<file>ic_navigation_black.svg</file>
<file>ic_photo_notavailable_white.svg</file>
<file>ic_save_white.svg</file>
</qresource>
</RCC>

View File

@ -10,8 +10,16 @@ SET(QGIS_QUICK_PLUGIN_SRC
)
SET(QGIS_QUICK_PLUGIN_RESOURCES
editor/qgsquickcheckbox.qml
editor/qgsquickdatetime.qml
editor/qgsquickexternalresource.qml
editor/qgsquicktextedit.qml
editor/qgsquickvaluemap.qml
qgsquickfeatureform.qml
qgsquickfeatureformstyling.qml
qgsquickmapcanvas.qml
qgsquickmessagelog.qml
qgsquickphotopanel.qml
qgsquickpositionmarker.qml
qgsquickscalebar.qml
qmldir

View File

@ -0,0 +1,49 @@
/***************************************************************************
qgsquickcheckbox.qml
--------------------------------------
Date : 2017
Copyright : (C) 2017 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QgisQuick 0.1 as QgsQuick
/**
* Checkbox for QGIS Attribute Form
* Requires various global properties set to function, see qgsquickfeatureform Loader section
* Do not use directly from Application QML
*/
Item {
signal valueChanged( var value, bool isNull )
height: childrenRect.height
anchors {
right: parent.right
left: parent.left
}
CheckBox {
property var currentValue: value
checked: value == config['CheckedState']
onCheckedChanged: {
valueChanged( checked ? config['CheckedState'] : config['UncheckedState'], false )
forceActiveFocus()
}
// Workaround to get a signal when the value has changed
onCurrentValueChanged: {
checked = currentValue == config['CheckedState']
}
}
}

View File

@ -0,0 +1,125 @@
/***************************************************************************
qgsquickdatetime.qml
--------------------------------------
Date : 2017
Copyright : (C) 2017 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4 as Controls1
import QgsQuick 0.1 as QgsQuick
/**
* Calendar for QGIS Attribute Form
* Requires various global properties set to function, see qgsquickfeatureform Loader section
* Do not use directly from Application QML
*/
Item {
signal valueChanged(var value, bool isNull)
height: childrenRect.height
anchors { right: parent.right; left: parent.left }
ColumnLayout {
id: main
property var currentValue: value
anchors { right: parent.right; left: parent.left }
Item {
anchors { right: parent.right; left: parent.left }
Layout.minimumHeight: 48 * QgsQuick.Utils.dp
Rectangle {
anchors.fill: parent
id: backgroundRect
border.color: "#17a81a"
border.width: 2
color: "#dddddd"
radius: 2
}
Label {
id: label
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
MouseArea {
anchors.fill: parent
onClicked: {
popup.open()
}
}
Image {
source: QgsQuick.Utils.getThemeIcon("ic_clear_black")
anchors.left: parent.right
visible: main.currentValue !== undefined && config['allow_null']
MouseArea {
anchors.fill: parent
onClicked: {
main.currentValue = undefined
}
}
}
}
}
Popup {
id: popup
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
parent: ApplicationWindow.overlay
ColumnLayout {
Controls1.Calendar {
id: calendar
selectedDate: main.currentValue
weekNumbersVisible: true
focus: false
onSelectedDateChanged: {
main.currentValue = selectedDate
}
}
RowLayout {
Button {
text: qsTr( "Ok" )
Layout.fillWidth: true
onClicked: popup.close()
}
}
}
}
onCurrentValueChanged: {
valueChanged(currentValue, main.currentValue === undefined)
if (main.currentValue === undefined)
{
label.text = qsTr('(no date)')
label.color = 'gray'
}
else
{
label.color = 'black'
label.text = new Date(value).toLocaleString(Qt.locale(), config['display_format'] )
}
}
}
}

View File

@ -0,0 +1,85 @@
/***************************************************************************
qgsquickexternalresource.qml
--------------------------------------
Date : 2017
Copyright : (C) 2017 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.5
import QtQuick.Controls 2.0
import QgsQuick 0.1 as QgsQuick
/**
* External Resource (Photo capture) for QGIS Attribute Form
* Requires various global properties set to function, see qgsquickfeatureform Loader section
* Do not use directly from Application QML
*/
Item {
signal valueChanged(var value, bool isNull)
property var image: image
id: fieldItem
anchors.left: parent.left
anchors.right: parent.right
height: Math.max(image.height, button.height)
QgsQuick.PhotoCapture {
id: photoCapturePanel
visible: false
height: window.height
width: window.width
edge: Qt.RightEdge
}
Image {
property var currentValue: value
id: image
width: 200 * QgsQuick.Utils.dp
autoTransform: true
fillMode: Image.PreserveAspectFit
Component.onCompleted: image.source = getSource()
function getSource() {
if (image.status === Image.Error)
return QgsQuick.Utils.getThemeIcon("ic_broken_image_black")
else if (image.currentValue && QgsQuick.Utils.fileExists(homePath + "/" + image.currentValue))
return homePath + "/" + image.currentValue
else
return QgsQuick.Utils.getThemeIcon("ic_photo_notavailable_white")
}
}
Button {
id: button
visible: fieldItem.enabled
width: 45 * QgsQuick.Utils.dp
height: 45 * QgsQuick.Utils.dp
anchors.right: parent.right
anchors.bottom: parent.bottom
onClicked: {
photoCapturePanel.visible = true
photoCapturePanel.targetDir = homePath
photoCapturePanel.fieldItem = fieldItem
}
background: Image {
source: QgsQuick.Utils.getThemeIcon("ic_camera_alt_border")
width: button.width
height: button.height
}
}
}

View File

@ -0,0 +1,88 @@
/***************************************************************************
qgsquicktextedit.qml
--------------------------------------
Date : 2017
Copyright : (C) 2017 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick 2.5
import QgsQuick 0.1 as QgsQuick
/**
* Text Edit for QGIS Attribute Form
* Requires various global properties set to function, see qgsquickfeatureform Loader section
* Do not use directly from Application QML
*/
Item {
signal valueChanged(var value, bool isNull)
height: childrenRect.height
TextField {
id: textField
height: textArea.height == 0 ? fontMetrics.height + 20 * QgsQuick.Utils.dp : 0
topPadding: 10 * QgsQuick.Utils.dp
bottomPadding: 10 * QgsQuick.Utils.dp
visible: height !== 0
anchors.left: parent.left
anchors.right: parent.right
font.pointSize: 14 * QgsQuick.Utils.dp
text: value || ''
inputMethodHints: field.isNumeric || widget == 'Range' ? field.precision === 0 ? Qt.ImhDigitsOnly : Qt.ImhFormattedNumbersOnly : Qt.ImhNone
// Make sure we do not input more characters than allowed for strings
states: [
State {
name: "limitedTextLengthState"
when: (!field.isNumeric) && (field.length > 0)
PropertyChanges {
target: textField
maximumLength: field.length
}
}
]
background: Rectangle {
y: textField.height - height - textField.bottomPadding / 2
implicitWidth: 120 * QgsQuick.Utils.dp
height: textField.activeFocus ? 2 * QgsQuick.Utils.dp : 1 * QgsQuick.Utils.dp
color: textField.activeFocus ? "#4CAF50" : "#C8E6C9"
}
onTextChanged: {
valueChanged( text, text == '' )
}
}
TextArea {
id: textArea
height: config['IsMultiline'] === true ? undefined : 0
visible: height !== 0
anchors.left: parent.left
anchors.right: parent.right
text: value || ''
textFormat: config['UseHtml'] ? TextEdit.RichText : TextEdit.PlainText
onEditingFinished: {
valueChanged( text, text == '' )
}
}
FontMetrics {
id: fontMetrics
font: textField.font
}
}

View File

@ -0,0 +1,109 @@
/***************************************************************************
qgsquickvaluemap.qml
--------------------------------------
Date : 2017
Copyright : (C) 2017 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
import QgsQuick 0.1 as QgsQuick
/**
* Value Map for QGIS Attribute Form
* Requires various global properties set to function, see qgsquickfeatureform Loader section
* Do not use directly from Application QML
*/
Item {
signal valueChanged(var value, bool isNull)
anchors {
left: parent.left
right: parent.right
rightMargin: 10 * QgsQuick.Utils.dp
}
height: childrenRect.height + 10 * QgsQuick.Utils.dp
ComboBox {
id: comboBox
property var reverseConfig: ({})
property var currentValue: value
anchors { left: parent.left; right: parent.right }
currentIndex: find(reverseConfig[value])
Component.onCompleted: {
model = Object.keys(config['map']);
for(var key in config['map']) {
reverseConfig[config['map'][key]] = key;
}
currentIndex = find(reverseConfig[value])
}
onCurrentTextChanged: {
valueChanged(config['map'][currentText], false)
}
// Workaround to get a signal when the value has changed
onCurrentValueChanged: {
currentIndex = find(reverseConfig[value])
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: mouse.accepted = false
onPressed: { forceActiveFocus(); mouse.accepted = false; }
onReleased: mouse.accepted = false;
onDoubleClicked: mouse.accepted = false;
onPositionChanged: mouse.accepted = false;
onPressAndHold: mouse.accepted = false;
}
// [hidpi fixes]
delegate: ItemDelegate {
width: comboBox.width
height: 36 * QgsQuick.Utils.dp
text: modelData
font.weight: comboBox.currentIndex === index ? Font.DemiBold : Font.Normal
font.pointSize: 12 * QgsQuick.Utils.dp
highlighted: comboBox.highlightedIndex == index
}
contentItem: Text {
height: 36 * QgsQuick.Utils.dp
text: comboBox.displayText
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Item {
implicitWidth: 120 * QgsQuick.Utils.dp
implicitHeight: 36 * QgsQuick.Utils.dp
Rectangle {
anchors.fill: parent
id: backgroundRect
border.color: comboBox.pressed ? "#17a81a" : "#21be2b"
border.width: comboBox.visualFocus ? 2 : 1
color: "#dddddd"
radius: 2
}
}
// [/hidpi fixes]
}
}

View File

@ -0,0 +1,487 @@
/***************************************************************************
qgsquickfeatureform.qml
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.6
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import QtQml.Models 2.2
import QtQml 2.2
// We use calendar in datetime widget that is not yet implemented in Controls 2.2
import QtQuick.Controls 1.4 as Controls1
import QgsQuick 0.1 as QgsQuick
Item {
/**
* When feature in the form is saved.
*/
signal saved
/**
* When the form is about to be closed by closeButton or deleting a feature.
*/
signal canceled
/**
* AttributeFormModel binded on a feature supporting auto-generated editor layouts and "tab" layout.
*/
property QgsQuick.AttributeFormModel model
/**
* Visibility of toolbar.
*/
property alias toolbarVisible: toolbar.visible
/**
* When adding a new feature, add checkbox to be able to save the same value for the next feature as default.
*/
property bool allowRememberAttribute: false
/**
* Active project.
*/
property QgsQuick.Project project
/**
* The function used for a component loader to find qml edit widget componets used in form.
*/
property var loadWidgetFn: QgsQuick.Utils.getEditorComponentSource
/**
* Icon path for save button.
*/
property string saveButtonIcon: QgsQuick.Utils.getThemeIcon( "ic_save_white" )
/**
* Icon path for delete button.
*/
property string deleteButtonIcon: QgsQuick.Utils.getThemeIcon( "ic_delete_forever_white" )
/**
* Icon path for close button
*/
property string closeButtonIcon: QgsQuick.Utils.getThemeIcon( "ic_clear_white" )
/**
* Predefined form styling
*/
property FeatureFormStyling style: FeatureFormStyling {}
id: form
states: [
State {
name: "ReadOnly"
},
State {
name: "Edit"
},
State {
name: "Add"
}
]
function reset() {
master.reset()
}
function save() {
parent.focus = true
if ( form.state === "Add" ) {
model.create()
state = "Edit"
}
else
{
model.save()
}
saved()
}
/**
* This is a relay to forward private signals to internal components.
*/
QtObject {
id: master
/**
* This signal is emitted whenever the state of Flickables and TabBars should
* be restored.
*/
signal reset
}
Item {
id: container
clip: true
anchors {
top: toolbar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
Flickable {
id: flickable
anchors {
left: parent.left
right: parent.right
}
height: tabRow.height
flickableDirection: Flickable.HorizontalFlick
contentWidth: tabRow.width
// Tabs
TabBar {
id: tabRow
visible: model.hasTabs
height: form.style.tabs.height
Connections {
target: master
onReset: tabRow.currentIndex = 0
}
Connections {
target: swipeView
onCurrentIndexChanged: tabRow.currentIndex = swipeView.currentIndex
}
Repeater {
model: form.model
TabButton {
id: tabButton
text: Name
leftPadding: 8 * QgsQuick.Utils.dp
rightPadding: 8 * QgsQuick.Utils.dp
width: contentItem.width + leftPadding + rightPadding
height: form.style.tabs.height
contentItem: Text {
// Make sure the width is derived from the text so we can get wider
// than the parent item and the Flickable is useful
width: paintedWidth
text: tabButton.text
color: !tabButton.enabled ? form.style.tabs.disabledColor : tabButton.down ||
tabButton.checked ? form.style.tabs.activeColor : form.style.tabs.normalColor
font.weight: tabButton.checked ? Font.DemiBold : Font.Normal
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
}
}
SwipeView {
id: swipeView
currentIndex: tabRow.currentIndex
anchors {
top: flickable.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
}
Repeater {
/**
* One page per tab in tabbed forms, 1 page in auto forms
*/
model: form.model.hasTabs ? form.model : 1
Item {
id: formPage
property int currentIndex: index
/**
* The main form content area
*/
Rectangle {
anchors.fill: parent
color: form.style.backgroundColor
opacity: form.style.backgroundOpacity
}
ListView {
id: content
anchors.fill: parent
clip: true
section.property: "Group"
section.labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels
section.delegate: Component {
// section header: group box name
Rectangle {
width: parent.width
height: section === "" ? 0 : form.style.group.height
color: form.style.group.backgroundColor
Text {
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
font.bold: true
text: section
}
}
}
Connections {
target: master
onReset: content.contentY = 0
}
model: QgsQuick.SubModel {
id: contentModel
model: form.model
rootIndex: form.model.hasTabs ? form.model.index(currentIndex, 0) : undefined
}
delegate: fieldItem
}
}
}
}
}
/**
* A field editor
*/
Component {
id: fieldItem
Item {
id: fieldContainer
visible: Type === 'field'
height: childrenRect.height
anchors {
left: parent.left
right: parent.right
leftMargin: 12 * QgsQuick.Utils.dp
}
Label {
id: fieldLabel
text: qsTr(Name) || ''
font.bold: true
color: ConstraintValid ? form.style.constraint.validColor : form.style.constraint.invalidColor
}
Label {
id: constraintDescriptionLabel
anchors {
left: parent.left
right: parent.right
top: fieldLabel.bottom
}
text: qsTr(ConstraintDescription)
height: ConstraintValid ? 0 : undefined
visible: !ConstraintValid
color: form.style.constraint.descriptionColor
}
Item {
id: placeholder
height: childrenRect.height
anchors { left: parent.left; right: rememberCheckbox.left; top: constraintDescriptionLabel.bottom }
Loader {
id: attributeEditorLoader
height: childrenRect.height
anchors { left: parent.left; right: parent.right }
enabled: form.state !== "ReadOnly" && !!AttributeEditable
property var value: AttributeValue
property var config: EditorWidgetConfig
property var widget: EditorWidget
property var field: Field
property var constraintValid: ConstraintValid
property var homePath: form.project ? form.project.homePath : ""
active: widget !== 'Hidden'
source: form.loadWidgetFn(widget.toLowerCase())
}
Connections {
target: attributeEditorLoader.item
onValueChanged: {
AttributeValue = isNull ? undefined : value
}
}
}
CheckBox {
id: rememberCheckbox
checked: RememberValue ? true : false
visible: form.allowRememberAttribute && form.state === "Add" && EditorWidget !== "Hidden"
width: visible ? undefined : 0
anchors { right: parent.right; top: fieldLabel.bottom }
onCheckedChanged: {
RememberValue = checked
}
}
}
}
Connections {
target: Qt.inputMethod
onVisibleChanged: {
Qt.inputMethod.commit()
}
}
/** The form toolbar **/
Item {
id: toolbar
height: visible ? 48 * QgsQuick.Utils.dp : 0
visible: form.state === 'Add'
anchors {
top: parent.top
left: parent.left
right: parent.right
}
RowLayout {
anchors.fill: parent
Layout.margins: 0
ToolButton {
id: saveButton
Layout.preferredWidth: form.style.toolbutton.size
Layout.preferredHeight: form.style.toolbutton.size
visible: form.state !== "ReadOnly"
contentItem: Image {
source: form.saveButtonIcon
sourceSize: Qt.size(width, height)
}
background: Rectangle {
color: model.constraintsValid ? form.style.toolbutton.backgroundColor : form.style.toolbutton.backgroundColorInvalid
}
enabled: model.constraintsValid
onClicked: {
form.save()
}
}
ToolButton {
id: deleteButton
Layout.preferredWidth: form.style.toolbutton.size
Layout.preferredHeight: form.style.toolbutton.size
visible: form.state === "Edit"
contentItem: Image {
source: form.deleteButtonIcon
sourceSize: Qt.size(width, height)
}
background: Rectangle {
color: form.style.toolbutton.backgroundColor
}
onClicked: deleteDialog.visible = true
}
Label {
id: titleLabel
text:
{
var currentLayer = model.attributeModel.featureLayerPair.layer
var layerName = 'N/A'
if (!!currentLayer)
layerName = currentLayer.name
if ( form.state === 'Add' )
qsTr( 'Add feature on <i>%1</i>' ).arg(layerName )
else if ( form.state === 'Edit' )
qsTr( 'Edit feature on <i>%1</i>' ).arg(layerName)
else
qsTr( 'View feature on <i>%1</i>' ).arg(layerName)
}
font.bold: true
font.pointSize: 16
elide: Label.ElideRight
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
Layout.fillWidth: true
color: "white"
}
ToolButton {
id: closeButton
anchors.right: parent.right
Layout.preferredWidth: form.style.toolbutton.size
Layout.preferredHeight: form.style.toolbutton.size
contentItem: Image {
source: form.closeButtonIcon
sourceSize: Qt.size(width, height)
}
background: Rectangle {
color: form.style.toolbutton.backgroundColor
}
onClicked: {
Qt.inputMethod.hide()
form.canceled()
}
}
}
}
MessageDialog {
id: deleteDialog
visible: false
title: qsTr( "Delete feature" )
text: qsTr( "Really delete this feature?" )
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok | StandardButton.Cancel
onAccepted: {
model.attributeModel.deleteFeature()
visible = false
form.canceled()
}
onRejected: {
visible = false
}
}
}

View File

@ -0,0 +1,47 @@
/***************************************************************************
qgsquickfeatureformstyling.qml
--------------------------------------
Date : January 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.0
import QgsQuick 0.1 as QgsQuick
QtObject {
property color backgroundColor: "white"
property real backgroundOpacity: 1
property QtObject group: QtObject {
property color backgroundColor: "lightGray"
property real height: 30 * QgsQuick.Utils.dp
}
property QtObject tabs: QtObject {
property color normalColor: "#4CAF50"
property color activeColor: "#1B5E20"
property color disabledColor: "#999999"
property real height: 48 * QgsQuick.Utils.dp
}
property QtObject constraint: QtObject {
property color validColor: "black"
property color invalidColor: "#c0392b"
property color descriptionColor: "#e67e22"
}
property QtObject toolbutton: QtObject {
property color backgroundColor: "transparent"
property color backgroundColorInvalid: "#bdc3c7"
property real size: 80 * QgsQuick.Utils.dp
}
}

View File

@ -0,0 +1,222 @@
/***************************************************************************
qgsquickphotopanel.qml
--------------------------------------
Date : Dec 2017
Copyright : (C) 2017 by Viktor Sklencar
Email : vsklencar at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.3
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.2
import QtQml 2.2
import QtMultimedia 5.8
import QtGraphicalEffects 1.0
import QgsQuick 0.1 as QgsQuick
Drawer {
property var targetDir
property var lastPhotoName
property int iconSize: photoPanel.width/20
property var fieldItem
property color bgColor: "white"
property real bgOpacity: 0.8
property color borderColor: "black"
// icons:
property var captureButtonIcon: QgsQuick.Utils.getThemeIcon("ic_camera_alt_border")
property var okButtonIcon: QgsQuick.Utils.getThemeIcon("ic_check_black")
property var cancelButtonIcon: QgsQuick.Utils.getThemeIcon("ic_clear_black")
id: photoPanel
visible: false
modal: true
interactive: true
dragMargin: 0 // prevents opening the drawer by dragging.
background: Rectangle {
color: photoPanel.bgColor
opacity: photoPanel.bgOpacity
}
onVisibleChanged: {
if (visible) {
camera.setCameraState(Camera.ActiveState)
camera.start()
} else {
camera.stop()
photoPreview.visible = false
}
}
// PhotoCapture item
Item {
property bool saveImage: false
id: captureItem
width: window.width
height: window.height
Component.onDestruction: {
if (!captureItem && camera.imageCapture.capturedImagePath != ""){
captureItem.saveImage = false
QgsQuick.Utils.remove(camera.imageCapture.capturedImagePath)
}
captureItem.saveImage = false
}
Camera {
id: camera
cameraState: Camera.UnloadedState
imageCapture {
onImageCaptured: {
// Show the preview in an Image
photoPreview.source = preview
}
}
}
// Flipped VideoOutput on android - known ButtonGroup
// https://bugreports.qt.io/browse/QTBUG-64764
VideoOutput {
id: videoOutput
source: camera
focus : visible // to receive focus and capture key events when visible
anchors.fill: parent
autoOrientation: true
Rectangle {
id: captureButton
property int borderWidth: 10 * QgsQuick.Utils.dp
width: parent.width/20
height: parent.width/20
color: photoPanel.bgColor
border.color: photoPanel.borderColor
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
border.width: borderWidth
radius: width*0.5
antialiasing: true
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (targetDir !== "") {
camera.imageCapture.captureToLocation(photoPanel.targetDir);
} else {
// saved to default location - TODO handle this case
camera.imageCapture.capture();
}
photoPreview.visible = true;
}
}
Image {
id: captureButtonImage
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
sourceSize.height: captureButton.height/2
height: captureButton.height/2
source: photoPanel.captureButtonIcon
}
}
Image {
id: photoPreview
width: parent.width
height: parent.height
fillMode: Image.PreserveAspectFit
// Cancel button
Rectangle {
id: cancelButton
visible: camera.imageCapture.capturedImagePath != ""
property int borderWidth: 10 * QgsQuick.Utils.dp
width: parent.width/20
height: parent.width/20
color: photoPanel.bgColor
border.color: photoPanel.borderColor
anchors.right: parent.right
anchors.top: confirmButton.bottom
border.width: borderWidth
radius: width*0.5
antialiasing: true
MouseArea {
anchors.fill: parent
onClicked: {
captureItem.saveImage = false
photoPreview.visible = false
if (camera.imageCapture.capturedImagePath != "") {
QgsQuick.Utils.remove(camera.imageCapture.capturedImagePath)
}
}
}
Image {
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
sourceSize.height: captureButton.height/2
height: captureButton.height/2
source: photoPanel.cancelButtonIcon
}
}
// OK button
Rectangle {
id: confirmButton
visible: camera.imageCapture.capturedImagePath != ""
property int borderWidth: 10 * QgsQuick.Utils.dp
width: parent.width/20
height: parent.width/20
color: photoPanel.bgColor
border.color: photoPanel.borderColor
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
border.width: borderWidth
radius: width*0.5
antialiasing: true
MouseArea {
anchors.fill: parent
onClicked: {
captureItem.saveImage = true
photoPanel.visible = false
photoPanel.lastPhotoName = QgsQuick.Utils.getFileName(camera.imageCapture.capturedImagePath)
if (photoPanel.lastPhotoName !== "") {
fieldItem.image.source = photoPanel.targetDir + "/" + photoPanel.lastPhotoName
fieldItem.valueChanged(photoPanel.lastPhotoName, photoPanel.lastPhotoName === "" || photoPanel.lastPhotoName === null)
}
}
}
Image {
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
sourceSize.height: captureButton.height/2
height: captureButton.height/2
source: photoPanel.okButtonIcon
}
}
}
}
}
}

View File

@ -30,6 +30,9 @@
#include "qgsvectorlayer.h"
#include "qgsunittypes.h"
#include "qgsquickattributeformmodel.h"
#include "qgsquickattributeformmodelbase.h"
#include "qgsquickattributemodel.h"
#include "qgsquickfeaturehighlight.h"
#include "qgsquickcoordinatetransformer.h"
#include "qgsquickidentifykit.h"
@ -41,6 +44,7 @@
#include "qgsquickplugin.h"
#include "qgsquickpositionkit.h"
#include "qgsquickscalebarkit.h"
#include "qgsquicksubmodel.h"
#include "qgsquickutils.h"
static QObject *_utilsProvider( QQmlEngine *engine, QJSEngine *scriptEngine )
@ -69,6 +73,8 @@ void QgsQuickPlugin::registerTypes( const char *uri )
qmlRegisterUncreatableType< QgsUnitTypes >( uri, 0, 1, "QgsUnitTypes", "Only enums from QgsUnitTypes can be used" );
qmlRegisterType< QgsProject >( uri, 0, 1, "Project" );
qmlRegisterType< QgsQuickAttributeFormModel >( uri, 0, 1, "AttributeFormModel" );
qmlRegisterType< QgsQuickAttributeModel >( uri, 0, 1, "AttributeModel" );
qmlRegisterType< QgsQuickFeatureHighlight >( uri, 0, 1, "FeatureHighlight" );
qmlRegisterType< QgsQuickCoordinateTransformer >( uri, 0, 1, "CoordinateTransformer" );
qmlRegisterType< QgsQuickIdentifyKit >( uri, 0, 1, "IdentifyKit" );
@ -78,6 +84,7 @@ void QgsQuickPlugin::registerTypes( const char *uri )
qmlRegisterType< QgsQuickMessageLogModel >( uri, 0, 1, "MessageLogModel" );
qmlRegisterType< QgsQuickPositionKit >( uri, 0, 1, "PositionKit" );
qmlRegisterType< QgsQuickScaleBarKit >( uri, 0, 1, "ScaleBarKit" );
qmlRegisterType< QgsQuickSubModel >( uri, 0, 1, "SubModel" );
qmlRegisterType< QgsVectorLayer >( uri, 0, 1, "VectorLayer" );
qmlRegisterSingletonType< QgsQuickUtils >( uri, 0, 1, "Utils", _utilsProvider );

View File

@ -14,8 +14,11 @@ module QgsQuick
plugin qgis_quick_plugin
MapCanvas 0.1 qgsquickmapcanvas.qml
FeatureForm 0.1 qgsquickfeatureform.qml
FeatureFormStyling 0.1 qgsquickfeatureformstyling.qml
PositionMarker 0.1 qgsquickpositionmarker.qml
ScaleBar 0.1 qgsquickscalebar.qml
PhotoCapture 0.1 qgsquickphotopanel.qml
MessageLog 0.1 qgsquickmessagelog.qml
typeinfo qgsquick.qmltypes

View File

@ -30,7 +30,7 @@
* Helper class for transform of coordinates (QgsPoint) to a different coordinate reference system.
*
* It requires connection of transformation context from mapSettings, source position and source CRS to
* calculate projected position in desired destination CRS
* calculate projected position in desired destination CRS.
*
* \note QML Type: CoordinateTransformer
*

View File

@ -27,7 +27,7 @@ class QgsQuickMapSettings;
/**
* \ingroup quick
*
* Creates map highlights for a geometry provided by a FeatureModel.
* Creates map highlights for a geometry provided by a AttributeModel.
*
* The highlights are compatible with the QtQuick scene graph and
* can be direcly shown on map canvas

View File

@ -36,6 +36,11 @@ QgsFeature QgsQuickFeatureLayerPair::feature() const
return mFeature;
}
QgsFeature &QgsQuickFeatureLayerPair::featureRef()
{
return mFeature;
}
bool QgsQuickFeatureLayerPair::isValid() const
{
return ( mLayer && mFeature.isValid() && hasValidGeometry() );

View File

@ -82,6 +82,9 @@ class QUICK_EXPORT QgsQuickFeatureLayerPair
//! \copydoc QgsQuickFeatureLayerPair::feature
QgsFeature feature() const;
//! \copydoc QgsQuickFeatureLayerPair::feature
QgsFeature &featureRef();
//! \copydoc QgsQuickFeatureLayerPair::valid
bool isValid() const;

View File

@ -44,12 +44,12 @@ class QUICK_EXPORT QgsQuickMapTransform : public QQuickTransform
Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged )
public:
//! create new map transform
//! Creates a new map transform
QgsQuickMapTransform() = default;
~QgsQuickMapTransform() = default;
/**
* Apply transformation based on current map settings to a matrix.
* Applies transformation based on current map settings to a matrix.
*
* Also optimize resulting matrix after transformation
* \param matrix Matrix to be transformed
@ -70,7 +70,7 @@ class QUICK_EXPORT QgsQuickMapTransform : public QQuickTransform
void updateMatrix();
private:
QgsQuickMapSettings *mMapSettings = nullptr;
QgsQuickMapSettings *mMapSettings = nullptr; // not owned
QMatrix4x4 mMatrix;
};

View File

@ -21,6 +21,7 @@
#include "qgsdistancearea.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
#include "qgsfeature.h"
#include "qgsquickfeaturelayerpair.h"
#include "qgsquickmapsettings.h"
@ -84,6 +85,20 @@ double QgsQuickUtils::screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int
return mDistanceArea.measureLine( p1, p2 );
}
bool QgsQuickUtils::fileExists( QString path )
{
QFileInfo check_file( path );
// check if file exists and if yes: Is it really a file and no directory?
return ( check_file.exists() && check_file.isFile() );
}
QString QgsQuickUtils::getFileName( QString path )
{
QFileInfo fileInfo( path );
QString filename( fileInfo.fileName() );
return filename;
}
void QgsQuickUtils::logMessage( const QString &message, const QString &tag, Qgis::MessageLevel level )
{
QgsMessageLog::logMessage( message, tag, level );
@ -101,6 +116,25 @@ const QUrl QgsQuickUtils::getThemeIcon( const QString &name ) const
return QUrl( path );
}
const QUrl QgsQuickUtils::getEditorComponentSource( const QString &widgetName )
{
QString path( "qgsquick%1.qml" );
QStringList supportedWidgets = { "textedit",
"valuemap",
"checkbox",
"externalresource",
"datetime"
};
if ( supportedWidgets.contains( widgetName ) )
{
return QUrl( path.arg( widgetName ) );
}
else
{
return QUrl( path.arg( "textedit" ) );
}
}
QString QgsQuickUtils::formatPoint(
const QgsPoint &point,
QgsCoordinateFormatter::Format format,

View File

@ -19,6 +19,7 @@
#include <QObject>
#include <QString>
#include <QUrl>
#include <QtPositioning/QGeoCoordinate>
#include <limits>
@ -31,9 +32,10 @@
#include "qgsquickmapsettings.h"
#include "qgsquickfeaturelayerpair.h"
#include "qgis_quick.h"
#include "qgsfeature.h"
#include "qgscoordinateformatter.h"
class QgsFeature;
class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
@ -115,7 +117,21 @@ class QUICK_EXPORT QgsQuickUtils: public QObject
*/
Q_INVOKABLE static double screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels );
//! Log message in QgsMessageLog
/**
* Returns whether file on path exists
* \since QGIS 3.4
*/
Q_INVOKABLE bool fileExists( QString path );
/**
* Extracts filename from path
* \since QGIS 3.4
*/
Q_INVOKABLE QString getFileName( QString path );
/**
* Log message in QgsMessageLog
*/
Q_INVOKABLE void logMessage( const QString &message,
const QString &tag = QString( "QgsQuick" ),
Qgis::MessageLevel level = Qgis::Warning );
@ -136,6 +152,15 @@ class QUICK_EXPORT QgsQuickUtils: public QObject
*/
Q_INVOKABLE const QUrl getThemeIcon( const QString &name ) const;
/**
* Returns url to field editor component for a feature form.
* If the widgetName does not match any supported widget, text edit is returned.
* \param widgetName name of the attribute field widget
*
* \since QGIS 3.4
*/
Q_INVOKABLE const QUrl getEditorComponentSource( const QString &widgetName );
/**
* \copydoc QgsCoordinateFormatter::format()
*

View File

@ -7,6 +7,7 @@ SET(QGIS_QUICK_APP_SRCS
SET(QGIS_QUICK_APP_QMLS
main.qml
FeaturePanel.qml
)
INCLUDE_DIRECTORIES(

View File

@ -0,0 +1,73 @@
/***************************************************************************
FeaturePanel.qml
----------------
Date : Nov 2017
Copyright : (C) 2017 by Peter Petrik
Email : zilolv at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QgsQuick 0.1 as QgsQuick
Drawer {
property var mapSettings
property var project
property alias state: featureForm.state
property alias feature: attributeModel.featureLayerPair
property alias currentAttributeModel: attributeModel
id: featurePanel
visible: false
modal: true
interactive: true
dragMargin: 0 // prevents opening the drawer by dragging.
background: Rectangle {
color: "white"
opacity: 0.5
}
function show_panel(feature, state) {
featurePanel.feature = feature
featurePanel.state = state
featurePanel.visible = true
}
QgsQuick.FeatureForm {
id: featureForm
// using anchors here is not working well as
width: featurePanel.width
height: featurePanel.height
model: QgsQuick.AttributeFormModel {
attributeModel: QgsQuick.AttributeModel {
id: attributeModel
}
}
project: featurePanel.project
toolbarVisible: true
onSaved: {
featurePanel.visible = false
}
onCanceled:
{
featurePanel.visible = false
}
}
}

View File

@ -1,6 +1,6 @@
/***************************************************************************
main.qml
--------------------------------------
--------
Date : Nov 2017
Copyright : (C) 2017 by Peter Petrik
Email : zilolv at gmail dot com
@ -21,7 +21,15 @@ ApplicationWindow {
id: window
visible: true
visibility: "Maximized"
title: qsTr("QGIS Quick Test App")
title: "QGIS Quick Test App"
// Some info
Button {
id: logbutton
text: "Log"
onClicked: logPanel.visible = true
z: 1
}
QgsQuick.MapCanvas {
id: mapCanvas
@ -38,9 +46,12 @@ ApplicationWindow {
}
onClicked: {
var screenPoint = Qt.point(mouse.x, mouse.y)
var screenPoint = Qt.point( mouse.x, mouse.y );
var res = identifyKit.identifyOne(screenPoint);
highlight.featureLayerPair = res
if (res.valid)
featurePanel.show_panel(res, "Edit" )
}
}
@ -55,11 +66,11 @@ ApplicationWindow {
/** Message Log */
Drawer {
id: logPanel
visible: true
visible: false
modal: true
interactive: true
height: window.height
width: QgsQuick.Utils.dp * 700
width: 700 * QgsQuick.Utils.dp
edge: Qt.RightEdge
z: 2 // make sure items from here are on top of the Z-order
@ -72,7 +83,6 @@ ApplicationWindow {
width: parent.width
height: parent.height
model: QgsQuick.MessageLogModel {}
visible: true
}
}
@ -108,6 +118,7 @@ ApplicationWindow {
QgsQuick.PositionMarker {
id: positionMarker
positionKit: positionKit
z: 2
}
Label {
@ -151,4 +162,14 @@ ApplicationWindow {
color: "steelblue"
z: 1
}
FeaturePanel {
id: featurePanel
height: window.height
width: 700 * QgsQuick.Utils.dp
edge: Qt.RightEdge
mapSettings: mapCanvas.mapSettings
project: __project
visible: false
}
}

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>FeaturePanel.qml</file>
</qresource>
</RCC>

View File

@ -1,5 +1,5 @@
/***************************************************************************
testqgsquickidentifykit.cpp.cpp
testqgsquickidentifykit.cpp
--------------------------------------
Date : May 2018
Copyright : (C) 2018 by Viktor Sklencar

View File

@ -40,6 +40,9 @@ class TestQgsQuickUtils: public QObject
void transformedPoint();
void formatPoint();
void formatDistance();
void loadIcon();
void fileExists();
void loadQmlComponent();
private:
QgsQuickUtils utils;
@ -136,5 +139,30 @@ void TestQgsQuickUtils::formatDistance()
QVERIFY( dist2str == "1.2 NM" );
}
void TestQgsQuickUtils::loadIcon()
{
QUrl url = utils.getThemeIcon( "ic_save_white" );
Q_ASSERT( url.toString() == QStringLiteral( "qrc:/ic_save_white.svg" ) );
QString fileName = utils.getFileName( url.toString() );
Q_ASSERT( fileName == QStringLiteral( "ic_save_white.svg" ) );
}
void TestQgsQuickUtils::fileExists()
{
QString path = QStringLiteral( TEST_DATA_DIR ) + "/quickapp_project.qgs";
Q_ASSERT( utils.fileExists( path ) );
}
void TestQgsQuickUtils::loadQmlComponent()
{
QUrl dummy = utils.getEditorComponentSource( "dummy" );
Q_ASSERT( dummy.path() == QString( "qgsquicktextedit.qml" ) );
QUrl valuemap = utils.getEditorComponentSource( "valuemap" );
Q_ASSERT( valuemap.path() == QString( "qgsquickvaluemap.qml" ) );
}
QGSTEST_MAIN( TestQgsQuickUtils )
#include "testqgsquickutils.moc"

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,6 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis projectname="" version="3.1.0-Master">
<homePath path=""/>
<title></title>
<autotransaction active="0"/>
<evaluateDefaultValues active="0"/>
@ -18,13 +19,13 @@
</projectCrs>
<layer-tree-group>
<customproperties/>
<layer-tree-layer name="Planes" id="points20151123133104693" expanded="1" checked="Qt::Checked" source="./points.shp" providerKey="ogr">
<layer-tree-layer expanded="1" name="Planes" id="points20151123133104693" providerKey="ogr" checked="Qt::Checked" source="./points.shp">
<customproperties/>
</layer-tree-layer>
<layer-tree-layer name="Land" id="polys20151123133114244" expanded="1" checked="Qt::Checked" source="./polys.shp" providerKey="ogr">
<layer-tree-layer expanded="1" name="Land" id="polys20151123133114244" providerKey="ogr" checked="Qt::Checked" source="./polys.shp">
<customproperties/>
</layer-tree-layer>
<layer-tree-layer name="Roads" id="lines20151123133101198" expanded="1" checked="Qt::Checked" source="./lines.shp" providerKey="ogr">
<layer-tree-layer expanded="1" name="Roads" id="lines20151123133101198" providerKey="ogr" checked="Qt::Checked" source="./lines.shp">
<customproperties/>
</layer-tree-layer>
<custom-order enabled="0">
@ -33,11 +34,11 @@
<item>lines20151123133101198</item>
</custom-order>
</layer-tree-group>
<snapping-settings tolerance="20" mode="1" type="2" enabled="1" unit="1" intersection-snapping="0">
<snapping-settings mode="1" intersection-snapping="0" enabled="1" tolerance="20" type="2" unit="1">
<individual-layer-settings>
<layer-setting tolerance="20" units="1" id="lines20151123133101198" type="2" enabled="1"/>
<layer-setting tolerance="20" units="1" id="polys20151123133114244" type="2" enabled="1"/>
<layer-setting tolerance="20" units="1" id="points20151123133104693" type="2" enabled="1"/>
<layer-setting units="1" enabled="1" id="lines20151123133101198" tolerance="20" type="2"/>
<layer-setting units="1" enabled="1" id="points20151123133104693" tolerance="20" type="2"/>
<layer-setting units="1" enabled="1" id="polys20151123133114244" tolerance="20" type="2"/>
</individual-layer-settings>
</snapping-settings>
<relations/>
@ -65,26 +66,25 @@
<rendermaptile>0</rendermaptile>
</mapcanvas>
<legend updateDrawingOrder="true">
<legendlayer name="Planes" drawingOrder="-1" open="true" checked="Qt::Checked" showFeatureCount="0">
<filegroup open="true" hidden="false">
<legendlayerfile layerid="points20151123133104693" isInOverview="0" visible="1"/>
<legendlayer name="Planes" showFeatureCount="0" open="true" checked="Qt::Checked" drawingOrder="-1">
<filegroup hidden="false" open="true">
<legendlayerfile layerid="points20151123133104693" visible="1" isInOverview="0"/>
</filegroup>
</legendlayer>
<legendlayer name="Land" drawingOrder="-1" open="true" checked="Qt::Checked" showFeatureCount="0">
<filegroup open="true" hidden="false">
<legendlayerfile layerid="polys20151123133114244" isInOverview="0" visible="1"/>
<legendlayer name="Land" showFeatureCount="0" open="true" checked="Qt::Checked" drawingOrder="-1">
<filegroup hidden="false" open="true">
<legendlayerfile layerid="polys20151123133114244" visible="1" isInOverview="0"/>
</filegroup>
</legendlayer>
<legendlayer name="Roads" drawingOrder="-1" open="true" checked="Qt::Checked" showFeatureCount="0">
<filegroup open="true" hidden="false">
<legendlayerfile layerid="lines20151123133101198" isInOverview="0" visible="1"/>
<legendlayer name="Roads" showFeatureCount="0" open="true" checked="Qt::Checked" drawingOrder="-1">
<filegroup hidden="false" open="true">
<legendlayerfile layerid="lines20151123133101198" visible="1" isInOverview="0"/>
</filegroup>
</legendlayer>
</legend>
<mapViewDocks/>
<mapViewDocks3D/>
<projectlayers>
<maplayer simplifyLocal="1" autoRefreshEnabled="0" simplifyDrawingTol="1" refreshOnNotifyEnabled="0" type="vector" simplifyMaxScale="1" maxScale="1" readOnly="0" simplifyDrawingHints="1" hasScaleBasedVisibilityFlag="0" autoRefreshTime="0" refreshOnNotifyMessage="" minScale="1e+8" simplifyAlgorithm="0" geometry="Line" labelsEnabled="1">
<maplayer refreshOnNotifyMessage="" simplifyDrawingHints="1" simplifyMaxScale="1" refreshOnNotifyEnabled="0" simplifyDrawingTol="1" autoRefreshTime="0" autoRefreshEnabled="0" geometry="Line" simplifyAlgorithm="0" simplifyLocal="1" maxScale="1" type="vector" minScale="1e+8" labelsEnabled="1" readOnly="0" hasScaleBasedVisibilityFlag="0">
<extent>
<xmin>-117.62319839219053108</xmin>
<ymin>23.20820580488508966</ymin>
@ -116,6 +116,7 @@
<type></type>
<title></title>
<abstract></abstract>
<links/>
<fees></fees>
<encoding></encoding>
<crs>
@ -127,29 +128,29 @@
<description></description>
<projectionacronym></projectionacronym>
<ellipsoidacronym></ellipsoidacronym>
<geographicflag>true</geographicflag>
<geographicflag>false</geographicflag>
</spatialrefsys>
</crs>
<extent/>
<links/>
</resourceMetadata>
<provider encoding="UTF-8">ogr</provider>
<vectorjoins/>
<layerDependencies/>
<dataDependencies/>
<legend type="default-vector"/>
<expressionfields/>
<map-layer-style-manager current="default">
<map-layer-style name="default"/>
</map-layer-style-manager>
<auxiliaryLayer/>
<renderer-v2 type="categorizedSymbol" enableorderby="0" attr="Name" symbollevels="0" forceraster="0">
<renderer-v2 forceraster="0" enableorderby="0" type="categorizedSymbol" attr="Name" symbollevels="0">
<categories>
<category symbol="0" label="Arterial" value="Arterial" render="true"/>
<category symbol="1" label="Highway" value="Highway" render="true"/>
<category label="Arterial" render="true" symbol="0" value="Arterial"/>
<category label="Highway" render="true" symbol="1" value="Highway"/>
</categories>
<symbols>
<symbol name="0" clip_to_extent="1" type="line" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SimpleLine">
<layer class="SimpleLine" enabled="1" locked="0" pass="0">
<prop k="capstyle" v="square"/>
<prop k="customdash" v="5;2"/>
<prop k="customdash_map_unit_scale" v="3x:0,0,0,0,0,0"/>
@ -175,7 +176,7 @@
</layer>
</symbol>
<symbol name="1" clip_to_extent="1" type="line" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SimpleLine">
<layer class="SimpleLine" enabled="1" locked="0" pass="0">
<prop k="capstyle" v="square"/>
<prop k="customdash" v="5;2"/>
<prop k="customdash_map_unit_scale" v="3x:0,0,0,0,0,0"/>
@ -205,7 +206,7 @@
<sizescale/>
</renderer-v2>
<customproperties>
<property value="0" key="embeddedWidgets/count"/>
<property key="embeddedWidgets/count" value="0"/>
<property key="variableNames"/>
<property key="variableValues"/>
</customproperties>
@ -213,12 +214,12 @@
<featureBlendMode>0</featureBlendMode>
<layerOpacity>1</layerOpacity>
<SingleCategoryDiagramRenderer diagramType="Pie" attributeLegend="1">
<DiagramCategory maxScaleDenominator="1e+8" scaleDependency="Area" opacity="1" sizeType="MM" backgroundColor="#ffffff" labelPlacementMethod="XHeight" penColor="#000000" barWidth="5" minimumSize="0" enabled="0" rotationOffset="270" minScaleDenominator="1" penAlpha="255" lineSizeType="MM" diagramOrientation="Up" height="15" scaleBasedVisibility="0" width="15" sizeScale="3x:0,0,0,0,0,0" lineSizeScale="3x:0,0,0,0,0,0" backgroundAlpha="255" penWidth="0">
<fontProperties description="Ubuntu,13,-1,5,50,0,0,0,0,0" style=""/>
<attribute color="#000000" field="" label=""/>
<DiagramCategory scaleBasedVisibility="0" enabled="0" height="15" lineSizeScale="3x:0,0,0,0,0,0" barWidth="5" maxScaleDenominator="1e+8" penAlpha="255" scaleDependency="Area" labelPlacementMethod="XHeight" penWidth="0" penColor="#000000" sizeType="MM" backgroundAlpha="255" minimumSize="0" opacity="1" diagramOrientation="Up" width="15" rotationOffset="270" minScaleDenominator="1" backgroundColor="#ffffff" sizeScale="3x:0,0,0,0,0,0" lineSizeType="MM">
<fontProperties style="" description="Ubuntu,13,-1,5,50,0,0,0,0,0"/>
<attribute label="" field="" color="#000000"/>
</DiagramCategory>
</SingleCategoryDiagramRenderer>
<DiagramLayerSettings placement="2" priority="0" showAll="1" linePlacementFlags="10" zIndex="0" obstacle="0" dist="0">
<DiagramLayerSettings priority="0" dist="0" placement="2" obstacle="0" showAll="1" zIndex="0" linePlacementFlags="10">
<properties>
<Option type="Map">
<Option name="name" type="QString" value=""/>
@ -256,32 +257,32 @@
</field>
</fieldConfiguration>
<aliases>
<alias name="" field="Name" index="0"/>
<alias name="" field="Value" index="1"/>
<alias index="0" field="Name" name=""/>
<alias index="1" field="Value" name=""/>
</aliases>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<defaults>
<default field="Name" applyOnUpdate="0" expression=""/>
<default field="Value" applyOnUpdate="0" expression=""/>
<default applyOnUpdate="0" expression="" field="Name"/>
<default applyOnUpdate="0" expression="" field="Value"/>
</defaults>
<constraints>
<constraint field="Name" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Value" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Name" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Value" constraints="0" unique_strength="0"/>
</constraints>
<constraintExpressions>
<constraint exp="" desc="" field="Name"/>
<constraint exp="" desc="" field="Value"/>
<constraint field="Name" desc="" exp=""/>
<constraint field="Value" desc="" exp=""/>
</constraintExpressions>
<attributeactions/>
<attributetableconfig actionWidgetStyle="dropDown" sortOrder="0" sortExpression="">
<attributetableconfig sortExpression="" sortOrder="0" actionWidgetStyle="dropDown">
<columns>
<column name="Name" type="field" hidden="0" width="-1"/>
<column name="Value" type="field" hidden="0" width="-1"/>
<column type="actions" hidden="1" width="-1"/>
<column name="Blocked" type="field" hidden="0" width="-1"/>
<column name="Situation" type="field" hidden="0" width="-1"/>
<column name="BlockStart" type="field" hidden="0" width="-1"/>
<column hidden="0" width="-1" name="Name" type="field"/>
<column hidden="0" width="-1" name="Value" type="field"/>
<column hidden="1" width="-1" type="actions"/>
<column hidden="0" width="-1" name="Blocked" type="field"/>
<column hidden="0" width="-1" name="Situation" type="field"/>
<column hidden="0" width="-1" name="BlockStart" type="field"/>
</columns>
</attributetableconfig>
<editform>../src/quickgui/app/qgis-data</editform>
@ -318,7 +319,7 @@ def my_form_open(dialog, layer, feature):
<previewExpression>COALESCE( "Name", '&lt;NULL>' )</previewExpression>
<mapTip></mapTip>
</maplayer>
<maplayer simplifyLocal="1" autoRefreshEnabled="0" simplifyDrawingTol="1" refreshOnNotifyEnabled="0" type="vector" simplifyMaxScale="1" maxScale="1" readOnly="0" simplifyDrawingHints="0" hasScaleBasedVisibilityFlag="0" autoRefreshTime="0" refreshOnNotifyMessage="" minScale="1e+8" simplifyAlgorithm="0" geometry="Point" labelsEnabled="1">
<maplayer refreshOnNotifyMessage="" simplifyDrawingHints="0" simplifyMaxScale="1" refreshOnNotifyEnabled="0" simplifyDrawingTol="1" autoRefreshTime="0" autoRefreshEnabled="0" geometry="Point" simplifyAlgorithm="0" simplifyLocal="1" maxScale="1" type="vector" minScale="1e+8" labelsEnabled="0" readOnly="0" hasScaleBasedVisibilityFlag="0">
<extent>
<xmin>-118.88888888888877204</xmin>
<ymin>22.80020703933767834</ymin>
@ -350,6 +351,16 @@ def my_form_open(dialog, layer, feature):
<type></type>
<title></title>
<abstract></abstract>
<contact>
<name></name>
<organization></organization>
<position></position>
<voice></voice>
<fax></fax>
<email></email>
<role></role>
</contact>
<links/>
<fees></fees>
<encoding></encoding>
<crs>
@ -364,27 +375,35 @@ def my_form_open(dialog, layer, feature):
<geographicflag>false</geographicflag>
</spatialrefsys>
</crs>
<extent/>
<links/>
<extent>
<spatial miny="0" crs="" minz="0" maxy="0" maxx="0" minx="0" maxz="0" dimensions="2"/>
<temporal>
<period>
<start></start>
<end></end>
</period>
</temporal>
</extent>
</resourceMetadata>
<provider encoding="UTF-8">ogr</provider>
<vectorjoins/>
<layerDependencies/>
<dataDependencies/>
<legend type="default-vector"/>
<expressionfields/>
<map-layer-style-manager current="default">
<map-layer-style name="default"/>
</map-layer-style-manager>
<auxiliaryLayer/>
<renderer-v2 type="categorizedSymbol" enableorderby="0" attr="Class" symbollevels="0" forceraster="0">
<renderer-v2 forceraster="0" enableorderby="0" type="categorizedSymbol" attr="Class" symbollevels="0">
<categories>
<category symbol="0" label="B52" value="B52" render="true"/>
<category symbol="1" label="Biplane" value="Biplane" render="true"/>
<category symbol="2" label="Jet" value="Jet" render="true"/>
<category label="B52" render="true" symbol="0" value="B52"/>
<category label="Biplane" render="true" symbol="1" value="Biplane"/>
<category label="Jet" render="true" symbol="2" value="Jet"/>
</categories>
<symbols>
<symbol name="0" clip_to_extent="1" type="marker" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SvgMarker">
<layer class="SvgMarker" enabled="1" locked="0" pass="0">
<prop k="angle" v="0"/>
<prop k="color" v="189,189,189,255"/>
<prop k="fixedAspectRatio" v="0"/>
@ -418,7 +437,7 @@ def my_form_open(dialog, layer, feature):
</layer>
</symbol>
<symbol name="1" clip_to_extent="1" type="marker" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SvgMarker">
<layer class="SvgMarker" enabled="1" locked="0" pass="0">
<prop k="angle" v="0"/>
<prop k="color" v="124,124,124,255"/>
<prop k="fixedAspectRatio" v="0"/>
@ -452,7 +471,7 @@ def my_form_open(dialog, layer, feature):
</layer>
</symbol>
<symbol name="2" clip_to_extent="1" type="marker" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SvgMarker">
<layer class="SvgMarker" enabled="1" locked="0" pass="0">
<prop k="angle" v="0"/>
<prop k="color" v="178,223,138,255"/>
<prop k="fixedAspectRatio" v="0"/>
@ -490,7 +509,7 @@ def my_form_open(dialog, layer, feature):
<sizescale/>
</renderer-v2>
<customproperties>
<property value="0" key="embeddedWidgets/count"/>
<property key="embeddedWidgets/count" value="0"/>
<property key="variableNames"/>
<property key="variableValues"/>
</customproperties>
@ -498,12 +517,12 @@ def my_form_open(dialog, layer, feature):
<featureBlendMode>0</featureBlendMode>
<layerOpacity>1</layerOpacity>
<SingleCategoryDiagramRenderer diagramType="Pie" attributeLegend="1">
<DiagramCategory maxScaleDenominator="1e+8" scaleDependency="Area" opacity="1" sizeType="MM" backgroundColor="#ffffff" labelPlacementMethod="XHeight" penColor="#000000" barWidth="5" minimumSize="0" enabled="0" rotationOffset="270" minScaleDenominator="1" penAlpha="255" lineSizeType="MM" diagramOrientation="Up" height="15" scaleBasedVisibility="0" width="15" sizeScale="3x:0,0,0,0,0,0" lineSizeScale="3x:0,0,0,0,0,0" backgroundAlpha="255" penWidth="0">
<fontProperties description="Ubuntu,13,-1,5,50,0,0,0,0,0" style=""/>
<attribute color="#000000" field="" label=""/>
<DiagramCategory scaleBasedVisibility="0" enabled="0" height="15" lineSizeScale="3x:0,0,0,0,0,0" barWidth="5" maxScaleDenominator="1e+8" penAlpha="255" scaleDependency="Area" labelPlacementMethod="XHeight" penWidth="0" penColor="#000000" sizeType="MM" backgroundAlpha="255" minimumSize="0" opacity="1" diagramOrientation="Up" width="15" rotationOffset="270" minScaleDenominator="1" backgroundColor="#ffffff" sizeScale="3x:0,0,0,0,0,0" lineSizeType="MM">
<fontProperties style="" description="Ubuntu,13,-1,5,50,0,0,0,0,0"/>
<attribute label="" field="" color="#000000"/>
</DiagramCategory>
</SingleCategoryDiagramRenderer>
<DiagramLayerSettings placement="0" priority="0" showAll="1" linePlacementFlags="10" zIndex="0" obstacle="0" dist="0">
<DiagramLayerSettings priority="0" dist="0" placement="0" obstacle="0" showAll="1" zIndex="0" linePlacementFlags="2">
<properties>
<Option type="Map">
<Option name="name" type="QString" value=""/>
@ -523,12 +542,18 @@ def my_form_open(dialog, layer, feature):
<editWidget type="ValueMap">
<config>
<Option type="Map">
<Option name="map" type="Map">
<Option name="map" type="List">
<Option type="Map">
<Option name="B52" type="QString" value="B52"/>
</Option>
<Option type="Map">
<Option name="Biplane" type="QString" value="Biplane"/>
</Option>
<Option type="Map">
<Option name="Jet" type="QString" value="Jet"/>
</Option>
</Option>
</Option>
</config>
</editWidget>
</field>
@ -536,8 +561,8 @@ def my_form_open(dialog, layer, feature):
<editWidget type="TextEdit">
<config>
<Option type="Map">
<Option name="IsMultiline" type="QString" value="0"/>
<Option name="UseHtml" type="QString" value="0"/>
<Option name="IsMultiline" type="bool" value="false"/>
<Option name="UseHtml" type="bool" value="false"/>
</Option>
</config>
</editWidget>
@ -556,8 +581,8 @@ def my_form_open(dialog, layer, feature):
<editWidget type="TextEdit">
<config>
<Option type="Map">
<Option name="IsMultiline" type="QString" value="0"/>
<Option name="UseHtml" type="QString" value="0"/>
<Option name="IsMultiline" type="bool" value="false"/>
<Option name="UseHtml" type="bool" value="false"/>
</Option>
</config>
</editWidget>
@ -576,57 +601,103 @@ def my_form_open(dialog, layer, feature):
<editWidget type="TextEdit">
<config>
<Option type="Map">
<Option name="IsMultiline" type="QString" value="0"/>
<Option name="UseHtml" type="QString" value="0"/>
<Option name="IsMultiline" type="bool" value="false"/>
<Option name="UseHtml" type="bool" value="false"/>
</Option>
</config>
</editWidget>
</field>
<field name="Construct">
<editWidget type="DateTime">
<config>
<Option type="Map">
<Option name="allow_null" type="bool" value="true"/>
<Option name="calendar_popup" type="bool" value="true"/>
<Option name="display_format" type="QString" value="yyyy-MM-dd"/>
<Option name="field_format" type="QString" value="yyyy-MM-dd"/>
<Option name="field_iso_format" type="bool" value="false"/>
</Option>
</config>
</editWidget>
</field>
<field name="Photo">
<editWidget type="ExternalResource">
<config>
<Option type="Map">
<Option name="DocumentViewer" type="int" value="0"/>
<Option name="DocumentViewerHeight" type="int" value="0"/>
<Option name="DocumentViewerWidth" type="int" value="0"/>
<Option name="FileWidget" type="bool" value="true"/>
<Option name="FileWidgetButton" type="bool" value="true"/>
<Option name="FileWidgetFilter" type="QString" value=""/>
<Option name="PropertyCollection" type="Map">
<Option name="name" type="QString" value=""/>
<Option name="properties"/>
<Option name="type" type="QString" value="collection"/>
</Option>
<Option name="RelativeStorage" type="int" value="0"/>
<Option name="StorageMode" type="int" value="0"/>
</Option>
</config>
</editWidget>
</field>
</fieldConfiguration>
<aliases>
<alias name="" field="Class" index="0"/>
<alias name="" field="Heading" index="1"/>
<alias name="" field="Importance" index="2"/>
<alias name="" field="Pilots" index="3"/>
<alias name="" field="Cabin Crew" index="4"/>
<alias name="" field="Staff" index="5"/>
<alias index="0" field="Class" name=""/>
<alias index="1" field="Heading" name=""/>
<alias index="2" field="Importance" name=""/>
<alias index="3" field="Pilots" name=""/>
<alias index="4" field="Cabin Crew" name=""/>
<alias index="5" field="Staff" name=""/>
<alias index="6" field="Construct" name=""/>
<alias index="7" field="Photo" name=""/>
</aliases>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<defaults>
<default field="Class" applyOnUpdate="0" expression=""/>
<default field="Heading" applyOnUpdate="0" expression=""/>
<default field="Importance" applyOnUpdate="0" expression=""/>
<default field="Pilots" applyOnUpdate="0" expression=""/>
<default field="Cabin Crew" applyOnUpdate="0" expression=""/>
<default field="Staff" applyOnUpdate="0" expression=""/>
<default applyOnUpdate="0" expression="" field="Class"/>
<default applyOnUpdate="0" expression="" field="Heading"/>
<default applyOnUpdate="0" expression="" field="Importance"/>
<default applyOnUpdate="0" expression="" field="Pilots"/>
<default applyOnUpdate="0" expression="" field="Cabin Crew"/>
<default applyOnUpdate="0" expression="" field="Staff"/>
<default applyOnUpdate="0" expression="" field="Construct"/>
<default applyOnUpdate="0" expression="" field="Photo"/>
</defaults>
<constraints>
<constraint field="Class" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Heading" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Importance" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Pilots" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Cabin Crew" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Staff" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Class" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Heading" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Importance" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Pilots" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Cabin Crew" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Staff" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Construct" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Photo" constraints="0" unique_strength="0"/>
</constraints>
<constraintExpressions>
<constraint exp="" desc="" field="Class"/>
<constraint exp="" desc="" field="Heading"/>
<constraint exp="" desc="" field="Importance"/>
<constraint exp="" desc="" field="Pilots"/>
<constraint exp="" desc="" field="Cabin Crew"/>
<constraint exp="" desc="" field="Staff"/>
<constraint field="Class" desc="" exp=""/>
<constraint field="Heading" desc="" exp=""/>
<constraint field="Importance" desc="" exp=""/>
<constraint field="Pilots" desc="" exp=""/>
<constraint field="Cabin Crew" desc="" exp=""/>
<constraint field="Staff" desc="" exp=""/>
<constraint field="Construct" desc="" exp=""/>
<constraint field="Photo" desc="" exp=""/>
</constraintExpressions>
<attributeactions/>
<attributetableconfig actionWidgetStyle="dropDown" sortOrder="0" sortExpression="">
<attributeactions>
<defaultAction key="Canvas" value="{00000000-0000-0000-0000-000000000000}"/>
</attributeactions>
<attributetableconfig sortExpression="" sortOrder="0" actionWidgetStyle="dropDown">
<columns>
<column name="Class" type="field" hidden="0" width="-1"/>
<column name="Heading" type="field" hidden="0" width="-1"/>
<column name="Importance" type="field" hidden="0" width="-1"/>
<column name="Pilots" type="field" hidden="0" width="-1"/>
<column name="Cabin Crew" type="field" hidden="0" width="-1"/>
<column name="Staff" type="field" hidden="0" width="-1"/>
<column type="actions" hidden="1" width="-1"/>
<column hidden="0" width="-1" name="Class" type="field"/>
<column hidden="0" width="-1" name="Heading" type="field"/>
<column hidden="0" width="-1" name="Importance" type="field"/>
<column hidden="0" width="-1" name="Pilots" type="field"/>
<column hidden="0" width="-1" name="Cabin Crew" type="field"/>
<column hidden="0" width="-1" name="Staff" type="field"/>
<column hidden="1" width="-1" type="actions"/>
<column hidden="0" width="-1" name="Construct" type="field"/>
<column hidden="0" width="-1" name="Photo" type="field"/>
</columns>
</attributetableconfig>
<editform>../src/quickgui/app/qgis-data</editform>
@ -652,8 +723,26 @@ def my_form_open(dialog, layer, feature):
]]></editforminitcode>
<featformsuppress>0</featformsuppress>
<editorlayout>generatedlayout</editorlayout>
<editable/>
<labelOnTop/>
<editable>
<field name="Cabin Crew" editable="1"/>
<field name="Class" editable="1"/>
<field name="Construct" editable="1"/>
<field name="Heading" editable="1"/>
<field name="Importance" editable="1"/>
<field name="Photo" editable="1"/>
<field name="Pilots" editable="1"/>
<field name="Staff" editable="1"/>
</editable>
<labelOnTop>
<field name="Cabin Crew" labelOnTop="0"/>
<field name="Class" labelOnTop="0"/>
<field name="Construct" labelOnTop="0"/>
<field name="Heading" labelOnTop="0"/>
<field name="Importance" labelOnTop="0"/>
<field name="Photo" labelOnTop="0"/>
<field name="Pilots" labelOnTop="0"/>
<field name="Staff" labelOnTop="0"/>
</labelOnTop>
<widgets/>
<conditionalstyles>
<rowstyles/>
@ -663,7 +752,7 @@ def my_form_open(dialog, layer, feature):
<previewExpression>COALESCE( "Class", '&lt;NULL>' )</previewExpression>
<mapTip></mapTip>
</maplayer>
<maplayer simplifyLocal="1" autoRefreshEnabled="0" simplifyDrawingTol="1" refreshOnNotifyEnabled="0" type="vector" simplifyMaxScale="1" maxScale="1" readOnly="0" simplifyDrawingHints="1" hasScaleBasedVisibilityFlag="0" autoRefreshTime="0" refreshOnNotifyMessage="" minScale="1e+8" simplifyAlgorithm="0" geometry="Polygon" labelsEnabled="1">
<maplayer refreshOnNotifyMessage="" simplifyDrawingHints="1" simplifyMaxScale="1" refreshOnNotifyEnabled="0" simplifyDrawingTol="1" autoRefreshTime="0" autoRefreshEnabled="0" geometry="Polygon" simplifyAlgorithm="0" simplifyLocal="1" maxScale="1" type="vector" minScale="1e+8" labelsEnabled="1" readOnly="0" hasScaleBasedVisibilityFlag="0">
<extent>
<xmin>-118.92286230599032137</xmin>
<ymin>24.50786971868489061</ymin>
@ -695,6 +784,7 @@ def my_form_open(dialog, layer, feature):
<type></type>
<title></title>
<abstract></abstract>
<links/>
<fees></fees>
<encoding></encoding>
<crs>
@ -710,25 +800,25 @@ def my_form_open(dialog, layer, feature):
</spatialrefsys>
</crs>
<extent/>
<links/>
</resourceMetadata>
<provider encoding="UTF-8">ogr</provider>
<vectorjoins/>
<layerDependencies/>
<dataDependencies/>
<legend type="default-vector"/>
<expressionfields/>
<map-layer-style-manager current="default">
<map-layer-style name="default"/>
</map-layer-style-manager>
<auxiliaryLayer/>
<renderer-v2 type="categorizedSymbol" enableorderby="0" attr="Name" symbollevels="0" forceraster="0">
<renderer-v2 forceraster="0" enableorderby="0" type="categorizedSymbol" attr="Name" symbollevels="0">
<categories>
<category symbol="0" label="Dam" value="Dam" render="true"/>
<category symbol="1" label="Lake" value="Lake" render="true"/>
<category label="Dam" render="true" symbol="0" value="Dam"/>
<category label="Lake" render="true" symbol="1" value="Lake"/>
</categories>
<symbols>
<symbol name="0" clip_to_extent="1" type="fill" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SimpleFill">
<layer class="SimpleFill" enabled="1" locked="0" pass="0">
<prop k="border_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="color" v="118,191,227,132"/>
<prop k="joinstyle" v="bevel"/>
@ -750,7 +840,7 @@ def my_form_open(dialog, layer, feature):
</layer>
</symbol>
<symbol name="1" clip_to_extent="1" type="fill" alpha="1">
<layer pass="0" locked="0" enabled="1" class="SimpleFill">
<layer class="SimpleFill" enabled="1" locked="0" pass="0">
<prop k="border_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="color" v="110,194,217,255"/>
<prop k="joinstyle" v="bevel"/>
@ -776,7 +866,7 @@ def my_form_open(dialog, layer, feature):
<sizescale/>
</renderer-v2>
<customproperties>
<property value="0" key="embeddedWidgets/count"/>
<property key="embeddedWidgets/count" value="0"/>
<property key="variableNames"/>
<property key="variableValues"/>
</customproperties>
@ -784,12 +874,12 @@ def my_form_open(dialog, layer, feature):
<featureBlendMode>0</featureBlendMode>
<layerOpacity>1</layerOpacity>
<SingleCategoryDiagramRenderer diagramType="Pie" attributeLegend="1">
<DiagramCategory maxScaleDenominator="1e+8" scaleDependency="Area" opacity="1" sizeType="MM" backgroundColor="#ffffff" labelPlacementMethod="XHeight" penColor="#000000" barWidth="5" minimumSize="0" enabled="0" rotationOffset="270" minScaleDenominator="1" penAlpha="255" lineSizeType="MM" diagramOrientation="Up" height="15" scaleBasedVisibility="0" width="15" sizeScale="3x:0,0,0,0,0,0" lineSizeScale="3x:0,0,0,0,0,0" backgroundAlpha="255" penWidth="0">
<fontProperties description="Ubuntu,13,-1,5,50,0,0,0,0,0" style=""/>
<attribute color="#000000" field="" label=""/>
<DiagramCategory scaleBasedVisibility="0" enabled="0" height="15" lineSizeScale="3x:0,0,0,0,0,0" barWidth="5" maxScaleDenominator="1e+8" penAlpha="255" scaleDependency="Area" labelPlacementMethod="XHeight" penWidth="0" penColor="#000000" sizeType="MM" backgroundAlpha="255" minimumSize="0" opacity="1" diagramOrientation="Up" width="15" rotationOffset="270" minScaleDenominator="1" backgroundColor="#ffffff" sizeScale="3x:0,0,0,0,0,0" lineSizeType="MM">
<fontProperties style="" description="Ubuntu,13,-1,5,50,0,0,0,0,0"/>
<attribute label="" field="" color="#000000"/>
</DiagramCategory>
</SingleCategoryDiagramRenderer>
<DiagramLayerSettings placement="0" priority="0" showAll="1" linePlacementFlags="10" zIndex="0" obstacle="0" dist="0">
<DiagramLayerSettings priority="0" dist="0" placement="0" obstacle="0" showAll="1" zIndex="0" linePlacementFlags="10">
<properties>
<Option type="Map">
<Option name="name" type="QString" value=""/>
@ -829,29 +919,29 @@ def my_form_open(dialog, layer, feature):
</field>
</fieldConfiguration>
<aliases>
<alias name="" field="Name" index="0"/>
<alias name="" field="Value" index="1"/>
<alias index="0" field="Name" name=""/>
<alias index="1" field="Value" name=""/>
</aliases>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<defaults>
<default field="Name" applyOnUpdate="0" expression=""/>
<default field="Value" applyOnUpdate="0" expression=""/>
<default applyOnUpdate="0" expression="" field="Name"/>
<default applyOnUpdate="0" expression="" field="Value"/>
</defaults>
<constraints>
<constraint field="Name" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint field="Value" unique_strength="0" exp_strength="0" constraints="0" notnull_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Name" constraints="0" unique_strength="0"/>
<constraint notnull_strength="0" exp_strength="0" field="Value" constraints="0" unique_strength="0"/>
</constraints>
<constraintExpressions>
<constraint exp="" desc="" field="Name"/>
<constraint exp="" desc="" field="Value"/>
<constraint field="Name" desc="" exp=""/>
<constraint field="Value" desc="" exp=""/>
</constraintExpressions>
<attributeactions/>
<attributetableconfig actionWidgetStyle="dropDown" sortOrder="0" sortExpression="">
<attributetableconfig sortExpression="" sortOrder="0" actionWidgetStyle="dropDown">
<columns>
<column name="Name" type="field" hidden="0" width="-1"/>
<column name="Value" type="field" hidden="0" width="-1"/>
<column type="actions" hidden="1" width="-1"/>
<column hidden="0" width="-1" name="Name" type="field"/>
<column hidden="0" width="-1" name="Value" type="field"/>
<column hidden="1" width="-1" type="actions"/>
</columns>
</attributetableconfig>
<editform>../src/quickgui/app/qgis-data</editform>
@ -895,124 +985,146 @@ def my_form_open(dialog, layer, feature):
<layer id="lines20151123133101198"/>
</layerorder>
<properties>
<WMSPrecision type="QString">8</WMSPrecision>
<WMSContactMail type="QString"></WMSContactMail>
<WMSFees type="QString">conditions unknown</WMSFees>
<WMSAddWktGeometry type="bool">false</WMSAddWktGeometry>
<Paths>
<Absolute type="bool">false</Absolute>
</Paths>
<WMSUrl type="QString"></WMSUrl>
<Legend>
<filterByMap type="bool">false</filterByMap>
</Legend>
<WMSContactPosition type="QString"></WMSContactPosition>
<ddt2>
<designs type="QString"></designs>
</ddt2>
<WMSAccessConstraints type="QString">None</WMSAccessConstraints>
<WMSContactPerson type="QString"></WMSContactPerson>
<WMSOnlineResource type="QString"></WMSOnlineResource>
<WFSTLayers>
<Insert type="QStringList"/>
<Update type="QStringList"/>
<Delete type="QStringList"/>
</WFSTLayers>
<Variables>
<variableValues type="QStringList"/>
<variableNames type="QStringList"/>
</Variables>
<PositionPrecision>
<DegreeFormat type="QString">MU</DegreeFormat>
<DecimalPlaces type="int">2</DecimalPlaces>
<Automatic type="bool">true</Automatic>
</PositionPrecision>
<WMSSegmentizeFeatureInfoGeometry type="bool">false</WMSSegmentizeFeatureInfoGeometry>
<Macros>
<pythonCode type="QString"></pythonCode>
</Macros>
<WMSContactOrganization type="QString"></WMSContactOrganization>
<Identify>
<disabledLayers type="QStringList"/>
</Identify>
<DefaultStyles>
<ColorRamp type="QString"></ColorRamp>
<AlphaInt type="int">255</AlphaInt>
<Fill type="QString"></Fill>
<ColorRamp type="QString"></ColorRamp>
<Marker type="QString"></Marker>
<Line type="QString"></Line>
<Fill type="QString"></Fill>
<RandomColors type="bool">true</RandomColors>
</DefaultStyles>
<WMSServiceCapabilities type="bool">false</WMSServiceCapabilities>
<WMSServiceTitle type="QString"></WMSServiceTitle>
<WCSLayers type="QStringList"/>
<WMSServiceAbstract type="QString"></WMSServiceAbstract>
<SpatialRefSys>
<ProjectCrs type="QString">EPSG:4326</ProjectCrs>
<ProjectionsEnabled type="int">1</ProjectionsEnabled>
<ProjectCRSID type="int">3452</ProjectCRSID>
<ProjectCRSProj4String type="QString">+proj=longlat +datum=WGS84 +no_defs</ProjectCRSProj4String>
</SpatialRefSys>
<Measure>
<Ellipsoid type="QString">NONE</Ellipsoid>
</Measure>
<WMSKeywordList type="QStringList">
<value></value>
</WMSKeywordList>
<WMSUseLayerIDs type="bool">false</WMSUseLayerIDs>
<WMSContactPhone type="QString"></WMSContactPhone>
<WFSLayers type="QStringList"/>
<Measurement>
<AreaUnits type="QString">m2</AreaUnits>
<DistanceUnits type="QString">meters</DistanceUnits>
</Measurement>
<Variables>
<variableNames type="QStringList"/>
<variableValues type="QStringList"/>
</Variables>
<WMSAccessConstraints type="QString">None</WMSAccessConstraints>
<WMSAddWktGeometry type="bool">false</WMSAddWktGeometry>
<WMSFees type="QString">conditions unknown</WMSFees>
<SpatialRefSys>
<ProjectionsEnabled type="int">1</ProjectionsEnabled>
<ProjectCRSID type="int">3452</ProjectCRSID>
<ProjectCRSProj4String type="QString">+proj=longlat +datum=WGS84 +no_defs</ProjectCRSProj4String>
<ProjectCrs type="QString">EPSG:4326</ProjectCrs>
</SpatialRefSys>
<PAL>
<ShowingPartialsLabels type="bool">true</ShowingPartialsLabels>
<SearchMethod type="int">0</SearchMethod>
<ShowingAllLabels type="bool">false</ShowingAllLabels>
<DrawOutlineLabels type="bool">true</DrawOutlineLabels>
<CandidatesPolygon type="int">30</CandidatesPolygon>
<CandidatesPoint type="int">16</CandidatesPoint>
<CandidatesLine type="int">50</CandidatesLine>
<DrawRectOnly type="bool">false</DrawRectOnly>
<ShowingCandidates type="bool">false</ShowingCandidates>
</PAL>
<WMSContactPerson type="QString"></WMSContactPerson>
<WCSLayers type="QStringList"/>
<WFSTLayers>
<Insert type="QStringList"/>
<Delete type="QStringList"/>
<Update type="QStringList"/>
</WFSTLayers>
<Legend>
<filterByMap type="bool">false</filterByMap>
</Legend>
<WMSServiceTitle type="QString"></WMSServiceTitle>
<PositionPrecision>
<Automatic type="bool">true</Automatic>
<DecimalPlaces type="int">2</DecimalPlaces>
<DegreeFormat type="QString">MU</DegreeFormat>
</PositionPrecision>
<WMSContactMail type="QString"></WMSContactMail>
<WMSPrecision type="QString">8</WMSPrecision>
<WMSContactPhone type="QString"></WMSContactPhone>
<WMSServiceAbstract type="QString"></WMSServiceAbstract>
<ddt2>
<designs type="QString"></designs>
</ddt2>
<WMSSegmentizeFeatureInfoGeometry type="bool">false</WMSSegmentizeFeatureInfoGeometry>
<WMSServiceCapabilities type="bool">false</WMSServiceCapabilities>
<WMSContactOrganization type="QString"></WMSContactOrganization>
<WMSImageQuality type="int">90</WMSImageQuality>
<Gui>
<SelectionColorRedPart type="int">255</SelectionColorRedPart>
<CanvasColorGreenPart type="int">255</CanvasColorGreenPart>
<CanvasColorRedPart type="int">255</CanvasColorRedPart>
<CanvasColorBluePart type="int">255</CanvasColorBluePart>
<SelectionColorGreenPart type="int">255</SelectionColorGreenPart>
<SelectionColorAlphaPart type="int">255</SelectionColorAlphaPart>
<SelectionColorBluePart type="int">0</SelectionColorBluePart>
</Gui>
<WCSUrl type="QString"></WCSUrl>
<Digitizing>
<DefaultSnapType type="QString">to vertex and segment</DefaultSnapType>
<DefaultSnapToleranceUnit type="int">1</DefaultSnapToleranceUnit>
<LayerSnappingList type="QStringList">
<value>lines20151123133101198</value>
<value>points20151123133104693</value>
<value>polys20151123133114244</value>
</LayerSnappingList>
<DefaultSnapTolerance type="double">20</DefaultSnapTolerance>
<LayerSnappingToleranceList type="QStringList">
<value>20.000000</value>
<value>20.000000</value>
<value>20.000000</value>
</LayerSnappingToleranceList>
<LayerSnapToList type="QStringList">
<value>to_vertex_and_segment</value>
<value>to_vertex_and_segment</value>
<value>to_vertex_and_segment</value>
</LayerSnapToList>
<SnappingMode type="QString">current_layer</SnappingMode>
<AvoidIntersectionsList type="QStringList"/>
<LayerSnappingToleranceUnitList type="QStringList">
<value>1</value>
<value>1</value>
<value>1</value>
</LayerSnappingToleranceUnitList>
<LayerSnappingList type="QStringList">
<value>lines20151123133101198</value>
<value>points20151123133104693</value>
<value>polys20151123133114244</value>
</LayerSnappingList>
<SnappingMode type="QString">current_layer</SnappingMode>
<DefaultSnapTolerance type="double">20</DefaultSnapTolerance>
<LayerSnappingEnabledList type="QStringList">
<value>enabled</value>
<value>enabled</value>
<value>enabled</value>
</LayerSnappingEnabledList>
<LayerSnapToList type="QStringList">
<value>to_vertex_and_segment</value>
<value>to_vertex_and_segment</value>
<value>to_vertex_and_segment</value>
</LayerSnapToList>
<DefaultSnapToleranceUnit type="int">1</DefaultSnapToleranceUnit>
<AvoidIntersectionsList type="QStringList"/>
<DefaultSnapType type="QString">to vertex and segment</DefaultSnapType>
</Digitizing>
<Measurement>
<DistanceUnits type="QString">meters</DistanceUnits>
<AreaUnits type="QString">m2</AreaUnits>
</Measurement>
<WMSOnlineResource type="QString"></WMSOnlineResource>
<Macros>
<pythonCode type="QString"></pythonCode>
</Macros>
<WFSUrl type="QString"></WFSUrl>
<WMSImageQuality type="int">90</WMSImageQuality>
<Gui>
<SelectionColorGreenPart type="int">255</SelectionColorGreenPart>
<SelectionColorRedPart type="int">255</SelectionColorRedPart>
<CanvasColorRedPart type="int">255</CanvasColorRedPart>
<SelectionColorAlphaPart type="int">255</SelectionColorAlphaPart>
<CanvasColorGreenPart type="int">255</CanvasColorGreenPart>
<SelectionColorBluePart type="int">0</SelectionColorBluePart>
<CanvasColorBluePart type="int">255</CanvasColorBluePart>
</Gui>
<WCSUrl type="QString"></WCSUrl>
<WMSUrl type="QString"></WMSUrl>
<Identify>
<disabledLayers type="QStringList"/>
</Identify>
<WMSUseLayerIDs type="bool">false</WMSUseLayerIDs>
<WMSContactPosition type="QString"></WMSContactPosition>
<WMSKeywordList type="QStringList">
<value></value>
</WMSKeywordList>
<Paths>
<Absolute type="bool">false</Absolute>
</Paths>
</properties>
<visibility-presets/>
<transformContext/>
<projectMetadata>
<identifier></identifier>
<parentidentifier></parentidentifier>
<language></language>
<type></type>
<title></title>
<abstract></abstract>
<links/>
<author></author>
<creation></creation>
</projectMetadata>
<Annotations/>
<Layouts/>
</qgis>