From dbb2146b2fe2da5d64e9af0727b5573b142f2af9 Mon Sep 17 00:00:00 2001 From: Ange-Marie MAURIN Date: Sun, 11 Jan 2026 22:35:42 +0100 Subject: [PATCH] clean up imports and switch to absolute imports --- .gitignore | 8 +- README.md | 14 +- synodetik/__init__.py | 9 +- synodetik/core/__init__.py | 19 +- synodetik/core/events/__init__.py | 2 +- synodetik/core/library.py | 2 +- synodetik/core/model/__init__.py | 6 +- synodetik/core/model/machine.py | 10 +- synodetik/core/port.py | 4 +- synodetik/core/synoptik.py | 8 +- synodetik/events.py | 2 +- synodetik/gui/__init__.py | 8 +- synodetik/gui/app.py | 14 +- synodetik/gui/components/__init__.py | 6 +- synodetik/gui/components/node_editor.py | 4 +- synodetik/gui/components/palette.py | 4 +- synodetik/gui/components/properties.py | 4 +- synodetik/gui/dialogs/__init__.py | 4 +- synodetik/gui/dialogs/flux_editor.py | 8 +- synodetik/gui/dialogs/template_editor.py | 8 +- synodetik/gui/flux_editor.py | 407 ----------------------- synodetik/gui/template_editor.py | 250 -------------- synodetik/modules/__init__.py | 4 +- synodetik/modules/base.py | 2 +- synodetik/modules/manager.py | 2 +- synodetik/utils/__init__.py | 2 +- synodetik/utils/serialization.py | 2 +- 27 files changed, 82 insertions(+), 731 deletions(-) delete mode 100644 synodetik/gui/flux_editor.py delete mode 100644 synodetik/gui/template_editor.py diff --git a/.gitignore b/.gitignore index 64d9b21..c8c4913 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,10 @@ instance/ # Scrapy stuff: .scrapy +#vscode settings +.vscode/ + + # Sphinx documentation docs/_build/ @@ -174,6 +178,8 @@ cython_debug/ # PyPI configuration file .pypirc +prompt.md + Powered by Gitea Version: 1.24.4 @@ -182,4 +188,4 @@ Page: Template: 3ms Licenses -API \ No newline at end of file +API diff --git a/README.md b/README.md index 8ec5dde..5703b61 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ # synodetik -Modular audiovisual synoptic node editor. \ No newline at end of file +Modular audiovisual synoptic node editor. + +## installation + +Just git clone the project. Open a terminal in the cloned folder. + +``` shell + + + +``` + +## module development \ No newline at end of file diff --git a/synodetik/__init__.py b/synodetik/__init__.py index cddb137..98c7b66 100644 --- a/synodetik/__init__.py +++ b/synodetik/__init__.py @@ -4,7 +4,7 @@ Synodetik - Video/Audio synoptic editor. A node-based visual editor for designing and documenting signal routing in professional AV environments. """ -from .core import ( +from synodetik.core import ( # Models FluxCategory, create_flux_category, Flux, create_flux_type, @@ -22,11 +22,9 @@ from .core import ( # Events EventType, Event, EventDispatcher, get_dispatcher, - - ) -from .modules import ( +from synodetik.modules import ( Module, ModuleInfo, MenuItem, ModuleError, ModuleManager ) @@ -53,9 +51,6 @@ __all__ = [ 'EventType', 'Event', 'EventDispatcher', 'get_dispatcher', - # Registry - 'Registry', 'get_registry', - # Modules 'Module', 'ModuleInfo', 'MenuItem', 'ModuleError', 'ModuleManager', diff --git a/synodetik/core/__init__.py b/synodetik/core/__init__.py index 4b7e99a..f7007f4 100644 --- a/synodetik/core/__init__.py +++ b/synodetik/core/__init__.py @@ -15,27 +15,25 @@ Library: Services: - ConfigManager: Display and editor settings - EventDispatcher: Inter-module communication - - Registry: Service locator """ # Models -from .model import ( +from synodetik.core.model import ( FluxCategory, create_flux_category, Flux, create_flux_type, Machine, create_machine ) -from .port import Port, PortDirection, create_input, create_output, create_full_duplex -from .connection import Connection -from .synoptik import Synoptik +from synodetik.core.port import Port, PortDirection, create_input, create_output, create_full_duplex +from synodetik.core.connection import Connection +from synodetik.core.synoptik import Synoptik # Library -from .library import Library, get_library +from synodetik.core.library import Library, get_library # Config -from .config import ConfigManager, AppConfig, get_config_manager +from synodetik.core.config import ConfigManager, AppConfig, get_config_manager # Events -from .events import EventDispatcher, EventType, Event, get_dispatcher - +from synodetik.core.events import EventDispatcher, EventType, Event, get_dispatcher __all__ = [ @@ -54,7 +52,4 @@ __all__ = [ # Events "EventDispatcher", "EventType", "Event", "get_dispatcher", - - # Registry - "Registry", "get_registry", ] diff --git a/synodetik/core/events/__init__.py b/synodetik/core/events/__init__.py index eb9516d..2fedbd3 100644 --- a/synodetik/core/events/__init__.py +++ b/synodetik/core/events/__init__.py @@ -1,7 +1,7 @@ """ Event system for decoupled component communication. """ -from .dispatcher import ( +from synodetik.core.events.dispatcher import ( EventType, Event, EventHandler, diff --git a/synodetik/core/library.py b/synodetik/core/library.py index 9276aa5..6ee44f6 100644 --- a/synodetik/core/library.py +++ b/synodetik/core/library.py @@ -7,7 +7,7 @@ All items are stored as JSON files in ~/.synodetik/ from pathlib import Path from typing import Optional -from .model import Machine, Flux, FluxCategory +from synodetik.core.model import Machine, Flux, FluxCategory class Library: diff --git a/synodetik/core/model/__init__.py b/synodetik/core/model/__init__.py index e283161..7c4f691 100644 --- a/synodetik/core/model/__init__.py +++ b/synodetik/core/model/__init__.py @@ -6,9 +6,9 @@ Provides the main domain objects: - Flux: Signal/connection type definition - Machine: Equipment with input/output ports """ -from .flux_category import FluxCategory, create_flux_category -from .flux import Flux, create_flux_type -from .machine import Machine, create_machine +from synodetik.core.model.flux_category import FluxCategory, create_flux_category +from synodetik.core.model.flux import Flux, create_flux_type +from synodetik.core.model.machine import Machine, create_machine __all__ = [ "FluxCategory", "create_flux_category", diff --git a/synodetik/core/model/machine.py b/synodetik/core/model/machine.py index adf2cfc..ffe08b4 100644 --- a/synodetik/core/model/machine.py +++ b/synodetik/core/model/machine.py @@ -11,8 +11,8 @@ import uuid import json if TYPE_CHECKING: - from ..port import Port - from .flux import Flux + from synodetik.core.port import Port + from synodetik.core.model.flux import Flux @dataclass @@ -58,7 +58,7 @@ class Machine: def add_input(self, name: str, flux_type: "Flux") -> "Port": """Add an input port and return it.""" - from ..port import create_input + from synodetik.core.port import create_input port = create_input(name, flux_type, index=len(self.inputs)) port.machine_id = self.id self.inputs.append(port) @@ -66,7 +66,7 @@ class Machine: def add_output(self, name: str, flux_type: "Flux") -> "Port": """Add an output port and return it.""" - from ..port import create_output + from synodetik.core.port import create_output port = create_output(name, flux_type, index=len(self.outputs)) port.machine_id = self.id self.outputs.append(port) @@ -133,7 +133,7 @@ class Machine: @classmethod def from_dict(cls, data: dict) -> "Machine": """Deserialize from dictionary.""" - from ..port import Port + from synodetik.core.port import Port machine = cls( id=data.get("id", str(uuid.uuid4())), diff --git a/synodetik/core/port.py b/synodetik/core/port.py index fb02def..7e054b4 100644 --- a/synodetik/core/port.py +++ b/synodetik/core/port.py @@ -10,7 +10,7 @@ from typing import Optional, TYPE_CHECKING import uuid if TYPE_CHECKING: - from .model.flux import Flux + from synodetik.core.model.flux import Flux class PortDirection(Enum): @@ -69,7 +69,7 @@ class Port: @classmethod def from_dict(cls, data: dict) -> "Port": """Deserialize from dictionary.""" - from .model.flux import Flux + from synodetik.core.model.flux import Flux return cls( id=data.get("id", str(uuid.uuid4())), diff --git a/synodetik/core/synoptik.py b/synodetik/core/synoptik.py index 54a005d..43bbf6b 100644 --- a/synodetik/core/synoptik.py +++ b/synodetik/core/synoptik.py @@ -9,10 +9,10 @@ from typing import Optional from datetime import datetime import uuid -from .model.machine import Machine -from .connection import Connection -from .port import Port -from .model.flux import Flux +from synodetik.core.model.machine import Machine +from synodetik.core.connection import Connection +from synodetik.core.port import Port +from synodetik.core.model.flux import Flux @dataclass diff --git a/synodetik/events.py b/synodetik/events.py index 7f463d5..bf819f4 100644 --- a/synodetik/events.py +++ b/synodetik/events.py @@ -5,7 +5,7 @@ This module re-exports the event system from core.events to maintain backward compatibility with imports like: from synodetik.events import EventType, get_dispatcher """ -from .core.events import ( +from synodetik.core.events import ( EventType, Event, EventHandler, diff --git a/synodetik/gui/__init__.py b/synodetik/gui/__init__.py index 90dab1a..1997755 100644 --- a/synodetik/gui/__init__.py +++ b/synodetik/gui/__init__.py @@ -1,13 +1,13 @@ """ GUI module - DearPyGui interface components. """ -from .app import SynoptikApp, create_app -from .themes import ( +from synodetik.gui.app import SynoptikApp, create_app +from synodetik.gui.themes import ( DarkTheme, LightTheme, CATEGORY_COLORS, hex_to_rgba, color_with_opacity ) -from .components import PalettePanel, PropertiesPanel, NodeEditorPanel -from .dialogs import ( +from synodetik.gui.components import PalettePanel, PropertiesPanel, NodeEditorPanel +from synodetik.gui.dialogs import ( MachineEditor, FluxEditor, FluxManager, CategoryEditor ) diff --git a/synodetik/gui/app.py b/synodetik/gui/app.py index 5c8d1f2..f18be26 100644 --- a/synodetik/gui/app.py +++ b/synodetik/gui/app.py @@ -8,19 +8,19 @@ import dearpygui.dearpygui as dpg from pathlib import Path from typing import Optional, List -from ..core import ( +from synodetik.core import ( Synoptik, Machine, Library, get_library, get_config_manager ) -from ..utils import serialization -from ..events import EventType, get_dispatcher -from ..modules import ModuleManager +from synodetik.utils import serialization +from synodetik.events import EventType, get_dispatcher +from synodetik.modules import ModuleManager from synodetik.modules.base import MenuItem -from .themes import DarkTheme, hex_to_rgba -from .components import PalettePanel, PropertiesPanel, NodeEditorPanel -from .dialogs import MachineEditor, FluxManager, CategoryEditor +from synodetik.gui.themes import DarkTheme, hex_to_rgba +from synodetik.gui.components import PalettePanel, PropertiesPanel, NodeEditorPanel +from synodetik.gui.dialogs import MachineEditor, FluxManager, CategoryEditor class SynoptikApp: diff --git a/synodetik/gui/components/__init__.py b/synodetik/gui/components/__init__.py index 35d31fd..34a249c 100644 --- a/synodetik/gui/components/__init__.py +++ b/synodetik/gui/components/__init__.py @@ -1,8 +1,8 @@ """ GUI components package. """ -from .palette import PalettePanel -from .properties import PropertiesPanel -from .node_editor import NodeEditorPanel +from synodetik.gui.components.palette import PalettePanel +from synodetik.gui.components.properties import PropertiesPanel +from synodetik.gui.components.node_editor import NodeEditorPanel __all__ = ['PalettePanel', 'PropertiesPanel', 'NodeEditorPanel'] diff --git a/synodetik/gui/components/node_editor.py b/synodetik/gui/components/node_editor.py index b65b323..a97ac19 100644 --- a/synodetik/gui/components/node_editor.py +++ b/synodetik/gui/components/node_editor.py @@ -7,11 +7,11 @@ connections are drawn as links between ports. import dearpygui.dearpygui as dpg from typing import TYPE_CHECKING, Optional, Callable -from ..themes import DarkTheme, hex_to_rgba, color_with_opacity +from synodetik.gui.themes import DarkTheme, hex_to_rgba, color_with_opacity from synodetik.events import get_dispatcher, EventType if TYPE_CHECKING: - from ...core import Machine, Synoptik, Connection + from synodetik.core import Machine, Synoptik, Connection class NodeEditorPanel: diff --git a/synodetik/gui/components/palette.py b/synodetik/gui/components/palette.py index 5a21dc9..4458d8c 100644 --- a/synodetik/gui/components/palette.py +++ b/synodetik/gui/components/palette.py @@ -7,10 +7,10 @@ allowing users to add new machines to the synoptik. import dearpygui.dearpygui as dpg from typing import TYPE_CHECKING, Callable, Optional -from ..themes import DarkTheme, hex_to_rgba +from synodetik.gui.themes import DarkTheme, hex_to_rgba if TYPE_CHECKING: - from ...core import Library + from synodetik.core import Library class PalettePanel: diff --git a/synodetik/gui/components/properties.py b/synodetik/gui/components/properties.py index 2eb47e9..9905b65 100644 --- a/synodetik/gui/components/properties.py +++ b/synodetik/gui/components/properties.py @@ -6,10 +6,10 @@ Displays and allows editing of selected machine/connection properties. import dearpygui.dearpygui as dpg from typing import TYPE_CHECKING, Optional, Callable -from ..themes import DarkTheme +from synodetik.gui.themes import DarkTheme if TYPE_CHECKING: - from ...core import Machine, Synoptik + from synodetik.core import Machine, Synoptik class PropertiesPanel: diff --git a/synodetik/gui/dialogs/__init__.py b/synodetik/gui/dialogs/__init__.py index be33cdd..be79208 100644 --- a/synodetik/gui/dialogs/__init__.py +++ b/synodetik/gui/dialogs/__init__.py @@ -7,8 +7,8 @@ Provides editor dialogs for: - CategoryEditor: Manage flux categories - FluxManager: Browse and manage all flux types """ -from .template_editor import MachineEditor -from .flux_editor import FluxEditor, CategoryEditor, FluxManager +from synodetik.gui.dialogs.template_editor import MachineEditor +from synodetik.gui.dialogs.flux_editor import FluxEditor, CategoryEditor, FluxManager __all__ = [ 'MachineEditor', diff --git a/synodetik/gui/dialogs/flux_editor.py b/synodetik/gui/dialogs/flux_editor.py index b97748e..7a11cbc 100644 --- a/synodetik/gui/dialogs/flux_editor.py +++ b/synodetik/gui/dialogs/flux_editor.py @@ -7,10 +7,10 @@ and their categories (video, audio, network, etc.). import dearpygui.dearpygui as dpg from typing import TYPE_CHECKING, Optional, Callable -from ..themes import DarkTheme, hex_to_rgba +from synodetik.gui.themes import DarkTheme, hex_to_rgba if TYPE_CHECKING: - from ...core import Flux, FluxCategory, Library + from synodetik.core import Flux, FluxCategory, Library class FluxEditor: @@ -130,7 +130,7 @@ class FluxEditor: def _on_save(self, sender=None, app_data=None): """Save the flux type.""" - from ...core import Flux + from synodetik.core import Flux name = dpg.get_value(f"{self.WINDOW_TAG}_name") if not name: @@ -269,7 +269,7 @@ class CategoryEditor: def _on_add_category(self, sender=None, app_data=None): """Add a new category.""" - from ...core import FluxCategory + from synodetik.core import FluxCategory cat_id = dpg.get_value(f"{self.WINDOW_TAG}_new_id") cat_name = dpg.get_value(f"{self.WINDOW_TAG}_new_name") diff --git a/synodetik/gui/dialogs/template_editor.py b/synodetik/gui/dialogs/template_editor.py index 7257c91..9bca922 100644 --- a/synodetik/gui/dialogs/template_editor.py +++ b/synodetik/gui/dialogs/template_editor.py @@ -7,10 +7,10 @@ including port configuration and visual properties. import dearpygui.dearpygui as dpg from typing import TYPE_CHECKING, Optional, Callable -from ..themes import DarkTheme, hex_to_rgba +from synodetik.gui.themes import DarkTheme, hex_to_rgba if TYPE_CHECKING: - from ...core import Machine, Library, Port + from synodetik.core import Machine, Library, Port # Default machine categories @@ -209,7 +209,7 @@ class MachineEditor: def _collect_ports(self, direction: str) -> list['Port']: """Collect port data from the UI.""" - from ...core import create_input, create_output + from synodetik.core import create_input, create_output ports = [] parent = f"{self.WINDOW_TAG}_{direction}s" @@ -239,7 +239,7 @@ class MachineEditor: def _on_save(self, sender=None, app_data=None): """Save the machine.""" - from ...core import Machine + from synodetik.core import Machine name = dpg.get_value(f"{self.WINDOW_TAG}_name") if not name: diff --git a/synodetik/gui/flux_editor.py b/synodetik/gui/flux_editor.py deleted file mode 100644 index c229d21..0000000 --- a/synodetik/gui/flux_editor.py +++ /dev/null @@ -1,407 +0,0 @@ -""" -Flux and category editor windows. - -Provides UI for managing flux types and categories. -""" -import dearpygui.dearpygui as dpg -from typing import Optional, Callable - -from ..core import FluxTemplate, FluxLibrary, get_config_manager, FluxCategoryConfig -from .themes import DarkTheme, hex_to_rgba - - -class FluxTemplateEditor: - """Editor window for flux templates.""" - - def __init__( - self, - flux_library: FluxLibrary, - on_save: Optional[Callable[[FluxTemplate], None]] = None, - template: Optional[FluxTemplate] = None - ): - self.flux_library = flux_library - self.config = get_config_manager() - self.on_save = on_save - self.template = template - self._window_tag = f"flux_editor_{id(self)}" - - def show(self): - """Display the editor window.""" - if dpg.does_item_exist(self._window_tag): - dpg.focus_item(self._window_tag) - return - - is_edit = self.template is not None - title = "Edit Flux" if is_edit else "New Flux Type" - - categories = self.config.get_category_names() - category_ids = self.config.get_category_ids() - - # Find current category index - current_cat_idx = 0 - if is_edit and self.template.category_id in category_ids: - current_cat_idx = category_ids.index(self.template.category_id) - - with dpg.window( - label=title, - tag=self._window_tag, - width=400, - height=400, - on_close=self._on_close, - pos=(150, 150) - ): - dpg.add_input_text( - label="Name", - tag=f"{self._window_tag}_name", - default_value=self.template.name if is_edit else "", - width=250 - ) - - dpg.add_combo( - label="Category", - tag=f"{self._window_tag}_category", - items=categories, - default_value=categories[current_cat_idx] if categories else "", - width=200 - ) - - # Color picker - default_color = hex_to_rgba(self.template.color if is_edit else "#FFFFFF") - dpg.add_color_edit( - label="Color", - tag=f"{self._window_tag}_color", - default_value=( - default_color[0] / 255.0, - default_color[1] / 255.0, - default_color[2] / 255.0, - 1.0 - ), - no_alpha=True, - width=200 - ) - - dpg.add_input_text( - label="Connector Type", - tag=f"{self._window_tag}_connector", - default_value=self.template.connector_type if is_edit else "", - width=200 - ) - - dpg.add_combo( - label="Signal Type", - tag=f"{self._window_tag}_signal", - items=["digital", "analog", "hybrid"], - default_value=self.template.signal_type if is_edit else "digital", - width=150 - ) - - dpg.add_checkbox( - label="Bidirectional", - tag=f"{self._window_tag}_bidir", - default_value=self.template.bidirectional if is_edit else False - ) - - dpg.add_input_text( - label="Description", - tag=f"{self._window_tag}_desc", - default_value=self.template.description if is_edit else "", - multiline=True, - height=60, - width=350 - ) - - dpg.add_separator() - - with dpg.group(horizontal=True): - dpg.add_button(label="Save", callback=self._on_save, width=100) - dpg.add_button(label="Cancel", callback=self._on_close, width=80) - - def _on_save(self, sender=None, app_data=None): - """Save the flux template.""" - name = dpg.get_value(f"{self._window_tag}_name") - if not name: - return - - # Get category - cat_name = dpg.get_value(f"{self._window_tag}_category") - category_ids = self.config.get_category_ids() - category_names = self.config.get_category_names() - cat_idx = category_names.index(cat_name) if cat_name in category_names else 0 - category_id = category_ids[cat_idx] if category_ids else "data" - - # Get color - color_value = dpg.get_value(f"{self._window_tag}_color") - color_hex = ( - f"#{int(color_value[0]*255):02x}" - f"{int(color_value[1]*255):02x}" - f"{int(color_value[2]*255):02x}" - ) - - if self.template: - template = self.template - template.name = name - else: - template = FluxTemplate(name=name, category_id=category_id) - - template.category_id = category_id - template.color = color_hex - template.connector_type = dpg.get_value(f"{self._window_tag}_connector") - template.signal_type = dpg.get_value(f"{self._window_tag}_signal") - template.bidirectional = dpg.get_value(f"{self._window_tag}_bidir") - template.description = dpg.get_value(f"{self._window_tag}_desc") - - self.flux_library.save_template(template) - - if self.on_save: - self.on_save(template) - - self._on_close() - - def _on_close(self, sender=None, app_data=None): - """Close the window.""" - if dpg.does_item_exist(self._window_tag): - dpg.delete_item(self._window_tag) - - -class CategoryEditor: - """Editor window for flux categories.""" - - def __init__(self, on_save: Optional[Callable[[], None]] = None): - self.config = get_config_manager() - self.on_save = on_save - self._window_tag = "category_editor" - - def show(self): - """Display the editor window.""" - if dpg.does_item_exist(self._window_tag): - dpg.focus_item(self._window_tag) - return - - with dpg.window( - label="Manage Categories", - tag=self._window_tag, - width=450, - height=400, - on_close=self._on_close, - pos=(200, 100) - ): - dpg.add_text("Flux Categories:") - dpg.add_separator() - - with dpg.group(tag=f"{self._window_tag}_list"): - self._refresh_list() - - dpg.add_separator() - dpg.add_text("New Category:", color=DarkTheme.TEXT_SECONDARY) - - with dpg.group(horizontal=True): - dpg.add_input_text( - hint="ID (unique)", - tag=f"{self._window_tag}_new_id", - width=100 - ) - dpg.add_input_text( - hint="Name", - tag=f"{self._window_tag}_new_name", - width=120 - ) - dpg.add_color_edit( - tag=f"{self._window_tag}_new_color", - default_value=(0.5, 0.5, 0.5, 1.0), - no_alpha=True, - no_inputs=True, - width=50 - ) - dpg.add_button(label="Add", callback=self._on_add_category) - - def _refresh_list(self): - """Refresh the category list.""" - parent = f"{self._window_tag}_list" - if dpg.does_item_exist(parent): - dpg.delete_item(parent, children_only=True) - - for cat in self.config.get_categories(): - with dpg.group(horizontal=True, parent=parent): - dpg.add_color_button( - default_value=( - *[c/255 for c in hex_to_rgba(cat.color)[:3]], - 1.0 - ), - width=20, height=20, - no_border=True - ) - dpg.add_text(f"{cat.name} ({cat.id})") - dpg.add_button( - label="Delete", - callback=self._on_delete_category, - user_data=cat.id, - width=50 - ) - - def _on_add_category(self, sender=None, app_data=None): - """Add a new category.""" - cat_id = dpg.get_value(f"{self._window_tag}_new_id") - cat_name = dpg.get_value(f"{self._window_tag}_new_name") - - if not cat_id or not cat_name: - return - - color_value = dpg.get_value(f"{self._window_tag}_new_color") - color_hex = ( - f"#{int(color_value[0]*255):02x}" - f"{int(color_value[1]*255):02x}" - f"{int(color_value[2]*255):02x}" - ) - - category = FluxCategoryConfig( - id=cat_id.lower().replace(" ", "_"), - name=cat_name, - color=color_hex - ) - - self.config.add_category(category) - self._refresh_list() - - # Reset inputs - dpg.set_value(f"{self._window_tag}_new_id", "") - dpg.set_value(f"{self._window_tag}_new_name", "") - - if self.on_save: - self.on_save() - - def _on_delete_category(self, sender, app_data, user_data): - """Delete a category.""" - self.config.remove_category(user_data) - self._refresh_list() - - if self.on_save: - self.on_save() - - def _on_close(self, sender=None, app_data=None): - """Close the window.""" - if dpg.does_item_exist(self._window_tag): - dpg.delete_item(self._window_tag) - - -class FluxManager: - """Manager window for flux types.""" - - def __init__( - self, - flux_library: FluxLibrary, - on_change: Optional[Callable[[], None]] = None - ): - self.flux_library = flux_library - self.config = get_config_manager() - self.on_change = on_change - self._window_tag = "flux_manager" - - def show(self): - """Display the manager window.""" - if dpg.does_item_exist(self._window_tag): - dpg.focus_item(self._window_tag) - return - - with dpg.window( - label="Manage Flux Types", - tag=self._window_tag, - width=500, - height=500, - on_close=self._on_close, - pos=(100, 80) - ): - with dpg.group(horizontal=True): - dpg.add_button(label="+ New Flux", callback=self._on_new_flux) - dpg.add_button(label="Manage Categories", callback=self._on_manage_categories) - - dpg.add_separator() - - with dpg.group(tag=f"{self._window_tag}_list"): - self._refresh_list() - - def _refresh_list(self): - """Refresh the flux list.""" - parent = f"{self._window_tag}_list" - if dpg.does_item_exist(parent): - dpg.delete_item(parent, children_only=True) - - # Group by category - for cat in self.config.get_categories(): - flux_list = self.flux_library.get_by_category(cat.id) - if flux_list: - with dpg.tree_node( - label=f"{cat.name} ({len(flux_list)})", - parent=parent, - default_open=True - ): - for flux in flux_list: - with dpg.group(horizontal=True): - dpg.add_color_button( - default_value=( - *[c/255 for c in hex_to_rgba(flux.color)[:3]], - 1.0 - ), - width=16, height=16, - no_border=True - ) - dpg.add_text(flux.name) - dpg.add_text( - f"[{flux.connector_type}]", - color=DarkTheme.TEXT_SECONDARY - ) - dpg.add_button( - label="Edit", - callback=self._on_edit_flux, - user_data=flux.id, - width=50 - ) - dpg.add_button( - label="X", - callback=self._on_delete_flux, - user_data=flux.id, - width=25 - ) - - def _on_new_flux(self, sender=None, app_data=None): - """Create a new flux.""" - editor = FluxTemplateEditor( - flux_library=self.flux_library, - on_save=self._on_flux_saved - ) - editor.show() - - def _on_edit_flux(self, sender, app_data, user_data): - """Edit a flux.""" - template = self.flux_library.get_template(user_data) - if template: - editor = FluxTemplateEditor( - flux_library=self.flux_library, - on_save=self._on_flux_saved, - template=template - ) - editor.show() - - def _on_delete_flux(self, sender, app_data, user_data): - """Delete a flux.""" - self.flux_library.delete_template(user_data) - self._refresh_list() - - if self.on_change: - self.on_change() - - def _on_flux_saved(self, template: FluxTemplate): - """Handle flux save.""" - self._refresh_list() - - if self.on_change: - self.on_change() - - def _on_manage_categories(self, sender=None, app_data=None): - """Open category manager.""" - editor = CategoryEditor(on_save=self._refresh_list) - editor.show() - - def _on_close(self, sender=None, app_data=None): - """Close the window.""" - if dpg.does_item_exist(self._window_tag): - dpg.delete_item(self._window_tag) diff --git a/synodetik/gui/template_editor.py b/synodetik/gui/template_editor.py deleted file mode 100644 index 45a13de..0000000 --- a/synodetik/gui/template_editor.py +++ /dev/null @@ -1,250 +0,0 @@ -""" -Fenêtre d'édition de template de machine. -""" -import dearpygui.dearpygui as dpg -from typing import Optional, Callable - -from ..core import MachineTemplate, PortTemplate, MachineLibrary, get_config_manager -from .themes import DarkTheme, hex_to_rgba - - -class MachineTemplateEditor: - """Éditeur de template de machine dans une fenêtre séparée.""" - - # Catégories de machines prédéfinies - MACHINE_CATEGORIES = [ - "Source", "Affichage", "Mélangeur", "Distribution", - "Audio", "Réseau", "Contrôle", "Enregistrement", "Conversion", "Autre" - ] - - def __init__( - self, - library: MachineLibrary, - on_save: Optional[Callable[[MachineTemplate], None]] = None, - template: Optional[MachineTemplate] = None - ): - self.library = library - self.on_save = on_save - self.template = template - self._input_count = 0 - self._output_count = 0 - self._window_tag = f"template_editor_{id(self)}" - - def show(self): - """Affiche la fenêtre d'édition.""" - if dpg.does_item_exist(self._window_tag): - dpg.focus_item(self._window_tag) - return - - is_edit = self.template is not None - title = "Modifier Machine" if is_edit else "Nouvelle Machine" - - with dpg.window( - label=title, - tag=self._window_tag, - width=550, - height=650, - on_close=self._on_close, - pos=(100, 50) - ): - # Informations générales - with dpg.collapsing_header(label="Informations", default_open=True): - dpg.add_input_text( - label="Nom", - tag=f"{self._window_tag}_name", - default_value=self.template.name if is_edit else "", - width=300 - ) - dpg.add_combo( - label="Catégorie", - tag=f"{self._window_tag}_category", - items=self.MACHINE_CATEGORIES, - default_value=self.template.category if is_edit else self.MACHINE_CATEGORIES[0], - width=200 - ) - dpg.add_input_text( - label="Fabricant", - tag=f"{self._window_tag}_manufacturer", - default_value=self.template.manufacturer if is_edit else "", - width=200 - ) - dpg.add_input_text( - label="Modèle", - tag=f"{self._window_tag}_model", - default_value=self.template.model if is_edit else "", - width=200 - ) - - # Couleur - default_color = hex_to_rgba(self.template.color if is_edit else "#2D2D2D") - default_color_normalized = ( - default_color[0], - default_color[1], - default_color[2], - 1.0 - ) - dpg.add_color_edit( - label="Couleur", - tag=f"{self._window_tag}_color", - default_value=default_color_normalized, - no_alpha=True, - width=200 - ) - dpg.add_input_text( - label="Description", - tag=f"{self._window_tag}_description", - default_value=self.template.description if is_edit else "", - multiline=True, - height=60, - width=450 - ) - - # Ports d'entrée - with dpg.collapsing_header(label="Entrées", default_open=True): - with dpg.group(tag=f"{self._window_tag}_inputs"): - if is_edit: - for port in self.template.inputs: - self._add_port_row("input", port.name, port.flux_type_name) - dpg.add_button( - label="+ Ajouter Entrée", - callback=lambda: self._add_port_row("input") - ) - - # Ports de sortie - with dpg.collapsing_header(label="Sorties", default_open=True): - with dpg.group(tag=f"{self._window_tag}_outputs"): - if is_edit: - for port in self.template.outputs: - self._add_port_row("output", port.name, port.flux_type_name) - dpg.add_button( - label="+ Ajouter Sortie", - callback=lambda: self._add_port_row("output") - ) - - dpg.add_separator() - - # Boutons d'action - with dpg.group(horizontal=True): - dpg.add_button( - label="Sauvegarder", - callback=self._on_save, - width=120 - ) - dpg.add_button( - label="Annuler", - callback=self._on_close, - width=100 - ) - - def _add_port_row( - self, - direction: str, - name: str = "", - flux_name: str = "" - ): - """Ajoute une ligne de port.""" - parent = f"{self._window_tag}_{direction}s" - - if direction == "input": - idx = self._input_count - self._input_count += 1 - else: - idx = self._output_count - self._output_count += 1 - - row_tag = f"{self._window_tag}_{direction}_{idx}" - - # Récupère les noms de flux depuis la bibliothèque - flux_names = self.library.get_flux_names() - - with dpg.group(horizontal=True, parent=parent, tag=row_tag): - dpg.add_input_text( - hint="Nom du port", - default_value=name, - width=180, - tag=f"{row_tag}_name" - ) - dpg.add_combo( - items=flux_names, - default_value=flux_name if flux_name in flux_names else (flux_names[0] if flux_names else ""), - width=140, - tag=f"{row_tag}_flux" - ) - dpg.add_button( - label="X", - callback=self._on_remove_port, - user_data=row_tag, - width=30 - ) - - def _on_remove_port(self, sender, app_data, user_data): - """Supprime une ligne de port.""" - row_tag = user_data - if dpg.does_item_exist(row_tag): - dpg.delete_item(row_tag) - - def _collect_ports(self, direction: str) -> list[PortTemplate]: - """Collecte les ports depuis l'interface.""" - ports = [] - parent = f"{self._window_tag}_{direction}s" - - if not dpg.does_item_exist(parent): - return ports - - children = dpg.get_item_children(parent, 1) - if not children: - return ports - - for child in children: - row_children = dpg.get_item_children(child, 1) - if row_children and len(row_children) >= 2: - name = dpg.get_value(row_children[0]) - flux = dpg.get_value(row_children[1]) - - if name and flux: - ports.append(PortTemplate(name=name, flux_type_name=flux)) - - return ports - - def _on_save(self, sender=None, app_data=None): - """Sauvegarde le template.""" - name = dpg.get_value(f"{self._window_tag}_name") - - if not name: - print("Erreur: Le nom est requis") - return - - # Récupère la couleur - color_value = dpg.get_value(f"{self._window_tag}_color") - color_hex = f"#{int(color_value[0]*255):02x}{int(color_value[1]*255):02x}{int(color_value[2]*255):02x}" - - # Crée ou met à jour le template - if self.template is not None: - template = self.template - template.name = name - else: - template = MachineTemplate(name=name) - - template.category = dpg.get_value(f"{self._window_tag}_category") - template.manufacturer = dpg.get_value(f"{self._window_tag}_manufacturer") - template.model = dpg.get_value(f"{self._window_tag}_model") - template.color = color_hex - template.description = dpg.get_value(f"{self._window_tag}_description") - - # Ports - template.inputs = self._collect_ports("input") - template.outputs = self._collect_ports("output") - - # Sauvegarde dans la bibliothèque - self.library.save_template(template) - - # Callback - if self.on_save: - self.on_save(template) - - self._on_close() - - def _on_close(self, sender=None, app_data=None): - """Ferme la fenêtre.""" - if dpg.does_item_exist(self._window_tag): - dpg.delete_item(self._window_tag) diff --git a/synodetik/modules/__init__.py b/synodetik/modules/__init__.py index febf22e..ed5697f 100644 --- a/synodetik/modules/__init__.py +++ b/synodetik/modules/__init__.py @@ -20,8 +20,8 @@ Example: def on_unload(self): pass """ -from .base import Module, ModuleInfo, MenuItem, ModuleError -from .manager import ModuleManager +from synodetik.modules.base import Module, ModuleInfo, MenuItem, ModuleError +from synodetik.modules.manager import ModuleManager __all__ = [ 'Module', diff --git a/synodetik/modules/base.py b/synodetik/modules/base.py index 3f7cd20..fe32012 100644 --- a/synodetik/modules/base.py +++ b/synodetik/modules/base.py @@ -37,7 +37,7 @@ import dearpygui.dearpygui as dpg from synodetik.core.events import EventDispatcher, EventType, get_dispatcher if TYPE_CHECKING: - from synodetik.core.gui.app import SynoptikApp + from synodetik.gui.app import SynoptikApp @dataclass diff --git a/synodetik/modules/manager.py b/synodetik/modules/manager.py index 9a32c23..b279fdb 100644 --- a/synodetik/modules/manager.py +++ b/synodetik/modules/manager.py @@ -10,7 +10,7 @@ import sys from pathlib import Path from typing import TYPE_CHECKING, Optional, List -from .base import Module, ModuleInfo, ModuleError, MenuItem +from synodetik.modules.base import Module, ModuleInfo, ModuleError, MenuItem from synodetik.core.events import get_dispatcher, EventType if TYPE_CHECKING: diff --git a/synodetik/utils/__init__.py b/synodetik/utils/__init__.py index 0ea6866..a9cb6b0 100644 --- a/synodetik/utils/__init__.py +++ b/synodetik/utils/__init__.py @@ -5,7 +5,7 @@ This module re-exports utilities from core.utils to maintain backward compatibility with imports like: from synodetik.utils import serialization """ -from ..core.utils import ( +from synodetik.core.utils import ( save_json, load_json, to_json_string, from_json_string, save, load ) diff --git a/synodetik/utils/serialization.py b/synodetik/utils/serialization.py index 470ee8f..9396441 100644 --- a/synodetik/utils/serialization.py +++ b/synodetik/utils/serialization.py @@ -5,7 +5,7 @@ This module re-exports serialization utilities from core.utils.serialization to maintain backward compatibility with imports like: from synodetik.utils import serialization """ -from ..core.utils.serialization import ( +from synodetik.core.utils.serialization import ( save_json, load_json, to_json_string, from_json_string, save, load )