diff --git a/negative_raw.nef b/negative_raw.nef new file mode 100644 index 0000000..c439561 Binary files /dev/null and b/negative_raw.nef differ diff --git a/negative_raw.png b/negative_raw.png new file mode 100644 index 0000000..34ff7eb Binary files /dev/null and b/negative_raw.png differ diff --git a/negstation/event_bus.py b/negstation/event_bus.py index b0c73bc..ef07ee4 100644 --- a/negstation/event_bus.py +++ b/negstation/event_bus.py @@ -1,6 +1,8 @@ import threading import queue import logging +import inspect +import types class EventBus: @@ -42,3 +44,14 @@ class EventBus: callback(data) except queue.Empty: break + + def unsubscribe_instance(self, instance): + for event_type, subs in list(self.subscribers.items()): + new_subs = [] + for callback, main_thread in subs: + # if it's a bound method to our instance, skip it + if inspect.ismethod(callback) and callback.__self__ is instance: + continue + new_subs.append((callback, main_thread)) + if len(new_subs) != len(subs): + self.subscribers[event_type] = new_subs \ No newline at end of file diff --git a/negstation/image_pipeline.py b/negstation/image_pipeline.py index aba1754..944377a 100644 --- a/negstation/image_pipeline.py +++ b/negstation/image_pipeline.py @@ -6,17 +6,19 @@ from .event_bus import EventBus class ImagePipeline: def __init__(self, bus: EventBus): self.bus = bus - self.stages = [] + self.id_counter = 0 + self.stages = {} self.stagedata = {} def register_stage(self, name: str): - self.stages.append(name) - self.stagedata[name] = None + self.stages[self.id_counter] = name + self.stagedata[self.id_counter] = None self.bus.publish_deferred("pipeline_stages", self.stages) - return len(self.stages) - 1 + self.id_counter += 1 + return self.id_counter-1 def rename_stage(self, id: int, name: str): - if id >= 0 and id < len(self.stages): + if id in self.stages: self.stages[id] = name self.bus.publish_deferred("pipeline_stages", self.stages) @@ -30,11 +32,16 @@ class ImagePipeline: else: return None - def get_stage_name(self, id:int): + def get_stage_name(self, id: int): if id >= 0 and id < len(self.stages): - return self.stages[id] + return self.stages[id] else: return None - + def republish_stages(self): - self.bus.publish_deferred("pipeline_stages", self.stages) \ No newline at end of file + self.bus.publish_deferred("pipeline_stages", self.stages) + + def remove_stage(self, id: int): + del self.stages[id] + del self.stagedata[id] + self.republish_stages() diff --git a/negstation/negstation.py b/negstation/negstation.py index 774be49..f5b579f 100644 --- a/negstation/negstation.py +++ b/negstation/negstation.py @@ -12,7 +12,7 @@ from .layout_manager import LayoutManager from .widgets.base_widget import BaseWidget -logging.basicConfig(level=logging.DEBUG, +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") logger = logging.getLogger(__name__) @@ -76,6 +76,7 @@ class EditorManager: def _add_widget(self, widget_type: str): WidgetClass = self.widget_classes[widget_type] instance = WidgetClass(self, logger) + logger.info(f'Created instance: {str(instance)}') self.widgets.append(instance) instance.create() diff --git a/negstation/widgets/base_widget.py b/negstation/widgets/base_widget.py index 275b2c0..3d16df2 100644 --- a/negstation/widgets/base_widget.py +++ b/negstation/widgets/base_widget.py @@ -1,5 +1,6 @@ import dearpygui.dearpygui as dpg import logging +import gc from typing import TYPE_CHECKING @@ -40,9 +41,10 @@ class BaseWidget: dpg.add_item_resize_handler( callback=self._on_window_resize, parent=self.window_handler ) - dpg.bind_item_handler_registry(self.window_tag, self.window_handler) self.create_content() + + dpg.bind_item_handler_registry(self.window_tag, self.window_handler) def create_content(self): """Must be implemented by the widget, creates the content of the window""" @@ -65,14 +67,24 @@ class BaseWidget: # Callbacks def _on_window_close(self): - try: - dpg.delete_item(self.window_tag) - self.manager.widgets.remove(self) - except ValueError: - pass + """Some cleanup after closing a window""" + self.manager.bus.unsubscribe_instance(self) + + dpg.delete_item(self.window_tag) + dpg.delete_item(self.window_handler) + + self.manager.widgets.remove(self) + + self.manager = None + self.logger = None + + gc.collect() def _on_window_resize(self, data): win_w, win_h = dpg.get_item_rect_size(self.window_tag) self.window_height = win_h self.window_width = win_w self.on_resize(win_w, win_h) + + def __del__(self): + print("Widget deleted") diff --git a/negstation/widgets/pipeline_stage_widget.py b/negstation/widgets/pipeline_stage_widget.py index 2c233d8..08cdde1 100644 --- a/negstation/widgets/pipeline_stage_widget.py +++ b/negstation/widgets/pipeline_stage_widget.py @@ -22,6 +22,7 @@ class PipelineStageWidget(BaseWidget): self.pipeline_stage_in_id = None self.pipeline_stage_out_id = None self.pipeline_config_group_tag = dpg.generate_uuid() + self.stage_in_combo = dpg.generate_uuid() if self.has_pipeline_out: self.pipeline_stage_out_id = self.manager.pipeline.register_stage( @@ -38,11 +39,12 @@ class PipelineStageWidget(BaseWidget): def create_content(self): with dpg.group(tag=self.pipeline_config_group_tag): if self.has_pipeline_in: - self.stage_in_combo = dpg.add_combo( + dpg.add_combo( label="Stage In", items=[], callback=self._on_stage_in_select, default_value=f"{self.manager.pipeline.get_stage_name(0)} : 0", + tag=self.stage_in_combo ) if self.has_pipeline_out: dpg.add_input_text( @@ -72,9 +74,14 @@ class PipelineStageWidget(BaseWidget): # Callbacks + def _on_window_close(self): + if self.has_pipeline_out: + self.manager.pipeline.remove_stage(self.pipeline_stage_out_id) + return super()._on_window_close() + def _on_stage_list(self, stagelist): if self.has_pipeline_in: - stages = [f"{stage} : {id}" for id, stage in enumerate(stagelist)] + stages = [f"{stage} : {id}" for id, stage in stagelist.items()] dpg.configure_item(self.stage_in_combo, items=stages) def _on_stage_in_select(self, sender, selected_stage: str): diff --git a/negstation/widgets/stage_viewer_widget.py b/negstation/widgets/stage_viewer_widget.py index e19a72f..17e7278 100644 --- a/negstation/widgets/stage_viewer_widget.py +++ b/negstation/widgets/stage_viewer_widget.py @@ -32,6 +32,7 @@ class PipelineStageViewer(PipelineStageWidget): def update_texture(self, img: np.ndarray): """Only call from update function""" + # TODO show a smaller version of the image to speed things up if img is None: dpg.configure_item(self.image_item, show=False) return diff --git a/negstation_layout.ini b/negstation_layout.ini index 039e88a..da9a5ae 100644 --- a/negstation_layout.ini +++ b/negstation_layout.ini @@ -1,6 +1,6 @@ [Window][WindowOverViewport_11111111] Pos=0,19 -Size=1109,809 +Size=800,581 Collapsed=0 [Window][Debug##Default] @@ -20,9 +20,10 @@ Size=300,200 Collapsed=0 [Window][###34] -Pos=434,65 -Size=300,200 +Pos=0,480 +Size=250,120 Collapsed=0 +DockId=0x00000010,0 [Window][###33] Pos=0,445 @@ -50,9 +51,9 @@ DockId=0x00000008,0 [Window][###23] Pos=0,19 -Size=250,424 +Size=250,459 Collapsed=0 -DockId=0x0000000D,0 +DockId=0x00000017,0 [Window][###32] Pos=0,376 @@ -84,20 +85,53 @@ Size=857,809 Collapsed=0 DockId=0x00000002,0 -[Docking][Data] -DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=1109,809 Split=X - DockNode ID=0x00000009 Parent=0x7C6B3D9B SizeRef=250,581 Split=Y Selected=0xD36850C8 - DockNode ID=0x0000000B Parent=0x00000009 SizeRef=147,355 Split=Y Selected=0xD36850C8 - DockNode ID=0x0000000D Parent=0x0000000B SizeRef=250,304 Selected=0xD36850C8 - DockNode ID=0x0000000E Parent=0x0000000B SizeRef=250,275 Selected=0x1834836D - DockNode ID=0x0000000C Parent=0x00000009 SizeRef=147,224 Selected=0x2554AADD - DockNode ID=0x0000000A Parent=0x7C6B3D9B SizeRef=548,581 Split=X - DockNode ID=0x00000005 Parent=0x0000000A SizeRef=288,581 Split=Y Selected=0xEE087978 - DockNode ID=0x00000007 Parent=0x00000005 SizeRef=147,427 Selected=0xEE087978 - DockNode ID=0x00000008 Parent=0x00000005 SizeRef=147,152 Selected=0x62F4D00D - DockNode ID=0x00000006 Parent=0x0000000A SizeRef=510,581 Split=X - DockNode ID=0x00000001 Parent=0x00000006 SizeRef=299,581 Split=Y Selected=0xA4B861D9 - DockNode ID=0x00000003 Parent=0x00000001 SizeRef=299,379 Selected=0xA4B861D9 - DockNode ID=0x00000004 Parent=0x00000001 SizeRef=299,200 Selected=0xEDB425AD - DockNode ID=0x00000002 Parent=0x00000006 SizeRef=499,581 CentralNode=1 Selected=0x7FF1E0B5 +[Window][###42] +Pos=252,19 +Size=548,581 +Collapsed=0 +DockId=0x00000002,0 + +[Window][###57] +Pos=0,400 +Size=250,200 +Collapsed=0 +DockId=0x00000018,0 + +[Window][###65] +Pos=0,400 +Size=250,200 +Collapsed=0 +DockId=0x00000016,0 + +[Window][###73] +Pos=237,120 +Size=300,200 +Collapsed=0 + +[Docking][Data] +DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=800,581 Split=X + DockNode ID=0x00000009 Parent=0x7C6B3D9B SizeRef=250,581 Split=Y Selected=0xD36850C8 + DockNode ID=0x0000000B Parent=0x00000009 SizeRef=147,355 Split=Y Selected=0xD36850C8 + DockNode ID=0x0000000D Parent=0x0000000B SizeRef=250,304 Split=Y Selected=0xD36850C8 + DockNode ID=0x0000000F Parent=0x0000000D SizeRef=250,459 Split=Y Selected=0xD36850C8 + DockNode ID=0x00000011 Parent=0x0000000F SizeRef=250,379 Split=Y Selected=0xD36850C8 + DockNode ID=0x00000013 Parent=0x00000011 SizeRef=250,379 Split=Y Selected=0xD36850C8 + DockNode ID=0x00000015 Parent=0x00000013 SizeRef=250,379 Split=Y Selected=0xD36850C8 + DockNode ID=0x00000017 Parent=0x00000015 SizeRef=250,379 Selected=0xD36850C8 + DockNode ID=0x00000018 Parent=0x00000015 SizeRef=250,200 Selected=0x3BEDC6B0 + DockNode ID=0x00000016 Parent=0x00000013 SizeRef=250,200 Selected=0xC7B9E77E + DockNode ID=0x00000014 Parent=0x00000011 SizeRef=250,200 Selected=0x3BEDC6B0 + DockNode ID=0x00000012 Parent=0x0000000F SizeRef=250,200 Selected=0x83A5C17B + DockNode ID=0x00000010 Parent=0x0000000D SizeRef=250,120 Selected=0xAA145F7D + DockNode ID=0x0000000E Parent=0x0000000B SizeRef=250,275 Selected=0x1834836D + DockNode ID=0x0000000C Parent=0x00000009 SizeRef=147,224 Selected=0x2554AADD + DockNode ID=0x0000000A Parent=0x7C6B3D9B SizeRef=548,581 Split=X + DockNode ID=0x00000005 Parent=0x0000000A SizeRef=288,581 Split=Y Selected=0xEE087978 + DockNode ID=0x00000007 Parent=0x00000005 SizeRef=147,427 Selected=0xEE087978 + DockNode ID=0x00000008 Parent=0x00000005 SizeRef=147,152 Selected=0x62F4D00D + DockNode ID=0x00000006 Parent=0x0000000A SizeRef=510,581 Split=X + DockNode ID=0x00000001 Parent=0x00000006 SizeRef=299,581 Split=Y Selected=0xA4B861D9 + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=299,379 Selected=0xA4B861D9 + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=299,200 Selected=0xEDB425AD + DockNode ID=0x00000002 Parent=0x00000006 SizeRef=499,581 CentralNode=1 Selected=0x38519A65