docs: Create module bootstrap sequence (#148)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Document the two-phase bootstrap: console-phase addon loading via exec(), GUI-phase workbench registration, six deferred QTimer callbacks (kc_format, silo origin, auth panel, first-start check, activity panel, update checker), and the Gitea releases API polling with skip/interval logic.
This commit is contained in:
@@ -50,6 +50,8 @@
|
||||
# Reference
|
||||
|
||||
- [Configuration](./reference/configuration.md)
|
||||
- [Create Module Bootstrap](./reference/create-module-bootstrap.md)
|
||||
- [Datum Creator](./reference/datum-creator.md)
|
||||
- [Glossary](./reference/glossary.md)
|
||||
|
||||
# C++ API Reference
|
||||
|
||||
132
docs/src/reference/create-module-bootstrap.md
Normal file
132
docs/src/reference/create-module-bootstrap.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Create Module Bootstrap Sequence
|
||||
|
||||
The Create module (`src/Mod/Create/`) is the integration layer that bootstraps Kindred Create's addons and deferred services. It runs in two phases: console (headless) and GUI.
|
||||
|
||||
**Source files:**
|
||||
|
||||
- `src/Mod/Create/Init.py` -- console-phase addon loading
|
||||
- `src/Mod/Create/InitGui.py` -- GUI-phase workbench loading and deferred timers
|
||||
- `src/Mod/Create/update_checker.py` -- Gitea releases API polling
|
||||
- `src/Mod/Create/kc_format.py` -- .kc file format round-trip preservation
|
||||
|
||||
## Loading Mechanism
|
||||
|
||||
FreeCAD loads module `Init.py` and `InitGui.py` files using **direct `exec()`**, not Python's import system. The C++ startup code scans `Mod/` directories, reads each file as text, compiles it, and executes it in an isolated namespace:
|
||||
|
||||
```python
|
||||
source = init_py.read_text(encoding="utf-8")
|
||||
code = compile(source, init_py, "exec")
|
||||
exec(code)
|
||||
```
|
||||
|
||||
Modules loaded this way are **not** added to `sys.modules`. Each execution gets a fresh globals dict with no caching or dependency resolution.
|
||||
|
||||
## Phase 1: Console (`Init.py`)
|
||||
|
||||
Runs immediately at application startup, before any GUI is available.
|
||||
|
||||
`setup_kindred_addons()` discovers and loads two built-in addons from `mods/`:
|
||||
|
||||
| Addon | Path | Purpose |
|
||||
|-------|------|---------|
|
||||
| ztools | `mods/ztools/ztools/` | Part design, assembly, and sketcher tools |
|
||||
| silo | `mods/silo/freecad/` | Database integration and version control |
|
||||
|
||||
For each addon:
|
||||
|
||||
1. Build the path from `FreeCAD.getHomePath() + "mods/" + addon_path`
|
||||
2. Add to `sys.path` if not already present
|
||||
3. Find `Init.py` in the addon directory
|
||||
4. Execute it with `exec(compile(source, init_file, "exec"), exec_globals)`
|
||||
|
||||
Failures are logged to `FreeCAD.Console.PrintWarning()` and do not prevent other addons from loading.
|
||||
|
||||
## Phase 2: GUI (`InitGui.py`)
|
||||
|
||||
Runs after the console phase, when `FreeCADGui` is available.
|
||||
|
||||
### Synchronous
|
||||
|
||||
`setup_kindred_workbenches()` loads `InitGui.py` from the same two addons (ztools, silo), registering their workbenches and GUI commands.
|
||||
|
||||
### Deferred Timers
|
||||
|
||||
Six `QTimer.singleShot` calls stagger initialization to let the GUI event loop settle:
|
||||
|
||||
| Delay | Function | Purpose |
|
||||
|-------|----------|---------|
|
||||
| 500 ms | `_register_kc_format()` | Register .kc file format observer |
|
||||
| 1500 ms | `_register_silo_origin()` | Register Silo as a file origin |
|
||||
| 2000 ms | `_setup_silo_auth_panel()` | Dock the Database Auth panel |
|
||||
| 3000 ms | `_check_silo_first_start()` | Show settings dialog on first run |
|
||||
| 4000 ms | `_setup_silo_activity_panel()` | Dock the Database Activity panel |
|
||||
| 10000 ms | `_check_for_updates()` | Poll Gitea for new releases |
|
||||
|
||||
Every timer callback is wrapped in `try/except`. Failures log to `FreeCAD.Console.PrintLog()` and do not crash the application.
|
||||
|
||||
### Timer Details
|
||||
|
||||
**_register_kc_format (500 ms)** -- Imports `kc_format` and calls `kc_format.register()`, which installs a `DocumentObserver` with `slotStartSaveDocument` / `slotFinishSaveDocument` hooks. These cache `silo/` ZIP entries before FreeCAD's C++ save rewrites the archive, then re-inject them afterwards. Only processes `.kc` files.
|
||||
|
||||
**_register_silo_origin (1500 ms)** -- Imports `silo_origin` and calls `silo_origin.register_silo_origin()`, making Silo available in the origin selector dropdown alongside LocalFileOrigin.
|
||||
|
||||
**_setup_silo_auth_panel (2000 ms)** -- Creates a `QDockWidget` titled "Database Auth" containing a `SiloAuthDockWidget` from `silo_commands`. Docked on the right side of the main window. Guards against duplicates by checking if the widget already exists.
|
||||
|
||||
**_check_silo_first_start (3000 ms)** -- Reads `User parameter:BaseApp/Preferences/Mod/KindredSilo`. On the very first launch (when `FirstStartChecked` is not set), runs the `Silo_Settings` command if no API URL is configured.
|
||||
|
||||
**_setup_silo_activity_panel (4000 ms)** -- Creates a "Database Activity" dock widget showing the 20 most recent Silo items (part number, description, date). Falls back to "(Unable to connect to Silo database)" on connection failure.
|
||||
|
||||
**_check_for_updates (10000 ms)** -- Calls `update_checker._run_update_check()` in the background.
|
||||
|
||||
## Update Checker
|
||||
|
||||
`update_checker.py` polls the Gitea releases API for newer versions.
|
||||
|
||||
### Check Decision (`_should_check`)
|
||||
|
||||
Reads preferences from `User parameter:BaseApp/Preferences/Mod/KindredCreate/Update`:
|
||||
|
||||
| Parameter | Type | Default | Purpose |
|
||||
|-----------|------|---------|---------|
|
||||
| `CheckEnabled` | bool | true | Master enable/disable |
|
||||
| `CheckIntervalDays` | int | 1 | Minimum days between checks |
|
||||
| `LastCheckTimestamp` | string | (none) | ISO timestamp of last check |
|
||||
| `SkippedVersion` | string | (none) | Version the user chose to skip |
|
||||
|
||||
A check runs only if enabled, the interval has elapsed, and the current version is not the skipped version.
|
||||
|
||||
### Check Execution (`check_for_update`)
|
||||
|
||||
1. Query `https://git.kindred-systems.com/api/v1/repos/kindred/create/releases?limit=10` (5 s timeout)
|
||||
2. Filter out drafts, pre-releases, and the "latest" tag
|
||||
3. Parse version tags (`v0.1.3` becomes tuple `(0, 1, 3)`)
|
||||
4. Compare against `version.VERSION` from the Create module
|
||||
5. If a newer version exists, log it to the console
|
||||
6. Record `LastCheckTimestamp`
|
||||
|
||||
The checker never shows a dialog -- it only logs to `FreeCAD.Console`.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
FreeCAD startup
|
||||
|
|
||||
Init.py (exec'd, immediate)
|
||||
+-- setup_kindred_addons()
|
||||
| +-- ztools/Init.py (exec'd)
|
||||
| +-- silo/freecad/Init.py (exec'd)
|
||||
|
|
||||
[GUI startup]
|
||||
|
|
||||
InitGui.py (exec'd, immediate)
|
||||
+-- setup_kindred_workbenches()
|
||||
| +-- ztools/InitGui.py (exec'd)
|
||||
| +-- silo/freecad/InitGui.py (exec'd)
|
||||
|
|
||||
+-- QTimer 500ms --> kc_format.register()
|
||||
+-- QTimer 1500ms --> silo_origin.register_silo_origin()
|
||||
+-- QTimer 2000ms --> SiloAuthDockWidget
|
||||
+-- QTimer 3000ms --> Silo first-start check
|
||||
+-- QTimer 4000ms --> Database Activity panel
|
||||
+-- QTimer 10000ms --> update_checker._run_update_check()
|
||||
```
|
||||
Reference in New Issue
Block a user