Exporting fixed

This commit is contained in:
2025-08-03 18:28:01 +02:00
parent bbedfe6d35
commit 1a5abca8e1
4 changed files with 137 additions and 25 deletions

View File

@ -13,7 +13,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__)

View File

@ -1,5 +1,42 @@
# import dearpygui.dearpygui as dpg
# import numpy as np
# from .pipeline_stage_widget import PipelineStageWidget
# class ExportStage(PipelineStageWidget):
# name = "Export Image"
# register = True
# has_pipeline_in = True
# has_pipeline_out = False
# def __init__(self, manager, logger):
# super().__init__(manager, logger, default_stage_out="opened_image")
# self.manager.bus.subscribe(
# "process_full_res", self._on_process_full_res, True)
# def create_pipeline_stage_content(self):
# dpg.add_text("Some export fields")
# def _on_process_full_res(self, data):
# self.logger.info("Starting full res pipeline export")
# def on_pipeline_data(self, img):
# if img is None:
# return
# self.logger.info("low res image received, ignore")
# def on_full_res_pipeline_data(self, img):
# if img is None:
# return
# h, w, _ = img.shape
# self.logger.info(f"Full res image received: {w}x{h}")
import os
import dearpygui.dearpygui as dpg
import numpy as np
from PIL import Image
from .pipeline_stage_widget import PipelineStageWidget
@ -11,23 +48,95 @@ class ExportStage(PipelineStageWidget):
has_pipeline_out = False
def __init__(self, manager, logger):
super().__init__(manager, logger, default_stage_out="opened_image")
self.manager.bus.subscribe(
"process_full_res", self._on_process_full_res, True)
# we dont register an output stage — this widget only consumes
super().__init__(manager, logger, default_stage_out="unused")
# tags for our “Save As” dialog
self._save_dialog_tag = dpg.generate_uuid()
self._save_path = None
def create_pipeline_stage_content(self):
dpg.add_text("Some export fields")
# Button to pop up the file-save dialog
dpg.add_button(label="Save As…",
callback=lambda s,a,u: dpg.configure_item(self._save_dialog_tag, show=True))
def _on_process_full_res(self, data):
self.logger.info("Starting full res pipeline export")
# File dialog for choosing export path & extension
with dpg.file_dialog(
directory_selector=False,
show=False,
callback=self._on_save_selected,
tag=self._save_dialog_tag,
width=400,
height=300
):
dpg.add_file_extension("PNG {.png}")
dpg.add_file_extension("JPEG {.jpg,.jpeg}")
dpg.add_file_extension("TIFF {.tif,.tiff}")
dpg.add_file_extension("All files {.*}")
def on_pipeline_data(self, img):
with dpg.child_window(autosize_x=True, autosize_y=True, horizontal_scrollbar=True):
self.path_label = dpg.add_text("...")
def _on_save_selected(self, sender, app_data):
"""
Called when the user picks a filename in the Save As… dialog.
Stores the path for the next full-res pass.
"""
# app_data is a dict with 'current_path' and 'selections'
path = os.path.join(
app_data["current_path"],
app_data["file_name"]
)
self._save_path = path
self.logger.info(f"Export path set to: {path}")
dpg.set_value(self.path_label, path)
def on_pipeline_data(self, img: np.ndarray):
# ignore all previews
return
def on_full_res_pipeline_data(self, img: np.ndarray):
"""
Receives the full-resolution NumPy image when the user fires
the “Run full-res pipeline” action. Saves via Pillow.
"""
if img is None:
self.logger.error("on_full_res_pipeline_data called with None image")
return
if not self._save_path:
self.logger.warning("No export path set — click Save As… first")
return
self.logger.info("low res image received, ignore")
def on_full_res_pipeline_data(self, img):
if img is None:
return
h, w, _ = img.shape
self.logger.info(f"Full res image received: {w}x{h}")
# Decide bit depth by extension
ext = os.path.splitext(self._save_path)[-1].lower()
# Convert floats → uint; or leave ints alone
if np.issubdtype(img.dtype, np.floating):
if ext in (".tif", ".tiff"):
arr = np.clip(img * 65535.0, 0, 65535).astype(np.uint16)
else:
arr = np.clip(img * 255.0, 0, 255).astype(np.uint8)
else:
arr = img
# Determine PIL mode
mode = None
if arr.ndim == 2:
mode = "L"
elif arr.ndim == 3:
c = arr.shape[2]
if c == 3:
mode = "RGB"
elif c == 4:
mode = "RGBA"
try:
im = Image.fromarray(arr, mode) if mode else Image.fromarray(arr)
# JPEG doesnt support alpha — drop it
if ext in (".jpg", ".jpeg") and im.mode == "RGBA":
im = im.convert("RGB")
im.save(self._save_path)
except Exception as e:
self.logger.error(f"Failed to save image to {self._save_path}: {e}")
else:
self.logger.info(f"Saved full-resolution image to {self._save_path}")

