shinestacker 1.5.4__py3-none-any.whl → 1.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shinestacker might be problematic. Click here for more details.

Files changed (45) hide show
  1. shinestacker/_version.py +1 -1
  2. shinestacker/algorithms/multilayer.py +1 -1
  3. shinestacker/algorithms/stack.py +17 -9
  4. shinestacker/app/args_parser_opts.py +4 -0
  5. shinestacker/app/gui_utils.py +10 -2
  6. shinestacker/app/main.py +8 -3
  7. shinestacker/app/project.py +7 -3
  8. shinestacker/app/retouch.py +8 -1
  9. shinestacker/app/settings_dialog.py +171 -0
  10. shinestacker/config/app_config.py +30 -0
  11. shinestacker/config/constants.py +3 -0
  12. shinestacker/config/gui_constants.py +4 -2
  13. shinestacker/config/settings.py +110 -0
  14. shinestacker/core/core_utils.py +3 -12
  15. shinestacker/core/logging.py +3 -2
  16. shinestacker/gui/action_config.py +6 -5
  17. shinestacker/gui/action_config_dialog.py +17 -74
  18. shinestacker/gui/config_dialog.py +78 -0
  19. shinestacker/gui/main_window.py +6 -6
  20. shinestacker/gui/menu_manager.py +2 -0
  21. shinestacker/gui/new_project.py +2 -1
  22. shinestacker/gui/project_controller.py +8 -6
  23. shinestacker/gui/project_model.py +16 -1
  24. shinestacker/gui/recent_file_manager.py +3 -21
  25. shinestacker/retouch/base_filter.py +1 -1
  26. shinestacker/retouch/display_manager.py +48 -7
  27. shinestacker/retouch/image_editor_ui.py +27 -36
  28. shinestacker/retouch/image_view_status.py +4 -1
  29. shinestacker/retouch/image_viewer.py +17 -9
  30. shinestacker/retouch/io_gui_handler.py +96 -44
  31. shinestacker/retouch/io_threads.py +78 -0
  32. shinestacker/retouch/layer_collection.py +12 -0
  33. shinestacker/retouch/overlaid_view.py +13 -5
  34. shinestacker/retouch/paint_area_manager.py +30 -0
  35. shinestacker/retouch/sidebyside_view.py +32 -16
  36. shinestacker/retouch/transformation_manager.py +1 -3
  37. shinestacker/retouch/undo_manager.py +15 -13
  38. shinestacker/retouch/view_strategy.py +79 -26
  39. {shinestacker-1.5.4.dist-info → shinestacker-1.6.1.dist-info}/METADATA +1 -1
  40. {shinestacker-1.5.4.dist-info → shinestacker-1.6.1.dist-info}/RECORD +44 -39
  41. shinestacker/retouch/io_manager.py +0 -69
  42. {shinestacker-1.5.4.dist-info → shinestacker-1.6.1.dist-info}/WHEEL +0 -0
  43. {shinestacker-1.5.4.dist-info → shinestacker-1.6.1.dist-info}/entry_points.txt +0 -0
  44. {shinestacker-1.5.4.dist-info → shinestacker-1.6.1.dist-info}/licenses/LICENSE +0 -0
  45. {shinestacker-1.5.4.dist-info → shinestacker-1.6.1.dist-info}/top_level.txt +0 -0
@@ -6,13 +6,10 @@ from .. config.gui_constants import gui_constants
6
6
  class UndoManager(QObject):
7
7
  stack_changed = Signal(bool, str, bool, str)
8
8
 
9
- def __init__(self, transformation_manager):
9
+ def __init__(self, transformation_manager, paint_area_manager):
10
10
  super().__init__()
11
11
  self.transformation_manager = transformation_manager
12
- self.x_start = None
13
- self.y_start = None
14
- self.x_end = None
15
- self.y_end = None
12
+ self.paint_area_manager = paint_area_manager
16
13
  self.undo_stack = None
17
14
  self.redo_stack = None
18
15
  self.reset()
@@ -24,22 +21,25 @@ class UndoManager(QObject):
24
21
  self.stack_changed.emit(False, "", False, "")
25
22
 
26
23
  def reset_undo_area(self):
27
- self.x_end = self.y_end = 0
28
- self.x_start = self.y_start = gui_constants.MAX_UNDO_SIZE
24
+ self.paint_area_manager.reset()
29
25
 
30
26
  def extend_undo_area(self, x_start, y_start, x_end, y_end):
31
- self.x_start = min(self.x_start, x_start)
32
- self.y_start = min(self.y_start, y_start)
33
- self.x_end = max(self.x_end, x_end)
34
- self.y_end = max(self.y_end, y_end)
27
+ self.paint_area_manager.extend(x_start, y_start, x_end, y_end)
28
+
29
+ def paint_area(self):
30
+ return self.paint_area_manager.area()
31
+
32
+ def set_paint_area(self, x_start, y_start, x_end, y_end):
33
+ self.paint_area_manager.set_area(x_start, y_start, x_end, y_end)
35
34
 
36
35
  def save_undo_state(self, layer, description):
37
36
  if layer is None:
38
37
  return
39
38
  self.redo_stack = []
