Skip to content

Commit

Permalink
[QgsQuick] Improvements patch (#42804)
Browse files Browse the repository at this point in the history
* [QgsQuick] Improvements

- several GUI fixes
- support for attaching extra handler to a text widget to set data (qr scanner, sensors)
- updating feature attributes with default value and "onUpdate" checked when any attribute has been edited (real-time form update)
- updated docs

Co-authored-by: vsklencar <viktor.sklencar@lutraconsulting.co.uk>
  • Loading branch information
sklencar and vsklencar committed Apr 15, 2021
1 parent dbb1efd commit 4f79ba8
Show file tree
Hide file tree
Showing 29 changed files with 432 additions and 184 deletions.
2 changes: 1 addition & 1 deletion src/quickgui/attributes/qgsquickattributeformmodel.h
Expand Up @@ -26,7 +26,7 @@ class QVariant;

/**
* \ingroup quick
* \brief This is a model implementation for attribute form of a feature from a vector layer.
* 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).
Expand Down
2 changes: 1 addition & 1 deletion src/quickgui/attributes/qgsquickattributeformmodelbase.h
Expand Up @@ -41,7 +41,7 @@ class QVariant;

/**
* \ingroup quick
* \brief This is an internal (implementation) class used as the source model for QgsQuickAttributeFormModel.
* This is an internal (implementation) class used as the source model for QgsQuickAttributeFormModel.
*
* \sa QgsQuickAttributeFormModel
*
Expand Down
18 changes: 17 additions & 1 deletion src/quickgui/attributes/qgsquickattributemodel.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgsvectorlayer.h"

#include "qgsquickattributemodel.h"
#include "qgsvectorlayereditbuffer.h"

QgsQuickAttributeModel::QgsQuickAttributeModel( QObject *parent )
: QAbstractListModel( parent )
Expand Down Expand Up @@ -184,7 +185,10 @@ bool QgsQuickAttributeModel::setData( const QModelIndex &index, const QVariant &
QString msg( 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" ) );
QString userFriendlyMsg( tr( "Value %1 is not compatible with field type %2." ).arg( value.toString(), fld.typeName() ) );
QgsMessageLog::logMessage( msg );
emit dataChangedFailed( userFriendlyMsg );
if ( !val.isNull() )
{
emit dataChangedFailed( userFriendlyMsg );
}
return false;
}
bool success = mFeatureLayerPair.featureRef().setAttribute( index.row(), val );
Expand Down Expand Up @@ -403,6 +407,18 @@ void QgsQuickAttributeModel::create()
emit featureCreated( mFeatureLayerPair.featureRef() );
}

bool QgsQuickAttributeModel::hasAnyChanges()
{
if ( FID_IS_NULL( mFeatureLayerPair.feature().id() ) ) return true;

if ( mFeatureLayerPair.layer() && mFeatureLayerPair.layer()->editBuffer() )
{
return mFeatureLayerPair.layer()->editBuffer()->isFeatureAttributesChanged( mFeatureLayerPair.feature().id() );
}

return false;
}

bool QgsQuickAttributeModel::commit()
{
if ( !mFeatureLayerPair.layer()->commitChanges() )
Expand Down
5 changes: 4 additions & 1 deletion src/quickgui/attributes/qgsquickattributemodel.h
Expand Up @@ -27,7 +27,7 @@
/**
* \ingroup quick
*
* \brief Basic item model for attributes of QgsFeature associated
* 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.
Expand Down Expand Up @@ -112,6 +112,9 @@ class QUICK_EXPORT QgsQuickAttributeModel : public QAbstractListModel
//! Adds feature from featureLayerPair to the layer
Q_INVOKABLE void create();

//! Returns true if a current feature is new or has uncommitted attribute changes. Geometry change is omitted.
Q_INVOKABLE bool hasAnyChanges();

/**
* Suppress layer's QgsEditFormConfig
*
Expand Down
2 changes: 1 addition & 1 deletion src/quickgui/attributes/qgsquicksubmodel.h
Expand Up @@ -22,7 +22,7 @@
/**
* \ingroup quick
*
* \brief Helper class for submodels (e.g. tabs within feature model).
* Helper class for submodels (e.g. tabs within feature model).
*
* It uses internal mapping from internal indexes to indexes in the parent model.
*
Expand Down
1 change: 1 addition & 0 deletions src/quickgui/images/ic_angle_down.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/quickgui/images/images.qrc
Expand Up @@ -14,5 +14,6 @@
<file>ic_today.svg</file>
<file>ic_back.svg</file>
<file>ic_angle_right.svg</file>
<file>ic_angle_down.svg</file>
</qresource>
</RCC>
1 change: 1 addition & 0 deletions src/quickgui/plugin/CMakeLists.txt
Expand Up @@ -12,6 +12,7 @@ set(QGIS_QUICK_PLUGIN_SRC
set(QGIS_QUICK_PLUGIN_RESOURCES
components/qgsquickicontextitem.qml
components/qgsquickcheckboxcomponent.qml
components/qgsquickswitch.qml
editor/qgsquickeditorwidgetcombobox.qml
editor/qgsquickcheckbox.qml
editor/qgsquickdatetime.qml
Expand Down
80 changes: 52 additions & 28 deletions src/quickgui/plugin/components/qgsquickicontextitem.qml
Expand Up @@ -15,44 +15,68 @@

import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
import QgsQuick 0.1 as QgsQuick

Item {
property real iconSize
property color fontColor
property real fontPixelSize: root.iconSize * 0.75
property real fontPointSize: root.iconSize * 0.75
property string iconSource
property string labelText

id: root
width: root.iconSize + text.width
height: root.iconSize

Image {
id: icon
source: root.iconSource
width: root.iconSize
height: root.iconSize
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
}

ColorOverlay {
anchors.fill: icon
source: icon
color: root.fontColor
}
ColumnLayout {
anchors.fill: parent
spacing: 2 * QgsQuick.Utils.dp

Item {
id: iconContainer

Layout.fillHeight: true
Layout.preferredHeight: root.height / 2
Layout.preferredWidth: root.width

Image {
id: icon

anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom

source: root.iconSource
width: root.iconSize
height: root.iconSize
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
}

ColorOverlay {
anchors.fill: icon
source: icon
color: root.fontColor
}
}

Item {
id: textContainer

Layout.fillHeight: true
Layout.preferredHeight: root.height / 2
Layout.preferredWidth: root.width

Text {
id: text

Text {
id: text
height: root.iconSize
text: root.labelText
font.pixelSize: root.fontPixelSize
color: root.fontColor
anchors.leftMargin: root.iconSize + fieldItem.textMargin
x: root.iconSize + fieldItem.textMargin
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
text: root.labelText
font.pointSize: root.fontPointSize
width: parent.width
color: root.fontColor
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
maximumLineCount: 3
}
}
}
}
46 changes: 46 additions & 0 deletions src/quickgui/plugin/components/qgsquickswitch.qml
@@ -0,0 +1,46 @@
import QtQuick 2.0
import QtQuick.Templates 2.0 as T
import QgsQuick 0.1 as QgsQuick

T.Switch {
id: control

property color bgndColorActive: "green"
property color bgndColorInactive: "grey"
property color handleColor: "white"
property bool isReadOnly: false

signal switchChecked( bool isChecked )

implicitHeight: 60 * QgsQuick.Utils.dp
implicitWidth: 2 * height

indicator: Rectangle {
x: {
let actualPosition = control.visualPosition * (control.width - width)

if ( control.checked ) // limit maximum position
return Math.min( actualPosition, control.width * 0.55 )
else // limit minimum position
return Math.max( actualPosition, control.width * 0.13 )
}
y: (control.height - height) / 2
height: parent.height * 0.66
width: height

radius: 20 * QgsQuick.Utils.dp
color: control.handleColor

Behavior on x {
enabled: !control.pressed
SmoothedAnimation { velocity: 200 }
}
}

background: Rectangle {
radius: 20 * QgsQuick.Utils.dp
color: control.isReadOnly || !control.checked ? control.bgndColorInactive : control.bgndColorActive
}

onCheckedChanged: control.switchChecked( checked )
}
76 changes: 34 additions & 42 deletions src/quickgui/plugin/editor/qgsquickcheckbox.qml
Expand Up @@ -13,7 +13,7 @@
* *
***************************************************************************/

import QtQuick 2.6
import QtQuick 2.7
import QtQuick.Controls 2.2
import QgsQuick 0.1 as QgsQuick

Expand All @@ -23,83 +23,75 @@ import QgsQuick 0.1 as QgsQuick
* Do not use directly from Application QML
*/
Item {
signal valueChanged( var value, bool isNull )
id: fieldItem

property var checkedState: getConfigValue(config['CheckedState'], true)
property var uncheckedState: getConfigValue(config['UncheckedState'], false)
property string booleanEnum: "1" // QMetaType::Bool Enum of Qvariant::Type
property bool isReadOnly: readOnly

id: fieldItem
enabled: !readOnly
height: childrenRect.height
anchors {
right: parent.right
left: parent.left
rightMargin: 10 * QgsQuick.Utils.dp
}
signal valueChanged( var value, bool isNull )

function getConfigValue(configValue, defaultValue) {
if (!configValue && field.type + "" === fieldItem.booleanEnum) {
return defaultValue
} else return configValue
}

enabled: !readOnly
height: childrenRect.height
anchors {
right: parent.right
left: parent.left
}

Rectangle {
id: fieldContainer
height: customStyle.fields.height
color: customStyle.fields.backgroundColor
radius: customStyle.fields.cornerRadius
anchors { right: parent.right; left: parent.left }

MouseArea {
anchors.fill: parent
onClicked: switchComp.toggle()
}

Text {
text: checkBox.checked ? fieldItem.checkedState : fieldItem.uncheckedState
font.pixelSize: customStyle.fields.fontPixelSize
text: switchComp.checked ? fieldItem.checkedState : fieldItem.uncheckedState
font.pointSize: customStyle.fields.fontPointSize
color: customStyle.fields.fontColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
leftPadding: 6 * QgsQuick.Utils.dp
leftPadding: customStyle.fields.sideMargin
}

CheckBox {
QgsQuick.SwitchComponent {
id: switchComp

property var currentValue: value
height: customStyle.fields.height/2
width: height * 2

isReadOnly: fieldItem.isReadOnly
bgndColorActive: customStyle.toolbutton.activeButtonColor
bgndColorInactive: customStyle.toolbutton.backgroundColorInvalid

anchors.right: parent.right
anchors.rightMargin: fieldItem.anchors.rightMargin
anchors.verticalCenter: parent.verticalCenter
id: checkBox
leftPadding: 0
checked: value === fieldItem.checkedState
anchors.rightMargin: customStyle.fields.sideMargin

indicator: Rectangle {
implicitWidth: parent.width
implicitHeight: parent.height
x: checkBox.leftPadding
y: parent.height / 2 - height / 2
radius: parent.height/2
color: checkBox.checked ? customStyle.fields.fontColor : "#ffffff"
border.color: checkBox.checked ? customStyle.fields.fontColor : customStyle.fields.normalColor

Rectangle {
x: checkBox.checked ? parent.width - width : 0
width: parent.height
height: parent.height
radius: parent.height/2
color: "#ffffff"
border.color: checkBox.checked ? customStyle.fields.fontColor : customStyle.fields.normalColor
}
}
implicitHeight: fieldContainer.height * 0.6

checked: value === fieldItem.checkedState

onCheckedChanged: {
valueChanged( checked ? fieldItem.checkedState : fieldItem.uncheckedState, false )
forceActiveFocus()
onSwitchChecked: {
valueChanged( isChecked ? fieldItem.checkedState : fieldItem.uncheckedState, false )
}

// Workaround to get a signal when the value has changed
onCurrentValueChanged: {
checked = currentValue === fieldItem.checkedState
switchComp.checked = currentValue === fieldItem.checkedState
}
}
}
Expand Down

0 comments on commit 4f79ba8

Please sign in to comment.