docs(sdk): add KCSDK API reference and addon developer guide
- docs/src/reference/kcsdk-python.md: full kcsdk Python API reference - docs/src/development/writing-an-addon.md: step-by-step addon guide - docs/INTEGRATION_PLAN.md: add Phase 7 KCSDK section - docs/ARCHITECTURE.md: add src/Gui/SDK/ to source layout - docs/src/SUMMARY.md: add new pages to mdBook navigation
This commit is contained in:
@@ -116,6 +116,17 @@ mods/silo/ [submodule → silo-mod.git] FreeCAD workbench
|
||||
├── silo_commands.py Commands + FreeCADSiloSettings adapter
|
||||
└── silo_origin.py FileOrigin backend for Silo (via SDK)
|
||||
|
||||
src/Gui/SDK/ KCSDK C++ shared library (libKCSDK.so)
|
||||
├── KCSDKGlobal.h DLL export macros
|
||||
├── Types.h Plain C++ types (ContextDef, DockArea, PanelPersistence)
|
||||
├── IPanelProvider.h Abstract dock panel interface
|
||||
├── WidgetBridge.h/.cpp PySide QWidget <-> C++ QWidget* (via Gui::PythonWrapper)
|
||||
├── SDKRegistry.h/.cpp Singleton registry — contexts, panels, providers
|
||||
└── bindings/ pybind11 module (kcsdk.so)
|
||||
├── kcsdk_py.cpp Module definition — enums, functions, classes
|
||||
├── PyIPanelProvider.h Trampoline for Python subclassing
|
||||
└── PyProviderHolder.h GIL-safe forwarding wrapper
|
||||
|
||||
src/Gui/EditingContext.h/.cpp EditingContextResolver singleton + context registry
|
||||
src/Gui/BreadcrumbToolBar.h/.cpp Color-coded breadcrumb toolbar (Catppuccin Mocha)
|
||||
src/Gui/FileOrigin.h/.cpp FileOrigin base class + LocalFileOrigin
|
||||
|
||||
@@ -167,6 +167,52 @@ Theme colors are now centralized in the SDK's YAML palette (`mods/sdk/kindred_sd
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: KCSDK — C++-backed SDK module -- IN PROGRESS
|
||||
|
||||
**Goal:** Replace the pure-Python SDK wrappers with a C++ shared library (`libKCSDK.so`) and pybind11 bindings (`kcsdk.so`). This gives addons a stable, typed API with proper GIL safety and enables future C++ addon development without Python.
|
||||
|
||||
**Architecture:**
|
||||
|
||||
```
|
||||
Python Addons (silo, future addons, ...)
|
||||
|
|
||||
kindred_sdk (mods/sdk/) <- convenience layer (try kcsdk, fallback FreeCADGui)
|
||||
|
|
||||
kcsdk.so (pybind11 module) <- C++ API bindings
|
||||
|
|
||||
KCSDK (C++ shared library) <- SDKRegistry + provider interfaces
|
||||
|
|
||||
FreeCADGui (EditingContextResolver, DockWindowManager, OriginManager, ...)
|
||||
```
|
||||
|
||||
**Sub-phases:**
|
||||
|
||||
| # | Issue | Status | Description |
|
||||
|---|-------|--------|-------------|
|
||||
| 1 | #350 | DONE | Scaffold KCSDK library + kcsdk pybind11 module |
|
||||
| 2 | #351 | DONE | Migrate editing context API to kcsdk |
|
||||
| 3 | #352 | DONE | Panel provider system (IPanelProvider) |
|
||||
| 4 | #353 | — | C++ theme engine |
|
||||
| 5 | #354 | — | Toolbar provider system (IToolbarProvider) |
|
||||
| 6 | #355 | — | Menu and action system |
|
||||
| 7 | #356 | — | Status bar provider + origin migration |
|
||||
| 8 | #357 | — | Deprecation cleanup + SDK v1.0.0 |
|
||||
|
||||
**Key files:**
|
||||
|
||||
- `src/Gui/SDK/` — C++ library (KCSDKGlobal.h, Types.h, SDKRegistry, IPanelProvider, WidgetBridge)
|
||||
- `src/Gui/SDK/bindings/` — pybind11 module (kcsdk_py.cpp, PyIPanelProvider, PyProviderHolder)
|
||||
- `mods/sdk/kindred_sdk/` — Python wrappers with kcsdk/legacy fallback
|
||||
|
||||
**Design decisions:**
|
||||
|
||||
- **No Qt in public C++ API** — `Types.h` uses `std::string`, `std::vector`, `std::function`. Qt conversion happens internally in `SDKRegistry.cpp`.
|
||||
- **GIL-safe Python callables** — Python callbacks stored via `std::make_shared<py::object>` with `py::gil_scoped_acquire` before every invocation.
|
||||
- **PySide widget bridging** — `WidgetBridge::toQWidget()` converts PySide QWidget objects to C++ `QWidget*` via `Gui::PythonWrapper` (Shiboken).
|
||||
- **Provider pattern** — Interfaces like `IPanelProvider` enable addons to register factories. The registry calls `create_widget()` once and manages the lifecycle through `DockWindowManager`.
|
||||
|
||||
---
|
||||
|
||||
## Design decisions
|
||||
|
||||
1. **`Create::` namespace prefix.** All Kindred Create C++ features use this prefix to distinguish them from FreeCAD core.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
- [Build System](./development/build-system.md)
|
||||
- [Gui Module Build](./development/gui-build-integration.md)
|
||||
- [Package.xml Schema Extensions](./development/package-xml-schema.md)
|
||||
- [Writing an Addon](./development/writing-an-addon.md)
|
||||
|
||||
# Silo Server
|
||||
|
||||
@@ -76,4 +77,5 @@
|
||||
- [OriginSelectorWidget](./reference/cpp-origin-selector-widget.md)
|
||||
- [FileOriginPython Bridge](./reference/cpp-file-origin-python.md)
|
||||
- [Creating a Custom Origin (C++)](./reference/cpp-custom-origin-guide.md)
|
||||
- [KCSDK Python API](./reference/kcsdk-python.md)
|
||||
- [KCSolve Python API](./reference/kcsolve-python.md)
|
||||
|
||||
283
docs/src/development/writing-an-addon.md
Normal file
283
docs/src/development/writing-an-addon.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# Writing an Addon
|
||||
|
||||
This guide walks through creating a Kindred Create addon from scratch. Addons are Python packages in the `mods/` directory that extend Create with commands, panels, and UI modifications through the SDK.
|
||||
|
||||
## Addon structure
|
||||
|
||||
A minimal addon has this layout:
|
||||
|
||||
```
|
||||
mods/my-addon/
|
||||
├── package.xml # Manifest (required)
|
||||
├── Init.py # Console-phase bootstrap
|
||||
├── InitGui.py # GUI-phase bootstrap
|
||||
└── my_addon/
|
||||
├── __init__.py
|
||||
└── commands.py # Your commands
|
||||
```
|
||||
|
||||
## Step 1: Create the manifest
|
||||
|
||||
Every addon needs a `package.xml` with a `<kindred>` extension block. The `<workbench>` tag is required for `InitGui.py` to be loaded, even if your addon doesn't register a workbench.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package format="1">
|
||||
<name>my-addon</name>
|
||||
<description>My custom addon for Kindred Create.</description>
|
||||
<version>0.1.0</version>
|
||||
<maintainer email="you@example.com">Your Name</maintainer>
|
||||
<license>LGPL-2.1-or-later</license>
|
||||
|
||||
<!-- Required for InitGui.py loading -->
|
||||
<workbench>
|
||||
<classname>MyAddonWorkbench</classname>
|
||||
</workbench>
|
||||
|
||||
<kindred>
|
||||
<min_create_version>0.1.5</min_create_version>
|
||||
<load_priority>70</load_priority>
|
||||
<pure_python>true</pure_python>
|
||||
<dependencies>
|
||||
<dependency>sdk</dependency>
|
||||
</dependencies>
|
||||
</kindred>
|
||||
</package>
|
||||
```
|
||||
|
||||
### Priority ranges
|
||||
|
||||
| Range | Use |
|
||||
|-------|-----|
|
||||
| 0-9 | SDK and core infrastructure |
|
||||
| 10-49 | Foundation addons |
|
||||
| 50-99 | Standard addons (ztools, silo) |
|
||||
| 100+ | Optional/user addons |
|
||||
|
||||
See [Package.xml Schema Extensions](./package-xml-schema.md) for the full schema.
|
||||
|
||||
## Step 2: Console bootstrap (Init.py)
|
||||
|
||||
`Init.py` runs during FreeCAD's console initialization, before the GUI exists. Use it for non-GUI setup.
|
||||
|
||||
```python
|
||||
import FreeCAD
|
||||
|
||||
FreeCAD.Console.PrintLog("my-addon: loaded (console)\n")
|
||||
```
|
||||
|
||||
## Step 3: GUI bootstrap (InitGui.py)
|
||||
|
||||
`InitGui.py` runs when the GUI is ready. This is where you register commands, contexts, panels, and overlays.
|
||||
|
||||
```python
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
FreeCAD.Console.PrintLog("my-addon: loaded (GUI)\n")
|
||||
|
||||
|
||||
def _deferred_setup():
|
||||
"""Register commands and UI after the main window is ready."""
|
||||
from my_addon import commands
|
||||
commands.register()
|
||||
|
||||
|
||||
from PySide.QtCore import QTimer
|
||||
QTimer.singleShot(2000, _deferred_setup)
|
||||
```
|
||||
|
||||
Deferred setup via `QTimer.singleShot()` avoids timing issues during startup. See [Create Module Bootstrap](../reference/create-module-bootstrap.md) for the full timer cascade.
|
||||
|
||||
## Step 4: Register commands
|
||||
|
||||
FreeCAD commands use `Gui.addCommand()`. This is a stable FreeCAD API and does not need SDK wrappers.
|
||||
|
||||
```python
|
||||
# my_addon/commands.py
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
|
||||
class MyCommand:
|
||||
def GetResources(self):
|
||||
return {
|
||||
"MenuText": "My Command",
|
||||
"ToolTip": "Does something useful",
|
||||
}
|
||||
|
||||
def Activated(self):
|
||||
FreeCAD.Console.PrintMessage("My command activated\n")
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
|
||||
|
||||
def register():
|
||||
FreeCADGui.addCommand("MyAddon_MyCommand", MyCommand())
|
||||
```
|
||||
|
||||
## Step 5: Inject into editing contexts
|
||||
|
||||
Use the SDK to add your commands to existing toolbar contexts, rather than creating a standalone workbench.
|
||||
|
||||
```python
|
||||
from kindred_sdk import inject_commands
|
||||
|
||||
# Add your command to the PartDesign body context toolbar
|
||||
inject_commands("partdesign.body", "PartDesign", ["MyAddon_MyCommand"])
|
||||
```
|
||||
|
||||
Built-in contexts you can inject into: `sketcher.edit`, `assembly.edit`, `partdesign.feature`, `partdesign.body`, `assembly.idle`, `spreadsheet`, `empty_document`, `no_document`.
|
||||
|
||||
## Step 6: Register a custom context
|
||||
|
||||
If your addon has its own editing mode, register a context to control which toolbars are visible.
|
||||
|
||||
```python
|
||||
from kindred_sdk import register_context
|
||||
|
||||
def _is_my_object_in_edit():
|
||||
import FreeCADGui
|
||||
doc = FreeCADGui.activeDocument()
|
||||
if doc and doc.getInEdit():
|
||||
obj = doc.getInEdit().Object
|
||||
return obj.isDerivedFrom("App::FeaturePython") and hasattr(obj, "MyAddonType")
|
||||
return False
|
||||
|
||||
register_context(
|
||||
"myaddon.edit",
|
||||
"Editing {name}",
|
||||
"#f9e2af", # Catppuccin yellow
|
||||
["MyAddonToolbar", "StandardViews"],
|
||||
_is_my_object_in_edit,
|
||||
priority=55,
|
||||
)
|
||||
```
|
||||
|
||||
## Step 7: Register a dock panel
|
||||
|
||||
For panels that live in the dock area (like Silo's database panels), use the SDK panel registration.
|
||||
|
||||
### Simple approach (recommended for most addons)
|
||||
|
||||
```python
|
||||
from kindred_sdk import register_dock_panel
|
||||
|
||||
def _create_my_panel():
|
||||
from PySide import QtWidgets
|
||||
widget = QtWidgets.QTreeWidget()
|
||||
widget.setHeaderLabels(["Name", "Value"])
|
||||
return widget
|
||||
|
||||
register_dock_panel(
|
||||
"MyAddonPanel", # unique object name
|
||||
"My Addon", # title bar text
|
||||
_create_my_panel,
|
||||
area="right",
|
||||
delay_ms=3000, # create 3 seconds after startup
|
||||
)
|
||||
```
|
||||
|
||||
### Advanced approach (IPanelProvider)
|
||||
|
||||
For full control over panel behavior, implement the `IPanelProvider` interface directly:
|
||||
|
||||
```python
|
||||
import kcsdk
|
||||
|
||||
class MyPanelProvider(kcsdk.IPanelProvider):
|
||||
def id(self):
|
||||
return "myaddon.inspector"
|
||||
|
||||
def title(self):
|
||||
return "Inspector"
|
||||
|
||||
def create_widget(self):
|
||||
from PySide import QtWidgets
|
||||
tree = QtWidgets.QTreeWidget()
|
||||
tree.setHeaderLabels(["Property", "Value"])
|
||||
return tree
|
||||
|
||||
def preferred_area(self):
|
||||
return kcsdk.DockArea.Left
|
||||
|
||||
def context_affinity(self):
|
||||
return "myaddon.edit" # only visible in your custom context
|
||||
|
||||
# Register and create
|
||||
kcsdk.register_panel(MyPanelProvider())
|
||||
kcsdk.create_panel("myaddon.inspector")
|
||||
```
|
||||
|
||||
## Step 8: Use theme colors
|
||||
|
||||
The SDK provides the Catppuccin Mocha palette for consistent theming.
|
||||
|
||||
```python
|
||||
from kindred_sdk import get_theme_tokens, load_palette
|
||||
|
||||
# Quick lookup
|
||||
tokens = get_theme_tokens()
|
||||
blue = tokens["blue"] # "#89b4fa"
|
||||
error = tokens["error"] # mapped from semantic role
|
||||
|
||||
# Full palette object
|
||||
palette = load_palette()
|
||||
palette.get("accent.primary") # semantic role lookup
|
||||
palette.get("mauve") # direct color lookup
|
||||
|
||||
# Format QSS templates
|
||||
qss = palette.format_qss("background: {base}; color: {text};")
|
||||
```
|
||||
|
||||
## Complete example
|
||||
|
||||
Putting it all together, here's a minimal addon that adds a command and a dock panel:
|
||||
|
||||
```
|
||||
mods/my-addon/
|
||||
├── package.xml
|
||||
├── Init.py
|
||||
├── InitGui.py
|
||||
└── my_addon/
|
||||
├── __init__.py
|
||||
└── commands.py
|
||||
```
|
||||
|
||||
**InitGui.py:**
|
||||
```python
|
||||
import FreeCAD
|
||||
|
||||
def _setup():
|
||||
from my_addon.commands import register
|
||||
from kindred_sdk import inject_commands, register_dock_panel
|
||||
|
||||
register()
|
||||
inject_commands("partdesign.body", "PartDesign", ["MyAddon_MyCommand"])
|
||||
|
||||
from PySide import QtWidgets
|
||||
register_dock_panel(
|
||||
"MyAddonPanel", "My Addon",
|
||||
lambda: QtWidgets.QLabel("Hello from my addon"),
|
||||
area="right", delay_ms=0,
|
||||
)
|
||||
|
||||
from PySide.QtCore import QTimer
|
||||
QTimer.singleShot(2500, _setup)
|
||||
```
|
||||
|
||||
## Key patterns
|
||||
|
||||
- **Use `kindred_sdk` wrappers** instead of `FreeCADGui.*` internals. The SDK handles fallback and error logging.
|
||||
- **Defer initialization** with `QTimer.singleShot()` to avoid startup timing issues.
|
||||
- **Declare `<dependency>sdk</dependency>`** in your manifest to ensure the SDK loads before your addon.
|
||||
- **Inject commands into existing contexts** rather than creating standalone workbenches. This gives users a unified toolbar experience.
|
||||
- **Use theme tokens** from the palette for colors. Don't hardcode hex values.
|
||||
|
||||
## Related
|
||||
|
||||
- [KCSDK Python API Reference](../reference/kcsdk-python.md)
|
||||
- [Package.xml Schema Extensions](./package-xml-schema.md)
|
||||
- [Create Module Bootstrap](../reference/create-module-bootstrap.md)
|
||||
252
docs/src/reference/kcsdk-python.md
Normal file
252
docs/src/reference/kcsdk-python.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# KCSDK Python API Reference
|
||||
|
||||
The `kcsdk` module provides Python access to the Kindred Create addon SDK. It is built with pybind11 and installed alongside the Create module.
|
||||
|
||||
The `kindred_sdk` package (`mods/sdk/kindred_sdk/`) provides convenience wrappers that route through `kcsdk` when available, falling back to legacy `FreeCADGui.*` bindings. Addons should prefer `kindred_sdk` over importing `kcsdk` directly.
|
||||
|
||||
```python
|
||||
import kcsdk # C++ bindings (low-level)
|
||||
import kindred_sdk # Python wrappers (recommended)
|
||||
```
|
||||
|
||||
## Module constants
|
||||
|
||||
| Name | Value | Description |
|
||||
|------|-------|-------------|
|
||||
| `API_VERSION_MAJOR` | `1` | KCSDK API major version |
|
||||
|
||||
## Enums
|
||||
|
||||
### DockArea
|
||||
|
||||
Dock widget placement area. Values match `Qt::DockWidgetArea`.
|
||||
|
||||
| Value | Integer | Description |
|
||||
|-------|---------|-------------|
|
||||
| `DockArea.Left` | 1 | Left dock area |
|
||||
| `DockArea.Right` | 2 | Right dock area |
|
||||
| `DockArea.Top` | 4 | Top dock area |
|
||||
| `DockArea.Bottom` | 8 | Bottom dock area |
|
||||
|
||||
### PanelPersistence
|
||||
|
||||
Whether a dock panel's visibility survives application restarts.
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `PanelPersistence.Session` | Visible until application close |
|
||||
| `PanelPersistence.Persistent` | Saved to preferences and restored on next launch |
|
||||
|
||||
## Editing Context API
|
||||
|
||||
These functions manage the context-aware UI system. Contexts control which toolbars are visible based on the current editing state.
|
||||
|
||||
### register_context(id, label, color, toolbars, match, priority=50)
|
||||
|
||||
Register an editing context.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `id` | `str` | Unique identifier (e.g. `"myaddon.edit"`) |
|
||||
| `label` | `str` | Display label template. Supports `{name}` placeholder |
|
||||
| `color` | `str` | Hex color for breadcrumb (e.g. `"#f38ba8"`) |
|
||||
| `toolbars` | `list[str]` | Toolbar names to show when active |
|
||||
| `match` | `callable` | Zero-arg callable returning `True` when active |
|
||||
| `priority` | `int` | Higher values checked first. Default 50 |
|
||||
|
||||
```python
|
||||
kcsdk.register_context(
|
||||
"myworkbench.edit",
|
||||
"Editing {name}",
|
||||
"#89b4fa",
|
||||
["MyToolbar", "StandardViews"],
|
||||
lambda: is_my_object_in_edit(),
|
||||
priority=60,
|
||||
)
|
||||
```
|
||||
|
||||
### unregister_context(id)
|
||||
|
||||
Remove a previously registered editing context.
|
||||
|
||||
### register_overlay(id, toolbars, match)
|
||||
|
||||
Register an editing overlay. Overlays add toolbars to whatever context is currently active when `match()` returns `True`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `id` | `str` | Unique overlay identifier |
|
||||
| `toolbars` | `list[str]` | Toolbar names to append |
|
||||
| `match` | `callable` | Zero-arg callable returning `True` when the overlay applies |
|
||||
|
||||
### unregister_overlay(id)
|
||||
|
||||
Remove a previously registered overlay.
|
||||
|
||||
### inject_commands(context_id, toolbar_name, commands)
|
||||
|
||||
Inject additional commands into an existing context's toolbar.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `context_id` | `str` | Target context identifier |
|
||||
| `toolbar_name` | `str` | Toolbar within that context |
|
||||
| `commands` | `list[str]` | Command names to add |
|
||||
|
||||
```python
|
||||
kcsdk.inject_commands("partdesign.body", "PartDesign", ["MyAddon_CustomFeature"])
|
||||
```
|
||||
|
||||
### current_context()
|
||||
|
||||
Return the current editing context as a dict, or `None`.
|
||||
|
||||
Keys: `id`, `label`, `color`, `toolbars`, `breadcrumb`, `breadcrumbColors`.
|
||||
|
||||
### refresh()
|
||||
|
||||
Force re-resolution of the editing context.
|
||||
|
||||
## Panel Provider API
|
||||
|
||||
These functions manage dock panel registration. Panels are created through the `IPanelProvider` interface and managed by `DockWindowManager`.
|
||||
|
||||
### IPanelProvider
|
||||
|
||||
Abstract base class for dock panel providers. Subclass in Python to create custom panels.
|
||||
|
||||
Three methods must be implemented:
|
||||
|
||||
```python
|
||||
class MyPanel(kcsdk.IPanelProvider):
|
||||
def id(self):
|
||||
return "myaddon.panel"
|
||||
|
||||
def title(self):
|
||||
return "My Panel"
|
||||
|
||||
def create_widget(self):
|
||||
from PySide import QtWidgets
|
||||
label = QtWidgets.QLabel("Hello from my addon")
|
||||
return label
|
||||
```
|
||||
|
||||
Optional methods with defaults:
|
||||
|
||||
| Method | Return type | Default | Description |
|
||||
|--------|-------------|---------|-------------|
|
||||
| `preferred_area()` | `DockArea` | `DockArea.Right` | Dock placement area |
|
||||
| `persistence()` | `PanelPersistence` | `PanelPersistence.Session` | Visibility persistence |
|
||||
| `context_affinity()` | `str` | `""` (always visible) | Only show in named context |
|
||||
|
||||
```python
|
||||
class SidePanel(kcsdk.IPanelProvider):
|
||||
def id(self): return "myaddon.side"
|
||||
def title(self): return "Side Panel"
|
||||
def create_widget(self):
|
||||
from PySide import QtWidgets
|
||||
return QtWidgets.QTreeWidget()
|
||||
def preferred_area(self):
|
||||
return kcsdk.DockArea.Left
|
||||
def context_affinity(self):
|
||||
return "partdesign.body" # only visible in PartDesign body context
|
||||
```
|
||||
|
||||
### register_panel(provider)
|
||||
|
||||
Register a dock panel provider. The provider is stored in the registry until `create_panel()` is called to instantiate the actual dock widget.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `provider` | `IPanelProvider` | Panel provider instance |
|
||||
|
||||
### unregister_panel(id)
|
||||
|
||||
Remove a registered panel provider and destroy its dock widget if created.
|
||||
|
||||
### create_panel(id)
|
||||
|
||||
Instantiate the dock widget for a registered panel. Calls the provider's `create_widget()` once and embeds the result in a `QDockWidget` via `DockWindowManager`. Skips silently if the panel already exists.
|
||||
|
||||
### create_all_panels()
|
||||
|
||||
Instantiate dock widgets for all registered panels.
|
||||
|
||||
### registered_panels()
|
||||
|
||||
Return IDs of all registered panel providers as `list[str]`.
|
||||
|
||||
### available()
|
||||
|
||||
Return names of all registered providers (across all provider types) as `list[str]`.
|
||||
|
||||
## `kindred_sdk` Convenience Wrappers
|
||||
|
||||
The `kindred_sdk` Python package wraps the `kcsdk` C++ module with input validation, error handling, and fallback to legacy APIs.
|
||||
|
||||
### kindred_sdk.register_dock_panel(object_name, title, widget_factory, area="right", delay_ms=0)
|
||||
|
||||
High-level dock panel registration. Creates an anonymous `IPanelProvider` internally and schedules creation via `QTimer`.
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `object_name` | `str` | | Qt object name (used as panel ID) |
|
||||
| `title` | `str` | | Dock widget title |
|
||||
| `widget_factory` | `callable` | | Zero-arg callable returning a `QWidget` |
|
||||
| `area` | `str` | `"right"` | `"left"`, `"right"`, `"top"`, or `"bottom"` |
|
||||
| `delay_ms` | `int` | `0` | Defer creation by this many milliseconds |
|
||||
|
||||
```python
|
||||
from kindred_sdk import register_dock_panel
|
||||
from PySide import QtWidgets
|
||||
|
||||
register_dock_panel(
|
||||
"MyAddonPanel",
|
||||
"My Addon",
|
||||
lambda: QtWidgets.QLabel("Hello"),
|
||||
area="left",
|
||||
delay_ms=2000,
|
||||
)
|
||||
```
|
||||
|
||||
### Other `kindred_sdk` Wrappers
|
||||
|
||||
These mirror the `kcsdk` functions with added type validation and try/except error handling:
|
||||
|
||||
| Function | Maps to |
|
||||
|----------|---------|
|
||||
| `kindred_sdk.register_context()` | `kcsdk.register_context()` |
|
||||
| `kindred_sdk.unregister_context()` | `kcsdk.unregister_context()` |
|
||||
| `kindred_sdk.register_overlay()` | `kcsdk.register_overlay()` |
|
||||
| `kindred_sdk.unregister_overlay()` | `kcsdk.unregister_overlay()` |
|
||||
| `kindred_sdk.inject_commands()` | `kcsdk.inject_commands()` |
|
||||
| `kindred_sdk.current_context()` | `kcsdk.current_context()` |
|
||||
| `kindred_sdk.refresh_context()` | `kcsdk.refresh()` |
|
||||
| `kindred_sdk.register_origin()` | `FreeCADGui.addOrigin()` |
|
||||
| `kindred_sdk.unregister_origin()` | `FreeCADGui.removeOrigin()` |
|
||||
| `kindred_sdk.get_theme_tokens()` | YAML palette lookup |
|
||||
| `kindred_sdk.load_palette()` | `Palette` object from YAML |
|
||||
| `kindred_sdk.create_version()` | Kindred Create version string |
|
||||
| `kindred_sdk.freecad_version()` | FreeCAD version tuple |
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Python Addon Code
|
||||
|
|
||||
kindred_sdk (mods/sdk/) <- convenience wrappers + validation
|
||||
|
|
||||
kcsdk.so (pybind11 module) <- C++ API bindings
|
||||
|
|
||||
libKCSDK.so (C++ shared library) <- SDKRegistry + provider interfaces
|
||||
|
|
||||
FreeCADGui (EditingContextResolver, DockWindowManager, OriginManager, ...)
|
||||
```
|
||||
|
||||
When `kcsdk` is not available (console mode, build not installed), `kindred_sdk` falls back to legacy `FreeCADGui.*` Python bindings.
|
||||
|
||||
## Related
|
||||
|
||||
- [Writing an Addon](../development/writing-an-addon.md)
|
||||
- [Package.xml Schema Extensions](../development/package-xml-schema.md)
|
||||
- [Create Module Bootstrap](./create-module-bootstrap.md)
|
||||
Reference in New Issue
Block a user