La base de données du projet
QElectroTech construit une base de données SQLite pour chaque projet ouvert. Il vaut la peine d'être précis sur ce qu'est cette base, car son nom invite à une supposition qui est fausse :
La base de données du projet est un cache dérivé, en mémoire. Elle est reconstruite à partir du XML
.qetà chaque ouverture du projet, et elle n'est jamais réécrite dans le fichier projet. Le XML est la seule chose qui persiste.
Tout le reste de cette page découle de cette seule phrase.
Source : sources/dataBase/projectdatabase.{h,cpp}.
1. Pourquoi elle existe
Répondre à « liste tous les composants de ce projet, avec leurs références
fabricant, groupés par folio » depuis une scène graphique suppose de parcourir
des milliers de QGraphicsItem et de comparer des chaînes. Y répondre depuis
une table, c'est un SELECT.
QET conserve donc la même information sous une seconde forme, adaptée aux requêtes, et s'en sert pour ce qui est naturellement une requête :
| Consommateur | Ce qu'il lit |
|---|---|
| Tableaux de nomenclature placés sur un folio | element_nomenclature_view, via ProjectDBModel |
| Le constructeur de requêtes de nomenclature | n'importe laquelle des vues, assemblée dans ElementQueryWidget |
| Dialogue de liste de câblage | wiring_list_view |
--export-bom (ligne de commande) |
element_nomenclature_view |
--export-wires, --export-cables |
wiring_list_view |
| Tableaux récapitulatifs de folios | project_summary_view |
Aucun de ces usages n'est du stockage. Chacun est un rapport sur des données qui existent déjà dans le XML.
2. Cycle de vie
QETProject construit
└── projectDataBase construit → createDataBase()
├── ouvre une connexion SQLite anonyme
├── CREATE TABLE × 6, CREATE VIEW × 3
└── updateDB()
XML du projet lu
└── updateDB() ← repeuplement complet, une fois, tout étant chargé
l'utilisateur édite le folio
└── addElement / removeElement / elementInfoChanged
addDiagram / removeDiagram / diagramInfoChanged / diagramOrderChanged
addConductor/ removeConductor / updateConductor ← incrémental
projet fermé
└── base abandonnée
QSqlDatabase::addDatabase("QSQLITE", …) est appelé sans
setDatabaseName() : il n'y a donc aucun fichier sur disque vers lequel la
connexion pointerait. Pendant le chargement, les signaux de la base sont
bloqués et un unique updateDB() s'exécute à la fin, plutôt qu'une insertion
par objet au fur et à mesure de la construction de la scène.
Trois PRAGMA sont posés juste après l'ouverture — temp_store = MEMORY,
journal_mode = MEMORY, synchronous = OFF. Ces réglages seraient
inconsidérés pour une base à laquelle on tient. Ils sont corrects ici
précisément parce que tout perdre ne coûte rien : elle est reconstruite à
l'ouverture suivante.
QETProject::readProjectXml() journalise la durée de chaque phase du
chargement, dont la reconstruction de la base : le coût sur un projet donné se
lit donc directement dans la sortie console au lieu d'être supposé.
3. Schéma
Six tables :
| Table | Clé | Remarques |
|---|---|---|
diagram |
uuid |
plus pos, l'ordre des folios |
element |
uuid |
diagram_uuid, pos, type, sub_type |
diagram_info |
diagram_uuid |
une colonne par QETInformation::diagramInfoKeys() — 9 aujourd'hui |
element_info |
element_uuid |
une colonne par QETInformation::elementInfoKeys() — 57 aujourd'hui |
terminal |
(uuid, element_uuid) |
voir §4 |
conductor |
uuid |
les deux extrémités en paires (uuid de borne, uuid d'élément) |
Trois vues : element_nomenclature_view, project_summary_view,
wiring_list_view.
Remarquez ce que signifient ces listes de colonnes : le schéma est engendré à
l'exécution à partir de elementInfoKeys(). Ajouter un champ d'information
d'élément ajoute une colonne automatiquement, sans migration ni version de
schéma — parce qu'il n'existe aucune base à migrer. C'est la conséquence
pratique la plus importante du fait que le cache soit dérivé.
Le filtre est dans la vue, pas dans la table
element contient tous les types d'éléments, esclaves et renvois de folio
compris. La restriction aux « choses qu'une nomenclature doit mentionner »
(type IN ('simple','terminal','master','thumbnail')) se fait à l'intérieur de
element_nomenclature_view.
Il n'en a pas toujours été ainsi, et la raison du changement est instructive : avec le filtre dans la table, un élément esclave (un contact de relais) était totalement absent de la base, si bien que tout autre lecteur de la table — la liste de câblage, par exemple — perdait silencieusement chaque conducteur aboutissant à un contact de relais. L'opinion d'une nomenclature sur ce qui constitue une ligne n'a pas sa place dans le modèle que le projet a de lui-même.
4. L'identité, et pourquoi les bornes sont difficiles
Les lignes ont besoin de clés stables. Les éléments et les folios ont de vrais UUID : pas de problème. Les bornes, non.
Terminal::uuid() provient de la définition .elmt du catalogue. Il identifie
une position de borne dans un symbole — « la borne du haut d'un contacteur » —
et il est donc identique pour chaque instance placée de ce symbole. Il est
en outre vide pour tout élément créé avant l'existence de ce champ, c'est-à-dire
pour l'essentiel de la collection installée.
Deux conséquences, toutes deux traitées :
- Une instance de borne n'est unique que par la paire (
uuid,element_uuid) : c'est pourquoi cette paire, et nonuuidseul, est la clé primaire de la table des bornes et la cible des clés étrangères de la table des conducteurs. Terminal::stableUuid()fournit une identité lorsque la définition n'en donne aucune, dérivée en UUID v5 à partir de la position locale et de l'orientation de la borne dans son élément — la même base que celle que le format de projet utilise déjà pour rattacher un conducteur à une borne. Les noms sont délibérément exclus de la dérivation, car QET réécrit une borne nommée_comme non nommée, ce qui changerait son identité dès la première réécriture.
projectDataBase::excludedConductorCount() indique combien de conducteurs
n'ont pu être indexés du tout, en comptant depuis la scène vivante et non
depuis la base — « précisément parce que la base est là où ces conducteurs ne
sont pas ». C'est ce qui permet à une liste de câblage d'annoncer « N fils
manquent, et voici pourquoi » au lieu de présenter une liste incomplète comme
si elle était complète.
C'est la contrainte à garder en tête pour tout travail futur sur la persistance. Tant que la base est dérivée, une borne dont l'identité est devinée par la géométrie coûte un défaut de cache. Dans un format de fichier, la même supposition devient une migration définitive, en une seule fois, des projets de tout le monde.
5. Ce qui découle de « dérivée »
| Parce qu'elle est dérivée… | …ceci est vrai |
|---|---|
| Reconstruite à chaque ouverture | Aucune version de schéma, aucune migration, jamais |
Jamais écrite dans le .qet |
Une ligne erronée ne coûte rien — rouvrez, elle a disparu |
| Le XML fait autorité | La base ne peut pas contredire le dessin ; si c'est le cas, c'est la base qui a tort |
| Abandonnée à la fermeture | synchronous = OFF et consorts sont sans danger |
| Vit dans un seul processus | Elle n'est ni partagée, ni concurrente, ni multi-utilisateur |
Et le revers, tout aussi vrai :
| Parce qu'elle est dérivée… | …ceci est vrai aussi |
|---|---|
| Rien ne survit à la fermeture | Tout ce que la base seule sait est perdu |
| Reconstruite intégralement à l'ouverture | Le coût d'ouverture croît avec la taille du projet |
| Absente du fichier | Deux personnes ne peuvent pas interroger la même base de projet |
6. La voir par soi-même
Les compilations avec QET_EXPORT_PROJECT_DB disposent d'une entrée de menu,
Exporter la base de donnée interne du projet, qui copie la base vivante dans
un fichier .sqlite via l'API de sauvegarde de SQLite. L'option CMake vaut
OFF par défaut, mais les empaquetages officiels Windows, macOS, Flatpak et
Snap l'activent tous — sur une version publiée, l'entrée est donc normalement
présente.
Le fichier exporté est un instantané, pour inspection. Le modifier ne change rien : rien ne le relit jamais.
sqlite3 monprojet.sqlite ".schema"
sqlite3 monprojet.sqlite "SELECT label, designation FROM element_nomenclature_view LIMIT 20;"
7. Ce qu'elle n'est pas
Ce n'est pas le fichier projet, ni une base partagée à laquelle une équipe se connecte, ni l'architecture « base de composants plus vue de dessin » d'outils comme EPLAN. Un projet reste un unique fichier XML ; la base est un index de requêtes au-dessus de lui, qui vit tant que la fenêtre est ouverte.
Savoir si cela doit rester ainsi est une question ouverte — voir les pages Vision et Feuille de route. Tout mouvement vers une persistance est une modification du format de fichier, dont le problème d'identité des bornes du §4 est le premier véritable obstacle.
Tant qu'une telle décision n'est pas prise, une règle empirique mérite d'être suivie lorsqu'on ajoute une fonctionnalité : ne créez pas d'état que seul le XML connaît, et ne créez pas d'état que seule la base connaît. Le premier rend le cache incomplet ; le second ne survit pas à une fermeture.
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
