From af4371ebe4238e7a970f919ed45eb8da5fa40a1a Mon Sep 17 00:00:00 2001 From: Jojojoppe Date: Fri, 1 Aug 2025 14:54:29 +0200 Subject: [PATCH] Added invert widget --- negstation/negstation.py | 2 +- negstation/widgets/base_widget.py | 1 + negstation/widgets/invert_stage.py | 25 ++++++++ negstation/widgets/open_image_widget.py | 1 + negstation/widgets/pipeline_stage_widget.py | 63 +++++++++++++++++++++ negstation/widgets/stage_viewer_widget.py | 1 + negstation_layout.ini | 34 ++++++++--- negstation_widgets.json | 4 ++ 8 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 negstation/widgets/invert_stage.py create mode 100644 negstation/widgets/pipeline_stage_widget.py diff --git a/negstation/negstation.py b/negstation/negstation.py index 43f41ea..93c6d0f 100644 --- a/negstation/negstation.py +++ b/negstation/negstation.py @@ -5,7 +5,6 @@ import importlib import inspect import sys from pathlib import Path -from importlib.machinery import SourceFileLoader from .event_bus import EventBus from .image_pipeline import ImagePipeline @@ -59,6 +58,7 @@ class EditorManager: ModuleBaseWidget and issubclass(cls, ModuleBaseWidget) and cls is not ModuleBaseWidget + and cls.register ): logging.info( f" -> Found and registered widget: {name}") diff --git a/negstation/widgets/base_widget.py b/negstation/widgets/base_widget.py index 5b82576..b5eed03 100644 --- a/negstation/widgets/base_widget.py +++ b/negstation/widgets/base_widget.py @@ -9,6 +9,7 @@ if TYPE_CHECKING: class BaseWidget: name: str = "BaseWidget" + register: bool = False def __init__(self, manager: "EditorManager", logger: logging.Logger): self.manager = manager diff --git a/negstation/widgets/invert_stage.py b/negstation/widgets/invert_stage.py new file mode 100644 index 0000000..e91c326 --- /dev/null +++ b/negstation/widgets/invert_stage.py @@ -0,0 +1,25 @@ +import dearpygui.dearpygui as dpg +import numpy as np +from .pipeline_stage_widget import PipelineStageWidget + + +class InvertStage(PipelineStageWidget): + name = "Invert Image" + register = True + + def __init__(self, manager, logger): + super().__init__(manager, logger) + self.stage_out = "inverted_image" + + def create_content(self): + dpg.add_button(label="Invert", callback=lambda s, a, u: self._do_invert()) + + def on_stage(self, img): + self._do_invert() + + def _do_invert(self): + if self.img is None: + return + inverted = self.img.copy() + inverted[...,:3] = 1.0 - inverted[...,:3] + self.publish(inverted) diff --git a/negstation/widgets/open_image_widget.py b/negstation/widgets/open_image_widget.py index 3cdf428..e0ea2c1 100644 --- a/negstation/widgets/open_image_widget.py +++ b/negstation/widgets/open_image_widget.py @@ -7,6 +7,7 @@ from .base_widget import BaseWidget class OpenImageWidget(BaseWidget): name: str = "Open Image" + register = True def __init__(self, manager, logger, stage_out="loaded_image"): super().__init__(manager, logger) diff --git a/negstation/widgets/pipeline_stage_widget.py b/negstation/widgets/pipeline_stage_widget.py new file mode 100644 index 0000000..37250a0 --- /dev/null +++ b/negstation/widgets/pipeline_stage_widget.py @@ -0,0 +1,63 @@ +import dearpygui.dearpygui as dpg +import numpy as np +from .base_widget import BaseWidget + + +class PipelineStageWidget(BaseWidget): + register = False + + def __init__(self, manager, logger): + super().__init__(manager, logger) + self.stage_in = "" + self.stage_out = "" + self.img = None + + self.manager.bus.subscribe( + "pipeline_stage", self._on_pipeline, main_thread=True + ) + self.manager.bus.subscribe( + "pipeline_stages", self._on_stage_list, main_thread=True + ) + + def create(self): + with dpg.window(label=self.name, tag=self.window_tag, width=400, height=300): + # top‐row: input / output + dpg.add_text("Configuration:") + self.combo = dpg.add_combo( + label="Stage In", items=[], callback=self._on_select + ) + dpg.add_input_text( + label="Stage Out", + default_value=self.stage_out, + callback=lambda s, a, u: setattr(self, "stage_out", a), + ) + dpg.add_separator() + # now let subclasses populate the rest + self.create_content() + + def _on_select(self, sender, selected_stage): + self.stage_in = selected_stage + self.img = self.manager.pipeline.get_stage(selected_stage) + self.on_stage(self.img) + + def update(self): + pass + + def create_content(self): + raise NotImplementedError + + def _on_pipeline(self, data): + name, img = data + if name == self.stage_in: + self.img = img + self.on_stage(img) + + def _on_stage_list(self, stages): + self.stages = stages + dpg.configure_item(self.combo, items=stages) + + def on_stage(self, img: np.ndarray): + pass + + def publish(self, img: np.ndarray): + self.manager.pipeline.add_stage(self.stage_out, img) diff --git a/negstation/widgets/stage_viewer_widget.py b/negstation/widgets/stage_viewer_widget.py index cc6a758..2f81a75 100644 --- a/negstation/widgets/stage_viewer_widget.py +++ b/negstation/widgets/stage_viewer_widget.py @@ -6,6 +6,7 @@ from .base_widget import BaseWidget class StageViewerWidget(BaseWidget): name: str = "Image Stage Viewer" + register = True def __init__(self, manager, logger): super().__init__(manager, logger) diff --git a/negstation_layout.ini b/negstation_layout.ini index ddb29d0..30b0b6b 100644 --- a/negstation_layout.ini +++ b/negstation_layout.ini @@ -1,17 +1,17 @@ [Window][WindowOverViewport_11111111] Pos=0,19 -Size=800,581 +Size=814,581 Collapsed=0 [Window][###22] Pos=0,19 -Size=299,581 +Size=291,244 Collapsed=0 -DockId=0x00000001,0 +DockId=0x00000005,0 [Window][###27] -Pos=301,19 -Size=499,581 +Pos=293,19 +Size=521,581 Collapsed=0 DockId=0x00000002,0 @@ -20,8 +20,24 @@ Pos=60,60 Size=400,400 Collapsed=0 -[Docking][Data] -DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=800,581 Split=X Selected=0x26E8F608 - DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=299,581 Selected=0xEE087978 - DockNode ID=0x00000002 Parent=0x7C6B3D9B SizeRef=499,581 CentralNode=1 Selected=0x26E8F608 +[Window][###41] +Pos=0,312 +Size=299,288 +Collapsed=0 +DockId=0x00000004,0 + +[Window][###34] +Pos=0,265 +Size=291,335 +Collapsed=0 +DockId=0x00000006,0 + +[Docking][Data] +DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=814,581 Split=X Selected=0x26E8F608 + DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=291,581 Split=Y Selected=0xEE087978 + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=299,291 Split=Y Selected=0xEE087978 + DockNode ID=0x00000005 Parent=0x00000003 SizeRef=299,244 Selected=0xEE087978 + DockNode ID=0x00000006 Parent=0x00000003 SizeRef=299,335 Selected=0xAA145F7D + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=299,288 Selected=0x7FF1E0B5 + DockNode ID=0x00000002 Parent=0x7C6B3D9B SizeRef=521,581 CentralNode=1 Selected=0x26E8F608 diff --git a/negstation_widgets.json b/negstation_widgets.json index 1259061..0cf7237 100644 --- a/negstation_widgets.json +++ b/negstation_widgets.json @@ -6,5 +6,9 @@ { "widget_type": "StageViewerWidget", "config": {} + }, + { + "widget_type": "InvertStage", + "config": {} } ] \ No newline at end of file