Demo 1: Hello World
Plugin Structure and Installation
What We Will Learn
- How to deploy and update a project plugin
- The structure of a project plugin
- Most important QML object classes
Production Installation and Update
- Deploy with QField Sync
- Problems? Reopen project, restart QField
Development Installation and Update
- Load directly from DOS into QField for Windows (local or Cloud)
- For each update, close and restart.
>>> "C:\Program Files\QField\usr\bin\qfield.exe" C:\demopath\demo1_hello.qgs
Plugin Structure
demo1_hello/
|
├── demo1_hello.qml # Main QML file
|
├── demo1_hello.qgs # QGIS project
|
└── components/
|
├── d1_plugin_component.qml # Main component / Dialog
|
├── d1_plugin_theme.qml # Style definitions
|
└── qmldir # Global imports
Plugin Structure
- Main QML has the same name as the project
- All other files in the attachment directory "components"
Core QML Classes (Components)
QObject
|___ QtObject
| |___ Item (Base for Visual Types, like QtWidget)
| | |___ Rectangle
| | |___ Text
| | |___ Image
| | |___ MouseArea
| | |___ Loader
| | |___ Controls
| | | |___ Button
| | | |___ AbstractButton
| | | | |___ QToolButton
| | | | | |___ QfToolButton
| |___ Component (Container/Utility Class. Not related to Item!)
| |___ Connections
| |___ Other non visual types and utilities
1. An Item is the Root Object of Our Plugin
Item {
id: plugin
parent: iface.mapCanvas()
anchors.fill: parent
Rectangle{...}
...
}
- parent: Must be set to mapCanvas
- anchors.fill: Fills the entire screen
- id: Object name for references
Every QML Document has a single root Object.
2. Component Provides Lifecycle Hooks
Component.onCompleted and Component.onDestruction can be attached to any QML object
Item {
id: plugin
parent: iface.mapCanvas()
anchors.fill: parent
Loader {...}
Component.onCompleted: {
// Called AFTER Item is fully constructed
iface.logMessage("Plugin loaded")
}
Component.onDestruction: {
// Called BEFORE Item is destroyed
iface.logMessage("Plugin unloaded")
}
}
Use Case: Register/deregister handlers, initialize data, clean up resources
3. Loader "Constructs" and "Destructs" the Root Object of the Source Component with the "active" Property
Item {
Loader {
id: pluginLoader
active: false
source: Qt.resolvedUrl(
'./components/d1_plugin_component.qml'
)
}
QfToolButton {
onClicked: {
pluginLoader.active = !pluginLoader.active
}
}
}
Loader loads/unloads components dynamically
Component Also Defines Reusable Templates
Component can wrap Item definitions for reuse with Loader
// Explicit Component definition (we don't use this)
Component {
id: myTemplate
Rectangle { text: "Reusable content" }
}
Loader { sourceComponent: myTemplate }
// Loading from file (what we use)
// QML file = implicit Component
Loader { source: "d1_plugin_component.qml" }
Why the confusion? QML developers call all reusable QML definitions "components,"
but Component is also a specific class. When loading from files, Components exist invisibly behind the scenes.
4. d1_plugin_component.qml Contains the Loaded Rectangle Component
Rectangle {
id: pluginFrame
anchors.fill: parent
color: PluginTheme.vanilla
Text {
text: "Demo 1 Vegetation Monitoring: Plugin Component"
color: PluginTheme.green
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent
}
...
}
PluginTheme.green comes from the style in qmldir and d1_plugin_theme.qml
qmldir and PluginTheme
Globally imported style definitions:
// qmldir
singleton PluginTheme 1.0 d1_plugin_theme.qml
// d1_plugin_theme.qml
QtObject {
readonly property color vanilla: "#ffecd1"
readonly property color green: "#6baa75"
}
Exercises for the Next 20 Minutes
Tasks:
- Exercise 1: Deploy Demo1 plugin and open in QField
- Exercise 2: Change the title text color (PluginTheme)
- Exercise 3: Change the text in the Rectangle