39
+ x_start, y_start, x_end, y_end = self.paint_area()
40
40
  undo_state = {
41
- 'master': layer[self.y_start:self.y_end, self.x_start:self.x_end],
42
- 'area': (self.x_start, self.y_start, self.x_end, self.y_end),
41
+ 'master': layer[y_start:y_end, x_start:x_end].copy(),
42
+ 'area': (x_start, y_start, x_end, y_end),
43
43
  'description': description
44
44
  }
45
45
  if len(self.undo_stack) >= gui_constants.MAX_UNDO_SIZE:
@@ -54,6 +54,7 @@ class UndoManager(QObject):
54
54
  return False
55
55
  undo_state = self.undo_stack.pop()
56
56
  x_start, y_start, x_end, y_end = undo_state['area']
57
+ self.set_paint_area(x_start, y_start, x_end, y_end)
57
58
  redo_state = {
58
59
  'master': layer[y_start:y_end, x_start:x_end].copy(),
59
60
  'area': (x_start, y_start, x_end, y_end),
@@ -80,6 +81,7 @@ class UndoManager(QObject):
80
81
  return False
81
82
  redo_state = self.redo_stack.pop()
82
83
  x_start, y_start, x_end, y_end = redo_state['area']
84
+ self.set_paint_area(x_start, y_start, x_end, y_end)
83
85
  undo_state = {
84
86
  'master': layer[y_start:y_end, x_start:x_end].copy(),
85
87
  'area': (x_start, y_start, x_end, y_end),
@@ -9,6 +9,7 @@ from PySide6.QtWidgets import (
9
9
  QGraphicsEllipseItem, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem,
10
10
  QGraphicsItemGroup, QGraphicsPathItem)
11
11
  from .. config.gui_constants import gui_constants
12
+ from .. config.app_config import AppConfig
12
13
  from .layer_collection import LayerCollectionHandler
13
14
  from .brush_gradient import create_default_brush_gradient
14
15
  from .brush_preview import BrushPreviewItem
@@ -78,10 +79,9 @@ class BrushCursor(QGraphicsItemGroup):
78
79
 
79
80
  class ViewSignals:
80
81
  temp_view_requested = Signal(bool)
81
- brush_operation_started = Signal(QPoint)
82
- brush_operation_continued = Signal(QPoint)
83
- brush_operation_ended = Signal()
82
+ end_copy_brush_area_requested = Signal()
84
83
  brush_size_change_requested = Signal(int) # +1 or -1
84
+ needs_update_requested = Signal()
85
85
 
86
86
 
87
87
  class ImageGraphicsViewBase(QGraphicsView):
@@ -102,9 +102,12 @@ class ImageGraphicsViewBase(QGraphicsView):
102
102
 
103
103
 
104
104
  class ViewStrategy(LayerCollectionHandler):
105
- def __init__(self, layer_collection, status):
105
+ def __init__(self, layer_collection, status, brush_tool, paint_area_manager):
106
106
  LayerCollectionHandler.__init__(self, layer_collection)
107
107
  self.status = status
108
+ self.brush_tool = brush_tool
109
+ self.paint_area_manager = paint_area_manager
110
+ self.mask_layer = None
108
111
  self.brush = None
109
112
  self.brush_cursor = None
110
113
  self.brush_preview = BrushPreviewItem(layer_collection)
@@ -121,6 +124,8 @@ class ViewStrategy(LayerCollectionHandler):
121
124
  self.last_mouse_pos = None
122
125
  self.last_update_time = QTime.currentTime()
123
126
  self.last_color_update_time = 0
127
+ self.last_cursor_update_time = 0
128
+ self.enable_paint = True
124
129
 
125
130
  @abstractmethod
126
131
  def create_pixmaps(self):
@@ -245,6 +250,8 @@ class ViewStrategy(LayerCollectionHandler):
245
250
 
246
251
  def set_cursor_style(self, style):
247
252
  self.cursor_style = style
253
+ if style != 'simple' and self.brush_cursor:
254
+ self.brush_cursor.setBrush(Qt.NoBrush)
248
255
  if style == 'preview':
249
256
  self.show_brush_preview()
250
257
  self.update_brush_cursor()
@@ -270,6 +277,25 @@ class ViewStrategy(LayerCollectionHandler):
270
277
  view.verticalScrollBar().setValue(self.status.v_scroll)
271
278
  self.arrange_images()
272
279
 
280
+ def update_master_display_area(self):
281
+ if self.empty():
282
+ return
283
+ x_start, y_start, x_end, y_end = self.paint_area_manager.area()
284
+ dirty_region = self.master_layer()[y_start:y_end, x_start:x_end]
285
+ qimage = self.numpy_to_qimage(dirty_region)
286
+ if not qimage:
287
+ return
288
+ pixmap = QPixmap.fromImage(qimage)
289
+ master_pixmap_item = self.get_master_pixmap()
290
+ current_pixmap = master_pixmap_item.pixmap()
291
+ if current_pixmap.isNull():
292
+ self.update_master_display()
293
+ return
294
+ painter = QPainter(current_pixmap)
295
+ painter.drawPixmap(x_start, y_start, pixmap)
296
+ painter.end()
297
+ master_pixmap_item.setPixmap(current_pixmap)
298
+
273
299
  def update_master_display(self):
274
300
  self.update_view_display(
275
301
  self.master_layer(),
@@ -283,7 +309,7 @@ class ViewStrategy(LayerCollectionHandler):
283
309
  self.update_view_display(
284
310
  self.current_layer(),
285
311
  self.get_current_pixmap(),
286
- self.get_current_view(),
312
+ self.get_current_scene(),
287
313
  self.get_current_view())
288
314
 
289
315
  def update_cursor_pen_width(self):
@@ -694,18 +720,60 @@ class ViewStrategy(LayerCollectionHandler):
694
720
  self.status.set_scroll(view.horizontalScrollBar().value(),
695
721
  view.verticalScrollBar().value())
696
722
 
723
+ def copy_brush_area_to_master(self, view_pos):
724
+ if self.layer_stack() is None or self.number_of_layers() == 0:
725
+ return
726
+ area = self.brush_tool.apply_brush_operation(
727
+ self.master_layer_copy(),
728
+ self.current_layer(),
729
+ self.master_layer(), self.mask_layer,
730
+ view_pos)
731
+ self.paint_area_manager.extend(*area)
732
+
733
+ def begin_copy_brush_area(self, pos):
734
+ self.mask_layer = self.blank_layer().copy()
735
+ self.copy_master_layer()
736
+ self.paint_area_manager.reset()
737
+ self.copy_brush_area_to_master(pos)
738
+ self.needs_update_requested.emit()
739
+
740
+ def continue_copy_brush_area(self, pos):
741
+ self.copy_brush_area_to_master(pos)
742
+ self.needs_update_requested.emit()
743
+
744
+ def mouse_press_event(self, event):
745
+ if self.empty():
746
+ return
747
+ if self.enable_paint and event.button() & Qt.LeftButton and self.has_master_layer():
748
+ if self.space_pressed:
749
+ self.scrolling = True
750
+ self.last_mouse_pos = event.position()
751
+ self.setCursor(Qt.ClosedHandCursor)
752
+ else:
753
+ self.last_brush_pos = event.position()
754
+ self.begin_copy_brush_area(event.position().toPoint())
755
+ self.dragging = True
756
+ if not self.scrolling:
757
+ self.show_brush_cursor()
758
+
697
759
  def mouse_move_event(self, event):
698
760
  if self.empty():
699
761
  return
762
+ current_time = time.time() * 1000 # ms
763
+ cursor_update_interval = AppConfig.get('cursor_update_time')
764
+ if current_time - self.last_cursor_update_time < cursor_update_interval:
765
+ return
766
+ self.last_cursor_update_time = current_time
700
767
  position = event.position()
701
768
  brush_size = self.brush.size
702
769
  if not self.space_pressed:
703
770
  self.update_brush_cursor()
704
- if self.dragging and event.buttons() & Qt.LeftButton:
771
+ if self.enable_paint and self.dragging and event.buttons() & Qt.LeftButton:
705
772
  current_time = QTime.currentTime()
706
- if self.last_update_time.msecsTo(current_time) >= gui_constants.PAINT_REFRESH_TIMER:
707
- min_step = brush_size * \
708
- gui_constants.MIN_MOUSE_STEP_BRUSH_FRACTION * self.zoom_factor()
773
+ paint_refresh_time = AppConfig.get('paint_refresh_time')
774
+ if self.last_update_time.msecsTo(current_time) >= paint_refresh_time:
775
+ min_step = AppConfig.get('min_mouse_step_brush_fraction')
776
+ min_step = brush_size * min_step * self.zoom_factor()
709
777
  x, y = position.x(), position.y()
710
778
  xp, yp = self.last_brush_pos.x(), self.last_brush_pos.y()
711
779
  distance = math.sqrt((x - xp)**2 + (y - yp)**2)
@@ -716,7 +784,7 @@ class ViewStrategy(LayerCollectionHandler):
716
784
  for i in range(0, n_steps + 1):
717
785
  pos = QPoint(self.last_brush_pos.x() + i * delta_x,
718
786
  self.last_brush_pos.y() + i * delta_y)
719
- self.brush_operation_continued.emit(pos)
787
+ self.continue_copy_brush_area(pos)
720
788
  self.last_brush_pos = position
721
789
  self.last_update_time = current_time
722
790
  if self.scrolling and event.buttons() & Qt.LeftButton:
@@ -728,21 +796,6 @@ class ViewStrategy(LayerCollectionHandler):
728
796
  self.last_mouse_pos = position
729
797
  self.scroll_view(master_view, delta.x(), delta.y())
730
798
 
731
- def mouse_press_event(self, event):
732
- if self.empty():
733
- return
734
- if event.button() == Qt.LeftButton and self.has_master_layer():
735
- if self.space_pressed:
736
- self.scrolling = True
737
- self.last_mouse_pos = event.position()
738
- self.setCursor(Qt.ClosedHandCursor)
739
- else:
740
- self.last_brush_pos = event.position()
741
- self.brush_operation_started.emit(event.position().toPoint())
742
- self.dragging = True
743
- if not self.scrolling:
744
- self.show_brush_cursor()
745
-
746
799
  def mouse_release_event(self, event):
747
800
  if self.empty():
748
801
  return
@@ -759,4 +812,4 @@ class ViewStrategy(LayerCollectionHandler):
759
812
  self.last_mouse_pos = None
760
813
  elif self.dragging:
761
814
  self.dragging = False
762
- self.brush_operation_ended.emit()
815
+ self.end_copy_brush_area_requested.emit()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shinestacker
3
- Version: 1.5.4
3
+ Version: 1.6.1
4
4
  Summary: ShineStacker
5
5
  Author-email: Luca Lista <luka.lista@gmail.com>
6
6
  License-Expression: LGPL-3.0
@@ -1,5 +1,5 @@
1
1
  shinestacker/__init__.py,sha256=uq2fjAw2z_6TpH3mOcWFZ98GoEPRsNhTAK8N0MMm_e8,448
2
- shinestacker/_version.py,sha256=HfPNw49U2WWQOQlRuPSUUE8x8nkPx62dtjaGJfbhyTk,21
2
+ shinestacker/_version.py,sha256=DH87PU9LcZeI_uvttoH-lyhGyb55UShgeFtsG2qIZgE,21
3
3
  shinestacker/algorithms/__init__.py,sha256=1FwVJ3w9GGbFFkjYJRUedTvcdE4j0ieSgaH9RC9iCY4,877
4
4
  shinestacker/algorithms/align.py,sha256=mb44u-YxZI1TTSHz81nRpX_2c8awlOhnGrK0LyfTQeQ,33543
5
5
  shinestacker/algorithms/align_auto.py,sha256=pJetw6zZEWQLouzcelkI8gD4cPiOp887ePXzVbm0E6Q,3800
@@ -9,54 +9,58 @@ shinestacker/algorithms/base_stack_algo.py,sha256=RzxvLDHqxqhnAl83u2onjvfsRea1qG
9
9
  shinestacker/algorithms/denoise.py,sha256=GL3Z4_6MHxSa7Wo4ZzQECZS87tHBFqO0sIVF_jPuYQU,426
10
10
  shinestacker/algorithms/depth_map.py,sha256=nRBrZQWbdUqFOtYMEQx9UNdnybrBTeAOr1eV91FlN8U,5611
11
11
  shinestacker/algorithms/exif.py,sha256=SM4ZDDe8hCJ3xY6053FNndOiwzEStzdp0WrXurlcHVc,9429
12
- shinestacker/algorithms/multilayer.py,sha256=WlB4L5oY9qra3w7Qahg-tqO6S_s3pMB_LmGR8PPR_7w,9904
12
+ shinestacker/algorithms/multilayer.py,sha256=EEMDr2NlCU9DCFO5ykbBrY-2q9oBUD0-ctm7x0IXU5U,9911
13
13
  shinestacker/algorithms/noise_detection.py,sha256=myUiLKYC3Ph62j28upUd_7rZum_JpqyxiN33nZN35zE,9346
14
14
  shinestacker/algorithms/pyramid.py,sha256=Z7tlp8Hh3ploAXJCr0VNe33d8H9GNrlqHXq_LapgRwo,8205
15
15
  shinestacker/algorithms/pyramid_auto.py,sha256=fl_jXNYLWsBiX0M0UghzCLqai0SGXlmKYHU7Z9SUYSo,6173
16
16
  shinestacker/algorithms/pyramid_tiles.py,sha256=ZBWIeifkDOIVFF4SCyspRZHSj6K_1P3dk4WLmuo53RU,12213
17
17
  shinestacker/algorithms/sharpen.py,sha256=h7PMJBYxucg194Usp_6pvItPUMFYbT-ebAc_-7XBFUw,949
18
- shinestacker/algorithms/stack.py,sha256=V9YX0CbNWrgAo7_uti64rmmuwU6RsRcjDoBpsES4aSE,5137
18
+ shinestacker/algorithms/stack.py,sha256=p9bLCbMjDMm7rX_FRfgXRTEJAj3GhNPOCvwePE9hVmc,5549
19
19
  shinestacker/algorithms/stack_framework.py,sha256=OMrjD5dKquHQXhM7TfLRExDsqN1n938WWhGAfkPYLZM,13883
20
20
  shinestacker/algorithms/utils.py,sha256=l6GJpEXpzDr_ml9Not03a1_F7wYvPwn8JkEWDuNwL9o,12116
21
21
  shinestacker/algorithms/vignetting.py,sha256=gJOv-FN3GnTgaVn70W_6d-qbw3WmqinDiO9oL053cus,10351
22
22
  shinestacker/algorithms/white_balance.py,sha256=PMKsBtxOSn5aRr_Gkx1StHS4eN6kBN2EhNnhg4UG24g,501
23
23
  shinestacker/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  shinestacker/app/about_dialog.py,sha256=pkH7nnxUP8yc0D3vRGd1jRb5cwi1nDVbQRk_OC9yLk8,4144
25
- shinestacker/app/args_parser_opts.py,sha256=TlfMR5GBd6Zz9wZNrN7CGJ1_hm1FnLZn5EoP5mkRHrk,776
26
- shinestacker/app/gui_utils.py,sha256=fSpkwPXTON_l676UHdAnJNrGq7BPbSlPOiHpOF_LZaI,2519
25
+ shinestacker/app/args_parser_opts.py,sha256=c6IUXOI0SJIFckPWPXYnwmBmdNnOcrtvU5S8hUDU_AQ,979
26
+ shinestacker/app/gui_utils.py,sha256=EGZejp0XZXRLVa_Wd_2VYAwK3oe9hMSoZzNgT7NUNRw,2986
27
27
  shinestacker/app/help_menu.py,sha256=g8lKG_xZmXtNQaC3SIRzyROKVWva_PLEgZsQWh6zUcQ,499
28
- shinestacker/app/main.py,sha256=UkWz4jvaBPH4Fs-arHe8H0NqKVTA4B_1e9BRn9S5oFo,10963
28
+ shinestacker/app/main.py,sha256=OQwPJvhrsAThv9kmiqtX7CX-smfwXdFXagQmGsQdmuU,11258
29
29
  shinestacker/app/open_frames.py,sha256=bsu32iJSYJQLe_tQQbvAU5DuMDVX6MRuNdE7B5lojZc,1488
30
- shinestacker/app/project.py,sha256=_kopeyb8e5JAA3XjNXoCshlYBfT5Avw-kTqiJN4GVlo,2805
31
- shinestacker/app/retouch.py,sha256=On2oV2QonfMziQAvRkO6qw-h6E6wWQE0svKzZQKHeyg,2687
30
+ shinestacker/app/project.py,sha256=jn4LlMTMxrJeAyQQrR5nVS4Di1rOrjiOJ8LOg0CZbwE,3045
31
+ shinestacker/app/retouch.py,sha256=fyk74fgpeoBqY1mRJiXWf1hRzSeG7xXFx4V-Sphr3Kg,3069
32
+ shinestacker/app/settings_dialog.py,sha256=0P3nqqZEiTIFgidW1_e3Q_zE7NbAouNsuj-yNsU41vk,8192
32
33
  shinestacker/config/__init__.py,sha256=aXxi-LmAvXd0daIFrVnTHE5OCaYeK1uf1BKMr7oaXQs,197
34
+ shinestacker/config/app_config.py,sha256=rM1Rndk1GDa5c0AhcVNEN9zSAzxPZixzQYfjODbJUwE,771
33
35
  shinestacker/config/config.py,sha256=eBko2D3ADhLTIm9X6hB_a_WsIjwgfE-qmBVkhP1XSvc,1636
34
- shinestacker/config/constants.py,sha256=EEdr7pZg4JpbIjUWaP7kJQfTuBB85FN739myDNAfn8A,8301
35
- shinestacker/config/gui_constants.py,sha256=55Qr0KzrTx8eQHCkT-EVabSiD59VvLZIh5o2cA9028s,2791
36
+ shinestacker/config/constants.py,sha256=Z7QjaklrYYsPjTX68Tjyh_wCOuYyQPBR8dnYrZfNwA8,8376
37
+ shinestacker/config/gui_constants.py,sha256=PNxzwmVEppJ2mV_vwp68NhWzJOEitVy1Pk9SwSmRsho,2882
38
+ shinestacker/config/settings.py,sha256=4p4r6wKOCbttzfH9tyHQSTd-iv-GfgCd1LxI3C7WIjU,3861
36
39
  shinestacker/core/__init__.py,sha256=IUEIx6SQ3DygDEHN3_E6uKpHjHtUa4a_U_1dLd_8yEU,484
37
40
  shinestacker/core/colors.py,sha256=kr_tJA1iRsdck2JaYDb2lS-codZ4Ty9gdu3kHfiWvuM,1340
38
- shinestacker/core/core_utils.py,sha256=1LYj19Dfc9jZN9-4dlf1paximDH5WZYa7DXvKr7R7QY,1719
41
+ shinestacker/core/core_utils.py,sha256=wmpu9_idDRe866JFEylMvGPJRT8lSVhikK9PljcbPSI,1392
39
42
  shinestacker/core/exceptions.py,sha256=2-noG-ORAGdvDhL8jBQFs0xxZS4fI6UIkMqrWekgk2c,1618
40
43
  shinestacker/core/framework.py,sha256=QaTfnzEUHwzlbyFG7KzeyteckTSWHWEEJE4d5Tc8H18,11015
41
- shinestacker/core/logging.py,sha256=9SuSSy9Usbh7zqmLYMqkmy-VBkOJW000lwqAR0XQs30,3067
44
+ shinestacker/core/logging.py,sha256=T-ZTGYjN2n2_5Vu2mBJ2id9qj1c9NA79OD2H2w-W0nM,3096
42
45
  shinestacker/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- shinestacker/gui/action_config.py,sha256=HeDbRy3YSl9IldLZBCdXj7eWIfTTHmUqC_AW9LJU2Pk,25859
44
- shinestacker/gui/action_config_dialog.py,sha256=Jp0LVhE0gdz70ve47LCL5N2sSLuj8_arZb4xL50_pkk,41101
46
+ shinestacker/gui/action_config.py,sha256=Xv7SGbhPl1F_dUnU04VBt_E-wIItnN_q6QuhU_d9GfI,25929
47
+ shinestacker/gui/action_config_dialog.py,sha256=QN95FiVPYL6uin2sYO5F7tq6G5rBWh9yRkeTVvwKrwU,38341
45
48
  shinestacker/gui/base_form_dialog.py,sha256=KAUQNtmJazttmOIe4E4pFifbtvcByTAhtCmcIYeA4UE,766
46
49
  shinestacker/gui/colors.py,sha256=-HaFprDuzRSKjXoZfX1rdOuvawQAkazqdgLBEiZcFII,1476
50
+ shinestacker/gui/config_dialog.py,sha256=yt3nvh0HPHQuCn3AFlzlIHUJnnxcz-Rrw3W3jS9ZYiE,3447
47
51
  shinestacker/gui/flow_layout.py,sha256=3yBU_z7VtvHKpx1H97CHVd81eq9pe1Dcja2EZBGGKcI,3791
48
52
  shinestacker/gui/folder_file_selection.py,sha256=IYWfZQFkoD5iO7zJ7BxVVDP9F3Dc0EXLILAhL4q-Cb8,4117
49
53
  shinestacker/gui/gui_images.py,sha256=k39DpdsxcmYoRdHNNZj6OpFAas0GOHS4JSG542wfheg,5728
50
54
  shinestacker/gui/gui_logging.py,sha256=kiZcrC2AFYCWgPZo0O5SKw-E5cFrezwf4anS3HjPuNw,8168
51
55
  shinestacker/gui/gui_run.py,sha256=zr7x4BVmM0n_ZRsSEaJVVKvHSWHuwhftgkUvgeg90gU,15767
52
- shinestacker/gui/main_window.py,sha256=zOxIRn6urmnmfDOR1JJ3n6xsT1qryOt1s7kZxwh-qYI,25202
53
- shinestacker/gui/menu_manager.py,sha256=legmYEpQxuzEQoDhxMUWiwCcYTXwd-uRfAju-Nymy8g,11664
54
- shinestacker/gui/new_project.py,sha256=z8e3EhRMB-KtoPwYQSiKLSOQ2dS0-Okm7zVw21B7zy8,16391
55
- shinestacker/gui/project_controller.py,sha256=7vSyoxepplJrf0VsbPrZkMqtHW6rtPgEOZgdPsOPVoQ,16490
56
+ shinestacker/gui/main_window.py,sha256=0G-ZjSVKY_rCK_DmstRn3wxOdvS5i3Ba3FBR2ijxpe0,25220
57
+ shinestacker/gui/menu_manager.py,sha256=q4m3cBSxUR68gexpwfIROVRKJ86zp-XPZVrohh1PfQU,11786
58
+ shinestacker/gui/new_project.py,sha256=XMv1ttYrkuqaN9629anXtVSn1bxosgyJpxSFPjlVryU,16437
59
+ shinestacker/gui/project_controller.py,sha256=MYv8QJNXUdc7r1K5D6LnBbds9YalCKSAo_CaO6b0TO8,16636
56
60
  shinestacker/gui/project_converter.py,sha256=Gmna0HwbvACcXiX74TaQYumif8ZV8sZ2APLTMM-L1mU,7436
57
61
  shinestacker/gui/project_editor.py,sha256=lSgQ42IoaobHs-NQQWT88Qhg5l7nu5ejxAO5VgIupr8,25498
58
- shinestacker/gui/project_model.py,sha256=eRUmH3QmRzDtPtZoxgT6amKzN8_5XzwjHgEJeL-_JOE,4263
59
- shinestacker/gui/recent_file_manager.py,sha256=FtatDGUqwmXXchepZdA0YRNzqCRS9Qp4EL9R9Wsg5qE,3731
62
+ shinestacker/gui/project_model.py,sha256=9dId8N-np4YHDpz_wO20Mvd06np3YKlej-0TMWaA_WE,4833
63
+ shinestacker/gui/recent_file_manager.py,sha256=010bciuirKLiVCfOAKs0uFlB3iUjHNBlPX_9K2vH5j0,2916
60
64
  shinestacker/gui/select_path_widget.py,sha256=HSwgSr702w5Et4c-6nkRXnIpm1KFqKJetAF5xQNa5zI,1017
61
65
  shinestacker/gui/sys_mon.py,sha256=zU41YYVeqQ1-v6oGIh2_BFzQWq87keN-398Wdm59-Nk,3526
62
66
  shinestacker/gui/tab_widget.py,sha256=VgRmuktWXCgbXbV7c1Tho0--W5_EmmzXPfzRZgwhGfg,2965
@@ -71,35 +75,36 @@ shinestacker/gui/img/forward-button-icon.png,sha256=lNw86T4TOEd_uokHYF8myGSGUXzd
71
75
  shinestacker/gui/img/play-button-round-icon.png,sha256=9j6Ks9mOGa-2cXyRFpimepAAvSaHzqJKBfxShRb4_dE,4595
72
76
  shinestacker/gui/img/plus-round-line-icon.png,sha256=LS068Hlu-CeBvJuB3dwwdJg1lZq6D5MUIv53lu1yKJA,7534
73
77
  shinestacker/retouch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- shinestacker/retouch/base_filter.py,sha256=zpedVvTpof8BsENWdDeu9PDul7Uw0UAXUSsyLZjbcpg,11265
78
+ shinestacker/retouch/base_filter.py,sha256=ObrAcwZv9YJbIhWMHcryHEEj41oAk9hfHmE4phEd1gE,11263
75
79
  shinestacker/retouch/brush.py,sha256=dzD2FzSpBIPdJRmTZobcrQ1FrVd3tF__ZPnUplNE72s,357
76
80
  shinestacker/retouch/brush_gradient.py,sha256=F5SFhyzl8YTMqjJU3jK8BrIlLCYLUvITd5wz3cQE4xk,1453
77
81
  shinestacker/retouch/brush_preview.py,sha256=cOFVMCbEsgR_alzmr_-LLghtGU_unrE-hAjLHcvrZAY,5484
78
82
  shinestacker/retouch/brush_tool.py,sha256=8uVncTA375uC3Nhp2YM0eZjpOR-nN47i2eGjN8tJzOU,8714
79
83
  shinestacker/retouch/denoise_filter.py,sha256=UpNKbFs7uArdglEej8AUHan7oCVYV5E7HNzkovj7XMQ,571
80
- shinestacker/retouch/display_manager.py,sha256=vRmaSfHP47rYQrqvJ3flx_GgMJnMZDfQO4PXTfKcpqE,8505
84
+ shinestacker/retouch/display_manager.py,sha256=fTZTGbvmX5DXagexuvbNgOF5GiH2Vv-stLUQQwoglp8,10181
81
85
  shinestacker/retouch/exif_data.py,sha256=LF-fRXW-reMq-xJ_QRE5j8DC2LVGKIlC6MR3QbC1cdg,1896
82
86
  shinestacker/retouch/file_loader.py,sha256=z02-A8_uDZxayI1NFTxT2GVUvEBWStchX9hlN1o5-0U,4784
83
87
  shinestacker/retouch/filter_manager.py,sha256=tOGIWj5HjViL1-iXHkd91X-sZ1c1G531pDmLO0x6zx0,866
84
88
  shinestacker/retouch/icon_container.py,sha256=6gw1HO1bC2FrdB4dc_iH81DQuLjzuvRGksZ2hKLT9yA,585
85
- shinestacker/retouch/image_editor_ui.py,sha256=-UVWeFSnXyHOG7XEIl9wNhiCjH5R0UFeM2VOg5R6ozs,33435
86
- shinestacker/retouch/image_view_status.py,sha256=bdIhsXiYXm7eyjkTGWkw5PRShzaF_by-g7daqgmhwjM,1858
87
- shinestacker/retouch/image_viewer.py,sha256=H8w-ORug1aKf7X3FeSX4lQV-a0IewZ9OVG1-50BK4cE,4452
88
- shinestacker/retouch/io_gui_handler.py,sha256=UOnrFT00s0075Ng_yJACJX9TP8UT9mQOWXwQwNAfpMw,12079
89
- shinestacker/retouch/io_manager.py,sha256=JUAA--AK0mVa1PTErJTnBFjaXIle5Qs7Ow0Wkd8at0o,2437
90
- shinestacker/retouch/layer_collection.py,sha256=fZlGrkm9-Ycc7AOzFSpImhafiTieBeCZRk-UlvlFHbo,5819
91
- shinestacker/retouch/overlaid_view.py,sha256=KBwuzC2OQsK7rdtFAKrSvXrHrEV1SiOoZhxPCQLGkvg,6734
89
+ shinestacker/retouch/image_editor_ui.py,sha256=r2S4Fgyi3xQi2XeqlVYTUxZ9YlPZe2rMaIpvIak8Aog,33181
90
+ shinestacker/retouch/image_view_status.py,sha256=2rWi2ugdyjMhWCtRJkwOnb7-tCtVfnGfCY_54qpZhwM,1970
91
+ shinestacker/retouch/image_viewer.py,sha256=xf1vYZRPb9ClCQbqrqAFhPubdqIIpku7DgcY8O5bvYU,4694
92
+ shinestacker/retouch/io_gui_handler.py,sha256=tyHMmR6_uE_IEI-CIcJibVhupxarKYHzX2fuhnesHaI,14616
93
+ shinestacker/retouch/io_threads.py,sha256=r0X4it2PfwnmiAU7eStniIfcHhPvuaqdqf5VlnvjZ-4,2832
94
+ shinestacker/retouch/layer_collection.py,sha256=xx8INSLCXIeTQn_nxfCo4QljAmQK1qukSYO1Zk4rqqo,6183
95
+ shinestacker/retouch/overlaid_view.py,sha256=QTTdegUWs99YBZZPlIRdPI5O80U3t_c3HnyegbRqNbA,7029
96
+ shinestacker/retouch/paint_area_manager.py,sha256=ilK6uQT7lzNyvdc8uNv4xTHHHAbk5hGEClJRNmiA4P8,894
92
97
  shinestacker/retouch/shortcuts_help.py,sha256=BFWTT5QvodqMhqa_9LI25hZqjICfckgyWG4fGrGzvnM,4283
93
- shinestacker/retouch/sidebyside_view.py,sha256=Ipx5ckaAuEv6fnxg4RpENj00ew6-M2jLsnTKUcYKcAk,18055
94
- shinestacker/retouch/transformation_manager.py,sha256=NSHGUF-JFv4Y81gSvizjQCTp49TLo1so7c0WoUElO08,1812
95
- shinestacker/retouch/undo_manager.py,sha256=cKUkqnJtnJ-Hq-LQs5Bv49FC6qkG6XSw9oCVySJ8jS0,4312
98
+ shinestacker/retouch/sidebyside_view.py,sha256=4sNa_IUMbNH18iECO7eDO9S_ls3v2ENwP1AWrBFhofI,18907
99
+ shinestacker/retouch/transformation_manager.py,sha256=QFYCL-l9V6qlhw3y7tcs0saWWClNPsh7F9pTBkfPbRU,1711
100
+ shinestacker/retouch/undo_manager.py,sha256=J4hEAnv9bKLQ0N1wllWswjJBhgRgasCnBoMT5LEw-dM,4453
96
101
  shinestacker/retouch/unsharp_mask_filter.py,sha256=Iapc8UmSVpj3V0LcJq_38P5qerRqTevMynbbk5Rk6iE,3634
97
- shinestacker/retouch/view_strategy.py,sha256=u9oOB-fxap_ijNJ3O3Ev5OMLH-bDZNGj4hyBkJUDeeo,27993
102
+ shinestacker/retouch/view_strategy.py,sha256=jZxB_vX3_0notH0ClxKkLzbdtx4is3vQiYoIP-sDv3M,30216
98
103
  shinestacker/retouch/vignetting_filter.py,sha256=JhFr6OVIripQzSJrZEG4lxq7wBsmpofLqJQ-aP2bKw8,3789
99
104
  shinestacker/retouch/white_balance_filter.py,sha256=UaH4yxG3fU4vPutBAkV5oTXIQyUTN09x0uTywAzv3sY,8286
100
- shinestacker-1.5.4.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
101
- shinestacker-1.5.4.dist-info/METADATA,sha256=1cFUICQsSTKF1T8L7sNiT7XQ_UkwXlIMRr64bym9TvA,6978
102
- shinestacker-1.5.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
- shinestacker-1.5.4.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
104
- shinestacker-1.5.4.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
105
- shinestacker-1.5.4.dist-info/RECORD,,
105
+ shinestacker-1.6.1.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
106
+ shinestacker-1.6.1.dist-info/METADATA,sha256=9sevPHbXS-yaj-wYc-42q28V34PNOdnPlntqNzlsgOo,6978
107
+ shinestacker-1.6.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
108
+ shinestacker-1.6.1.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
109
+ shinestacker-1.6.1.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
110
+ shinestacker-1.6.1.dist-info/RECORD,,
@@ -1,69 +0,0 @@
1
- # pylint: disable=E1101, C0114, C0115, C0116, E0611, W0718, R0903
2
- import traceback
3
- import cv2
4
- from PySide6.QtCore import QThread, Signal
5
- from .. algorithms.utils import read_img, validate_image, get_img_metadata
6
- from .. algorithms.exif import get_exif, write_image_with_exif_data
7
- from .. algorithms.multilayer import write_multilayer_tiff_from_images
8
- from .layer_collection import LayerCollectionHandler
9
-
10
-
11
- class FileMultilayerSaver(QThread):
12
- finished = Signal()
13
- error = Signal(str)
14
-
15
- def __init__(self, images_dict, path, exif_path=None):
16
- super().__init__()
17
- self.images_dict = images_dict
18
- self.path = path
19
- self.exif_path = exif_path
20
-
21
- def run(self):
22
- try:
23
- write_multilayer_tiff_from_images(
24
- self.images_dict, self.path, exif_path=self.exif_path)
25
- self.finished.emit()
26
- except Exception as e:
27
- traceback.print_tb(e.__traceback__)
28
- self.error.emit(str(e))
29
-
30
-
31
- class IOManager(LayerCollectionHandler):
32
- def __init__(self, layer_collection):
33
- super().__init__(layer_collection)
34
- self.exif_path = ''
35
- self.exif_data = None
36
-
37
- def import_frames(self, file_paths):
38
- stack = []
39
- labels = []
40
- master = None
41
- shape, dtype = get_img_metadata(self.master_layer())
42
- for path in file_paths:
43
- try:
44
- label = path.split("/")[-1].split(".")[0]
45
- img = cv2.cvtColor(read_img(path), cv2.COLOR_BGR2RGB)
46
- if shape is not None and dtype is not None:
47
- validate_image(img, shape, dtype)
48
- else:
49
- shape, dtype = get_img_metadata(img)
50
- label_x = label
51
- i = 0
52
- while label_x in labels:
53
- i += 1
54
- label_x = f"{label} ({i})"
55
- labels.append(label_x)
56
- stack.append(img)
57
- if master is None:
58
- master = img.copy()
59
- except Exception as e:
60
- raise RuntimeError(f"Error loading file: {path}.\n{str(e)}") from e
61
- return stack, labels, master
62
-
63
- def save_master(self, path):
64
- img = cv2.cvtColor(self.master_layer(), cv2.COLOR_RGB2BGR)
65
- write_image_with_exif_data(self.exif_data, img, path)
66
-
67
- def set_exif_data(self, path):
68
- self.exif_path = path
69
- self.exif_data = get_exif(path)