View File

@ -41,6 +41,9 @@ class HistogramWidget(PipelineStageWidget):
self.img = img
self.needs_redraw = True
def on_full_res_pipeline_data(self, img):
pass
def update(self):
# TODO move calculations to on_pipeline_data
if not self.needs_redraw or self.img is None:

View File

@ -88,7 +88,7 @@ DockId=0x00000011,0
[Window][###35]
Pos=272,19
Size=710,588
Size=700,588
Collapsed=0
DockId=0x00000033,0
@ -100,7 +100,7 @@ DockId=0x00000020,0
[Window][###81]
Pos=272,609
Size=710,191
Size=700,191
Collapsed=0
DockId=0x00000010,0
@ -117,8 +117,8 @@ Collapsed=0
DockId=0x00000036,1
[Window][###111]
Pos=974,630
Size=226,170
Pos=974,666
Size=226,134
Collapsed=0
DockId=0x00000015,0
@ -142,7 +142,7 @@ DockId=0x00000022,0
[Window][###103]
Pos=984,19
Size=216,679
Size=216,634
Collapsed=0
DockId=0x0000003F,0
@ -208,7 +208,7 @@ DockId=0x00000031,0
[Window][###95]
Pos=974,19
Size=226,609
Size=226,645
Collapsed=0
DockId=0x00000014,0
@ -225,14 +225,14 @@ Collapsed=0
DockId=0x00000039,0
[Window][###123]
Pos=984,634
Size=216,166
Pos=984,655
Size=216,145
Collapsed=0
DockId=0x0000003E,0
[Window][###102]
Pos=984,19
Size=216,781
Size=216,613
Collapsed=0
DockId=0x0000003D,0
@ -286,8 +286,8 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Siz
DockNode ID=0x00000021 Parent=0x0000001D SizeRef=216,579 Split=Y Selected=0x714F2F7B
DockNode ID=0x00000019 Parent=0x00000021 SizeRef=216,391 Selected=0x714F2F7B
DockNode ID=0x0000001A Parent=0x00000021 SizeRef=216,388 Split=Y Selected=0x7E9438EA
DockNode ID=0x00000014 Parent=0x0000001A SizeRef=216,609 Selected=0x36EF55AB
DockNode ID=0x00000015 Parent=0x0000001A SizeRef=216,170 Selected=0x7E9438EA
DockNode ID=0x00000014 Parent=0x0000001A SizeRef=216,645 Selected=0x36EF55AB
DockNode ID=0x00000015 Parent=0x0000001A SizeRef=216,134 Selected=0x7E9438EA
DockNode ID=0x00000022 Parent=0x0000001D SizeRef=216,200 Selected=0x0D80EC84
DockNode ID=0x0000001E Parent=0x0000001C SizeRef=216,435 Selected=0x7740BFE4
DockNode ID=0x00000024 Parent=0x00000027 SizeRef=216,781 Split=Y Selected=0xCF08B82F