Die Projektdatenbank
QElectroTech baut für jedes geöffnete Projekt eine SQLite-Datenbank auf. Es lohnt sich, genau zu sagen, was diese Datenbank ist, denn ihr Name legt eine Annahme nahe, die nicht zutrifft:
Die Projektdatenbank ist ein abgeleiteter Cache im Arbeitsspeicher. Sie wird bei jedem Öffnen des Projekts aus dem
.qet-XML neu aufgebaut und nie in die Projektdatei zurückgeschrieben. Allein das XML ist dauerhaft.
Alles Weitere auf dieser Seite folgt aus diesem einen Satz.
Quelle: sources/dataBase/projectdatabase.{h,cpp}.
1. Wozu sie da ist
„Liste alle Bauteile dieses Projekts mit Herstellerreferenzen, nach Folio
gruppiert“ aus einer Grafikszene zu beantworten, heißt Tausende von
QGraphicsItem zu durchlaufen und Zeichenketten zu vergleichen. Aus einer
Tabelle beantwortet, ist es ein SELECT.
QET hält dieselbe Information deshalb in einer zweiten Form vor, die für Abfragen taugt, und nutzt sie für das, was von Natur aus eine Abfrage ist:
| Nutzer | Was gelesen wird |
|---|---|
| Stücklistentabellen auf einem Folio | element_nomenclature_view, über ProjectDBModel |
| Der Abfrage-Assistent der Stückliste | jede der Sichten, zusammengesetzt in ElementQueryWidget |
| Dialog der Verdrahtungsliste | wiring_list_view |
--export-bom (Kommandozeile) |
element_nomenclature_view |
--export-wires, --export-cables |
wiring_list_view |
| Folio-Übersichtstabellen | project_summary_view |
Nichts davon ist Speicherung. Jedes davon ist ein Bericht über Daten, die im XML bereits vorliegen.
2. Lebenszyklus
QETProject erzeugt
└── projectDataBase erzeugt → createDataBase()
├── öffnet eine namenlose SQLite-Verbindung
├── CREATE TABLE × 6, CREATE VIEW × 3
└── updateDB()
Projekt-XML gelesen
└── updateDB() ← vollständiger Neuaufbau, einmal, nach allem Laden
Benutzer bearbeitet das Folio
└── addElement / removeElement / elementInfoChanged
addDiagram / removeDiagram / diagramInfoChanged / diagramOrderChanged
addConductor/ removeConductor / updateConductor ← inkrementell
Projekt geschlossen
└── Datenbank verworfen
QSqlDatabase::addDatabase("QSQLITE", …) wird ohne setDatabaseName()
aufgerufen; es gibt also keine Datei auf der Platte, auf die die Verbindung
zeigen könnte. Während des Ladens sind die Signale der Datenbank blockiert, und
am Ende läuft ein einziges updateDB() statt eines Einfügevorgangs je Objekt
während des Szenenaufbaus.
Direkt nach dem Öffnen werden drei PRAGMAs gesetzt — temp_store = MEMORY,
journal_mode = MEMORY, synchronous = OFF. Für eine Datenbank, an der einem
liegt, wären diese Einstellungen leichtsinnig. Hier sind sie genau deshalb
richtig, weil ein Totalverlust nichts kostet: Beim nächsten Öffnen wird ohnehin
neu aufgebaut.
QETProject::readProjectXml() protokolliert die Dauer jeder Ladephase, den
Datenbankaufbau eingeschlossen. Die Kosten für ein bestimmtes Projekt lassen
sich also direkt der Konsolenausgabe entnehmen, statt geschätzt zu werden.
3. Schema
Sechs Tabellen:
| Tabelle | Schlüssel | Anmerkungen |
|---|---|---|
diagram |
uuid |
dazu pos, die Folio-Reihenfolge |
element |
uuid |
diagram_uuid, pos, type, sub_type |
diagram_info |
diagram_uuid |
je eine Spalte pro QETInformation::diagramInfoKeys() — heute 9 |
element_info |
element_uuid |
je eine Spalte pro QETInformation::elementInfoKeys() — heute 57 |
terminal |
(uuid, element_uuid) |
siehe §4 |
conductor |
uuid |
beide Enden als Paare (Klemmen-uuid, Element-uuid) |
Drei Sichten: element_nomenclature_view, project_summary_view,
wiring_list_view.
Man beachte, was diese Spaltenlisten bedeuten: das Schema wird zur Laufzeit
aus elementInfoKeys() erzeugt. Ein neues Elementinformationsfeld fügt
automatisch eine Spalte hinzu, ohne Migration und ohne Schemaversion — denn es
gibt keine bestehende Datenbank zu migrieren. Das ist die praktisch wichtigste
Folge daraus, dass der Cache abgeleitet ist.
Der Filter sitzt in der Sicht, nicht in der Tabelle
element enthält alle Elementtypen, Slaves und Folioverweise
eingeschlossen. Die Einschränkung auf „was eine Stückliste nennen sollte“
(type IN ('simple','terminal','master','thumbnail')) geschieht innerhalb von
element_nomenclature_view.
Das war nicht immer so, und der Grund für die Änderung ist lehrreich: Lag der Filter in der Tabelle, fehlte ein Slave-Element (ein Relaiskontakt) in der Datenbank vollständig — jeder andere Leser der Tabelle, etwa die Verdrahtungsliste, verlor damit stillschweigend jeden Leiter, der an einem Relaiskontakt endete. Die Ansicht einer Stückliste darüber, was eine Position ist, gehört nicht in das Modell, das ein Projekt von sich selbst hat.
4. Identität, und warum Klemmen schwierig sind
Zeilen brauchen stabile Schlüssel. Elemente und Folios haben echte UUIDs, also kein Problem. Klemmen nicht.
Terminal::uuid() stammt aus der Katalogdefinition .elmt. Es bezeichnet eine
Klemmenposition in einem Symbol — „die obere Klemme eines Schützes“ — und ist
daher für jede platzierte Instanz dieses Symbols identisch. Zudem ist es bei
jedem Element leer, das vor Einführung dieses Feldes erstellt wurde, also beim
größten Teil der installierten Sammlung.
Zwei Konsequenzen, beide behandelt:
- Eine Klemmeninstanz ist nur als Paar (
uuid,element_uuid) eindeutig. Deshalb ist dieses Paar — und nichtuuidallein — der Primärschlüssel der Klemmentabelle und das Ziel der Fremdschlüssel der Leitertabelle. Terminal::stableUuid()liefert eine Identität, wenn die Definition keine vorgibt: als UUID v5, abgeleitet aus lokaler Position und Ausrichtung der Klemme innerhalb ihres Elements — derselben Grundlage, die das Projektformat ohnehin schon nutzt, um einen Leiter einer Klemme zuzuordnen. Namen bleiben bewusst außen vor, denn QET schreibt eine Klemme namens_als unbenannt zurück, wodurch sich die Identität beim ersten erneuten Speichern ändern würde.
projectDataBase::excludedConductorCount() meldet, wie viele Leiter überhaupt
nicht indizierbar waren — gezählt aus der lebenden Szene, nicht aus der
Datenbank, „genau deshalb, weil die Datenbank der Ort ist, an dem diese Leiter
nicht sind“. Das erlaubt einer Verdrahtungsliste zu sagen „N Drähte fehlen,
und zwar aus diesem Grund“, statt eine unvollständige Liste als vollständig
auszugeben.
Das ist die Randbedingung, die man bei jeder künftigen Persistenzarbeit im Blick behalten muss. Solange die Datenbank abgeleitet ist, kostet eine aus der Geometrie erratene Klemmenidentität einen Cache-Fehlschlag. Als Dateiformat wird aus derselben Schätzung eine endgültige, einmalige Migration der Projekte aller Anwender.
5. Was aus „abgeleitet“ folgt
| Weil sie abgeleitet ist… | …gilt dies |
|---|---|
| Bei jedem Öffnen neu aufgebaut | Keine Schemaversion, keine Migrationen, nie |
Nie ins .qet geschrieben |
Eine falsche Zeile kostet nichts — neu öffnen, und sie ist weg |
| Das XML ist maßgeblich | Die Datenbank kann der Zeichnung nicht widersprechen; tut sie es doch, irrt die Datenbank |
| Beim Schließen verworfen | synchronous = OFF und Verwandtes sind unbedenklich |
| Lebt in einem Prozess | Sie ist weder geteilt noch nebenläufig noch mehrbenutzerfähig |
Und die Kehrseite, ebenso wahr:
| Weil sie abgeleitet ist… | …gilt auch dies |
|---|---|
| Nichts überlebt das Schließen | Alles, was nur die Datenbank weiß, geht verloren |
| Beim Öffnen vollständig neu aufgebaut | Die Öffnungskosten wachsen mit der Projektgröße |
| Nicht in der Datei enthalten | Zwei Personen können nicht dieselbe Projektdatenbank abfragen |
6. Selbst nachsehen
Mit QET_EXPORT_PROJECT_DB übersetzte Programme haben einen Menüeintrag,
Interne Projektdatenbank exportieren, der die lebende Datenbank über die
Backup-API von SQLite in eine .sqlite-Datei kopiert. Die CMake-Option steht
standardmäßig auf OFF, doch die offiziellen Pakete für Windows, macOS,
Flatpak und Snap schalten sie alle ein — in einer veröffentlichten Version
ist der Eintrag also normalerweise vorhanden.
Die exportierte Datei ist eine Momentaufnahme zur Einsicht. Sie zu bearbeiten ändert nichts: Niemand liest sie je zurück.
sqlite3 meinprojekt.sqlite ".schema"
sqlite3 meinprojekt.sqlite "SELECT label, designation FROM element_nomenclature_view LIMIT 20;"
7. Was sie nicht ist
Sie ist nicht die Projektdatei, keine gemeinsame Datenbank, mit der sich ein Team verbindet, und nicht die Architektur aus Bauteildatenbank plus Zeichnungsansicht, wie sie Werkzeuge wie EPLAN haben. Ein Projekt bleibt eine einzige XML-Datei; die Datenbank ist ein Abfrageindex darüber, der lebt, solange das Fenster offen ist.
Ob das so bleiben soll, ist eine offene Frage — siehe die Seiten Vision und Entwicklungs-Roadmap. Jeder Schritt hin zu Persistenz ist eine Änderung des Dateiformats, und das Problem der Klemmenidentität aus §4 ist deren erste echte Hürde.
Bis eine solche Entscheidung fällt, lohnt beim Hinzufügen von Funktionen eine Faustregel: Erzeugen Sie keinen Zustand, den nur das XML kennt, und keinen, den nur die Datenbank kennt. Ersteres macht den Cache unvollständig; Letzteres überlebt kein Schließen.
Getting Started
🌐 Languages — English · Français · Deutsch
Guides
Conductors — wire properties, what feeds which export, and cables
Wire & cable catalogue — cable types, IEC 60757 core colours, assigning a core to a conductor
Printing and exporting — paper, PDF, images, and what each path does differently
Linking elements — master, slave, terminal
PLC modules — I/O tables and linking a wire to a specific point
Using the element editor — drawing tools, saving, checks
Preferences reference — what each settings page does
Keyboard-only control — mouseless QET, and the one real gap
Mouse modifiers — what Shift, Ctrl and Alt change while you drag
Managing collections — folders, writability, building your own shortlist
Templates — reusable multi-element blocks, and why clicking one does nothing
Search & Replace — bulk property changes
Building a nomenclature query — the BOM/summary table builder
Linking wires across pages — folio reports
Variables & formulas — %f, %{label}, sequences
Auto-numbering — schemes, sequences, freezing
Terminal strips — strips, levels, bridges
Title block templates — the .titleblock format
Importing EPLAN parts (.edz) — EPLAN Data Portal
DXF import & export — two unrelated features, one format
The project database — the in-memory SQLite cache
Development
Automating QET — CLI, XML formats, external tools
CLI Reference — command line usage
JavaScript Scripting — --run, geometry editing, undo
Vision — proposal, under discussion
