- IPanelProvider: abstract interface for dock panels with PySide widget bridging - PyIPanelProvider/PyProviderHolder: pybind11 trampoline + GIL-safe holder - WidgetBridge: PySide QWidget → C++ QWidget* conversion via Shiboken - SDKRegistry: panel registration, creation, and lifecycle management - ThemeEngine: C++ singleton with minimal YAML parser, palette cache, getColor/allTokens/formatQss matching Python Palette API - kcsdk bindings: DockArea, PanelPersistence enums, panel functions, theme_color, theme_tokens, format_qss, load_palette - dock.py: kcsdk delegation with FreeCADGui fallback - theme.py: kcsdk delegation with Python YAML fallback
145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
"""Dock panel registration helper.
|
|
|
|
Routes through the ``kcsdk`` C++ module (IPanelProvider / DockWindowManager)
|
|
when available, falling back to direct PySide QDockWidget creation for
|
|
backwards compatibility.
|
|
"""
|
|
|
|
import FreeCAD
|
|
|
|
# Try to import the C++ SDK module; None if not yet built/installed.
|
|
try:
|
|
import kcsdk as _kcsdk
|
|
except ImportError:
|
|
_kcsdk = None
|
|
|
|
_AREA_MAP = {
|
|
"left": 1, # Qt.LeftDockWidgetArea / DockArea.Left
|
|
"right": 2, # Qt.RightDockWidgetArea / DockArea.Right
|
|
"top": 4, # Qt.TopDockWidgetArea / DockArea.Top
|
|
"bottom": 8, # Qt.BottomDockWidgetArea / DockArea.Bottom
|
|
}
|
|
|
|
_DOCK_AREA_MAP = None # lazily populated from kcsdk
|
|
|
|
|
|
def _get_dock_area(area_str):
|
|
"""Convert area string to kcsdk.DockArea enum value."""
|
|
global _DOCK_AREA_MAP
|
|
if _DOCK_AREA_MAP is None:
|
|
_DOCK_AREA_MAP = {
|
|
"left": _kcsdk.DockArea.Left,
|
|
"right": _kcsdk.DockArea.Right,
|
|
"top": _kcsdk.DockArea.Top,
|
|
"bottom": _kcsdk.DockArea.Bottom,
|
|
}
|
|
return _DOCK_AREA_MAP.get(area_str)
|
|
|
|
|
|
def register_dock_panel(object_name, title, widget_factory, area="right", delay_ms=0):
|
|
"""Register a dock panel, optionally deferred.
|
|
|
|
Parameters
|
|
----------
|
|
object_name : str
|
|
Qt object name for duplicate prevention.
|
|
title : str
|
|
Dock widget title bar text.
|
|
widget_factory : callable
|
|
Zero-argument callable returning a ``QWidget``. Called only
|
|
when the panel is actually created (after *delay_ms*).
|
|
area : str, optional
|
|
Dock area: ``"left"``, ``"right"``, ``"top"``, or ``"bottom"``.
|
|
Default ``"right"``.
|
|
delay_ms : int, optional
|
|
Milliseconds to wait before creating the panel. Default 0
|
|
(immediate, but still posted to the event loop).
|
|
"""
|
|
if not isinstance(object_name, str):
|
|
raise TypeError(f"object_name must be str, got {type(object_name).__name__}")
|
|
if not callable(widget_factory):
|
|
raise TypeError("widget_factory must be callable")
|
|
|
|
if area not in _AREA_MAP:
|
|
raise ValueError(f"area must be one of {list(_AREA_MAP)}, got {area!r}")
|
|
|
|
if _kcsdk is not None:
|
|
_register_via_kcsdk(object_name, title, widget_factory, area, delay_ms)
|
|
else:
|
|
_register_via_pyside(object_name, title, widget_factory, area, delay_ms)
|
|
|
|
|
|
def _register_via_kcsdk(object_name, title, widget_factory, area, delay_ms):
|
|
"""Register using the C++ SDK panel provider system."""
|
|
dock_area = _get_dock_area(area)
|
|
|
|
class _AnonymousProvider(_kcsdk.IPanelProvider):
|
|
def id(self):
|
|
return object_name
|
|
|
|
def title(self):
|
|
return title
|
|
|
|
def create_widget(self):
|
|
return widget_factory()
|
|
|
|
def preferred_area(self):
|
|
return dock_area
|
|
|
|
try:
|
|
_kcsdk.register_panel(_AnonymousProvider())
|
|
|
|
def _create():
|
|
try:
|
|
_kcsdk.create_panel(object_name)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(
|
|
f"kindred_sdk: Panel '{object_name}' creation failed: {e}\n"
|
|
)
|
|
|
|
from PySide.QtCore import QTimer
|
|
|
|
QTimer.singleShot(max(0, delay_ms), _create)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(
|
|
f"kindred_sdk: kcsdk panel registration failed for '{object_name}', "
|
|
f"falling back: {e}\n"
|
|
)
|
|
_register_via_pyside(object_name, title, widget_factory, area, delay_ms)
|
|
|
|
|
|
def _register_via_pyside(object_name, title, widget_factory, area, delay_ms):
|
|
"""Legacy fallback: create dock widget directly via PySide."""
|
|
qt_area = _AREA_MAP[area]
|
|
|
|
def _create():
|
|
try:
|
|
import FreeCADGui
|
|
from PySide import QtCore, QtWidgets
|
|
|
|
mw = FreeCADGui.getMainWindow()
|
|
if mw is None:
|
|
return
|
|
|
|
if mw.findChild(QtWidgets.QDockWidget, object_name):
|
|
return
|
|
|
|
widget = widget_factory()
|
|
panel = QtWidgets.QDockWidget(title, mw)
|
|
panel.setObjectName(object_name)
|
|
panel.setWidget(widget)
|
|
mw.addDockWidget(QtCore.Qt.DockWidgetArea(qt_area), panel)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(
|
|
f"kindred_sdk: Dock panel '{object_name}' skipped: {e}\n"
|
|
)
|
|
|
|
try:
|
|
from PySide.QtCore import QTimer
|
|
|
|
QTimer.singleShot(max(0, delay_ms), _create)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(
|
|
f"kindred_sdk: Could not schedule dock panel '{object_name}': {e}\n"
|
|
)
|