Data Singleton¶
The Data hook is the backbone of a plugin. It creates a QML singleton that holds shared state and logic accessible from every other plugin file.
How it works¶
- Name your file with the
datakeyword: e.g.,MyPluginData.qml. - Start the file with
pragma Singleton. - Other plugin files import
Pluginand reference the type by its filename (without.qml).
MyPluginData.qml → MyPluginData (QML type name)
NetFetchData.qml → NetFetchData
MFDData.qml → MFDData
Naming rules
The filename must start with a capital letter (QML convention for type names). The type name used in other QML files must match the filename exactly.
Minimal example¶
pragma Singleton
import QtQuick
import QGroundControl.Plugin
Item {
property bool enabled: false
property string message: "Hello"
}
Consuming it from another plugin file:
Settings persistence¶
Use QtCore to persist user preferences across sessions. The Settings component maps QML properties to persistent storage using aliases:
pragma Singleton
import QtQuick
import QtCore
import QGroundControl.Plugin
Item {
id: root
property bool enabled: false
property int selectedMode: 0
Settings {
id: settings
category: "MyPlugin"
property alias enabled: root.enabled
property alias selectedMode: root.selectedMode
}
}
The category groups your settings under a namespace to avoid collisions with other plugins or AMC itself.
Logging¶
Use PluginManager.log() to write messages to the AMC log:
Initialization¶
Component.onCompleted runs once when the singleton is created. Use it for setup logic:
Component.onCompleted: {
PluginManager.log("MyPlugin: starting up")
if (enabled) {
startFetching()
}
}
Reacting to property changes¶
Use on<Property>Changed handlers to respond when properties change:
Full example: NetFetchData¶
The NetFetch plugin's data singleton demonstrates the complete pattern -- settings persistence, timers, HTTP requests, enums, and logging:
pragma Singleton
import QGroundControl
import QtQuick
import QtCore
import QGroundControl.Plugin
Item {
id: root
enum DataSource {
CurrentTime,
WeatherDemo,
ISSLocation
}
property bool enabled: true
property int selectedDataSource: NetFetchData.DataSource.CurrentTime
property string currentData: "No data"
property bool loading: false
property string lastError: ""
property int updateCount: 0
readonly property var dataSourceLabels: [
"Current Time", "Weather Demo", "ISS Location"
]
Timer {
id: updateTimer
interval: 10000
running: root.enabled
repeat: true
triggeredOnStart: true
onTriggered: fetchData()
}
function fetchData() {
if (!enabled) return
loading = true
lastError = ""
switch (selectedDataSource) {
case NetFetchData.DataSource.CurrentTime:
fetchTime(); break
case NetFetchData.DataSource.WeatherDemo:
fetchWeather(); break
case NetFetchData.DataSource.ISSLocation:
fetchISS(); break
default:
currentData = "Unknown data source"
loading = false
}
updateCount++
PluginManager.log("NetFetch: Fetching data, source=" + selectedDataSource + ", count=" + updateCount)
}
function fetchTime() {
Qt.callLater(function() {
loading = false
currentData = new Date().toLocaleString(Qt.locale(), "yyyy-MM-dd HH:mm:ss")
lastError = ""
})
}
function fetchWeather() {
Qt.callLater(function() {
loading = false
var temp = Math.floor(Math.random() * 30) + 10
var conditions = ["Sunny", "Cloudy", "Rainy", "Windy"]
currentData = conditions[Math.floor(Math.random() * conditions.length)] + ", " + temp + "°C"
lastError = ""
})
}
function fetchISS() {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
loading = false
if (xhr.status === 200) {
try {
var r = JSON.parse(xhr.responseText)
var lat = parseFloat(r.iss_position.latitude).toFixed(2)
var lon = parseFloat(r.iss_position.longitude).toFixed(2)
currentData = "ISS: " + lat + "°, " + lon + "°"
lastError = ""
} catch (e) {
lastError = "Parse error: " + e
currentData = "Error: " + lastError
}
} else {
lastError = "HTTP " + xhr.status
currentData = "Error: " + lastError
}
}
}
xhr.open("GET", "http://api.open-notify.org/iss-now.json")
xhr.send()
}
function refresh() { fetchData() }
Settings {
id: settings
category: "NetFetch"
property alias enabled: root.enabled
property alias selectedDataSource: root.selectedDataSource
}
Component.onCompleted: {
PluginManager.log("NetFetch: Data component initialized")
if (enabled) fetchData()
}
onEnabledChanged: {
if (enabled) fetchData()
else currentData = "Disabled"
}
onSelectedDataSourceChanged: {
if (enabled) fetchData()
}
}
Accessing vehicle data¶
The data singleton is a natural place to compute derived values from vehicle telemetry:
pragma Singleton
import QtQuick
import QGroundControl
import QGroundControl.Plugin
Item {
readonly property var vehicle:
QGroundControl.multiVehicleManager.activeVehicle
readonly property bool hasVehicle: vehicle !== null
readonly property string speedText:
hasVehicle ? vehicle.groundSpeed.value.toFixed(1) : "--"
readonly property string gpsInfo:
hasVehicle ? vehicle.gps.count.valueString + " sats" : "No GPS"
}