Demo 2a
Create, Read, Update, Delete
Was wir lernen werden
- Wie man eine Zeile zu einer Tabelle hinzufügt
- Wie man eine Zeile aktualisiert
- Wie man eine Zeile löscht
Die wichtigste Demo für Formulare
Was macht das Plugin?
-
Plot Id vom Parent-Plugin erhalten
-
Menü mit Eintragsreihen für Plot aus der Tabelle
entries füllen
-
New Entry Knopf: Neue, leere Eintrag erstellen
-
Menü Auswahl und New Entry Knopf: Eintragswerte ins Formular laden
-
Save Entry Knopf: Eintrag aus dem Formular aktualisieren
Plugin-Architektur: Drei Dateien, Drei Rollen
-
FormDataModel — d2a_data_model.qml
„Welche Felder hat das Formular?“
Ein statisches JS-Array mit Feldbeschreibungen (Name, Beschriftung). Steuert den Repeater — keine Logik, kein Laufzeitzustand.
-
View — d2a_plugin_component.qml
„Wie sieht das Formular aus?“
Layout und Widget-Bindungen. Ein Repeater iteriert über FormDataModel.fields und erstellt für jeden Eintrag ein FormField. Jede Eingabe liest aus dem Controller und schreibt zurück in ihn.
-
Controller — d2a_form_controller.qml
„Welche Werte haben die Felder, und wie werden sie gespeichert?“
Verwaltet den Laufzeit-Puffer (fieldValues) und die Auswahlliste (entryListModel). Führt alle QField-API-Aufrufe aus: Erstellen, Laden, Speichern, Löschen.
Model
Das Model konfiguriert die Eingaben für das Formular. Es wird vom Repeater verwendet, um dynamisch Eingabewidgets für jedes Layer-Attribut in der Tabelle entries zu erstellen.
QtObject{
property var fields: [{"name":"name", "label":"Entry Name"},
{"name":"s1", "label":"Thoughts"},
{"name":"s2", "label":"Feelings"}]
}
Plugin Component mit Model and Controller
Das Plugin-Component ist unsere View. Es enthält das Model und den Controller.
Item{
id: pluginFrame
// not a singleton (has state), must be instantiated
FormController{
id: formController
}
//singleton (static) FormDataModel instance is shared across all plugin instances
property var fieldModel: FormDataModel.fields
}
- View verwendet Model, um das Formular zu erstellen
- Controller enthält die Verarbeitungslogik
View : Model Interaktion mit Repeater
Anstatt jedes Widget manuell zu erstellen, verwenden wir einen Repeater, um die Form-Struktur dynamisch zu generieren
component FormField: Rectangle{
id: formField
property var fieldData
RowLayout {
id: fieldRow
Text {
text: fieldData.label
}
TextField {
id: input
text: controller.fieldValues[fieldData.name]
onTextChanged: {
controller.updateField(fieldData.name, text)
}
}
}
}
Repeater {
model: fieldModel
delegate: FormField {
fieldData: modelData
}
}
- Konfiguration in DataModel definiert
- Repeater iteriert und erstellt Eingabe-Objekte dynamisch
⚠️ Achtung
Niemals die QField-API umgehen
Niemals über den Daten-Provider zugreifen
Warum?
- QField Sync basiert auf QGIS Offline Editing
- Direkte Änderungen werden nicht erkannt
- Synchronisierung überschreibt Ihre Daten
Immer FeatureUtils & LayerUtils verwenden
CREATE: Feature einfügen
// 1. Bearbeitung starten
layer.startEditing()
// 2. Leeres Feature erstellen
var newFeature = FeatureUtils.createFeature(layer)
// 3. Attribute setzen
newFeature.setAttribute("f_uid", StringUtils.createUuid())
newFeature.setAttribute("plot_id", plotId)
newFeature.setAttribute("log_date", new Date().toISOString())
// 4. Feature zum Layer hinzufügen
LayerUtils.addFeature(layer, newFeature)
// 5. Änderungen commit
try{
layer.commitChanges()
} catch (e) {
layer.rollBack()
}
READ: Feature abrufen
var expression = "plot_id = '" + plotId + "'"
var iterator = LayerUtils.createFeatureIteratorFromExpression(layer, expression)
if (iterator.hasNext()) {
currentFeature = iterator.next()
iterator.close() // Immer schließen, sobald Sie fertig sind mit dem Iterator
return currentFeature
}
iterator.close() // Immer schließen
IMMER Iterator schließen
UPDATE: Feature aktualisieren
// 1. Bearbeitung starten
layer.startEditing()
// 2. Feature-ID und Fields holen
var fid = feature.id
var fields = feature.fields
var fieldName = 'field_name_to_update'
var value = 'new_value'
var fieldIndex = fields.indexOf(fieldName)
if (fieldIndex >= 0) {
layer.changeAttributeValue(fid, fieldIndex, value)
}
// 4. Änderungen commit
try{
layer.commitChanges()
} catch (e) {
layer.rollBack()
}
DELETE: Feature löschen
// 1. Bearbeitung starten
// 1. Bearbeitung starten
layer.startEditing()
// 2. Feature löschen
var featureId = feature.id
var success = layer.deleteFeature(featureId)
if (!success) {
// 3a. Bei Fehler: Rollback
layer.rollBack()
return false
}
// 3b. Bei Erfolg: Commit
var commitSuccess = layer.commitChanges()
if (!commitSuccess) {
layer.rollBack()
}
Wichtige Warnung: Field Index
Niemals hart kodierte Indizes verwenden
Index in QGIS ist nicht gleich Index in QField.
❌ FALSCH:
value = feature.attribute(3)
✅ RICHTIG:
value = feature.attribute("plot_id")
// Oder für Updates:
var fieldIndex = fields.indexOf("plot_id")
layer.changeAttributeValue(fid, fieldIndex, value)
Übungen für die nächsten 60 Minuten
Aufgaben:
- Übung 1: Demo2a-Plugin bereitstellen und testen
- Übung 2: Ein Element zum Formular hinzufügen, indem du das Model aktualisierst
- Übung 3: Ein Element von den Formular entfernen
- Übung 4: Einen Standardwert für die Beschreibung einfügen, mit dem Model und der View
- Übung 5: Bau ein eigenes Plugin.