shinestacker 1.3.1__py3-none-any.whl → 1.5.0__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 (38) hide show
  1. shinestacker/_version.py +1 -1
  2. shinestacker/algorithms/align.py +198 -18
  3. shinestacker/algorithms/align_parallel.py +17 -1
  4. shinestacker/algorithms/balance.py +23 -13
  5. shinestacker/algorithms/noise_detection.py +3 -1
  6. shinestacker/algorithms/utils.py +21 -10
  7. shinestacker/algorithms/vignetting.py +2 -0
  8. shinestacker/app/main.py +1 -1
  9. shinestacker/config/gui_constants.py +7 -2
  10. shinestacker/core/core_utils.py +10 -1
  11. shinestacker/gui/action_config.py +172 -7
  12. shinestacker/gui/action_config_dialog.py +246 -285
  13. shinestacker/gui/gui_run.py +2 -2
  14. shinestacker/gui/main_window.py +14 -5
  15. shinestacker/gui/menu_manager.py +26 -2
  16. shinestacker/gui/project_controller.py +4 -0
  17. shinestacker/gui/recent_file_manager.py +93 -0
  18. shinestacker/retouch/base_filter.py +13 -15
  19. shinestacker/retouch/brush_preview.py +3 -1
  20. shinestacker/retouch/brush_tool.py +11 -11
  21. shinestacker/retouch/display_manager.py +43 -59
  22. shinestacker/retouch/image_editor_ui.py +161 -82
  23. shinestacker/retouch/image_view_status.py +65 -0
  24. shinestacker/retouch/image_viewer.py +95 -431
  25. shinestacker/retouch/io_gui_handler.py +12 -2
  26. shinestacker/retouch/layer_collection.py +3 -0
  27. shinestacker/retouch/overlaid_view.py +215 -0
  28. shinestacker/retouch/shortcuts_help.py +13 -3
  29. shinestacker/retouch/sidebyside_view.py +477 -0
  30. shinestacker/retouch/transformation_manager.py +43 -0
  31. shinestacker/retouch/undo_manager.py +22 -3
  32. shinestacker/retouch/view_strategy.py +557 -0
  33. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/METADATA +7 -7
  34. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/RECORD +38 -32
  35. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/WHEEL +0 -0
  36. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/entry_points.txt +0 -0
  37. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/licenses/LICENSE +0 -0
  38. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,215 @@
1
+ # pylint: disable=C0114, C0115, C0116, E0611, E1101, R0904, R0912, R0914, R0902
2
+ from PySide6.QtCore import Qt, QPointF, QEvent, QRectF
3
+ from .. config.gui_constants import gui_constants
4
+ from .view_strategy import ViewStrategy, ImageGraphicsViewBase, ViewSignals
5
+
6
+
7
+ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
8
+ def __init__(self, layer_collection, status, parent):
9
+ ViewStrategy.__init__(self, layer_collection, status)
10
+ ImageGraphicsViewBase.__init__(self, parent)
11
+ self.scene = self.create_scene(self)
12
+ self.create_pixmaps()
13
+ self.scene.addItem(self.brush_preview)
14
+ self.brush_cursor = None
15
+ self.pinch_start_scale = 1.0
16
+ self.last_scroll_pos = QPointF()
17
+
18
+ def create_pixmaps(self):
19
+ self.pixmap_item_master = self.create_pixmap(self.scene)
20
+ self.pixmap_item_current = self.create_pixmap(self.scene)
21
+
22
+ def get_master_view(self):
23
+ return self
24
+
25
+ def get_current_view(self):
26
+ return self
27
+
28
+ def get_master_scene(self):
29
+ return self.scene
30
+
31
+ def get_current_scene(self):
32
+ return self.scene
33
+
34
+ def get_master_pixmap(self):
35
+ return self.pixmap_item_master
36
+
37
+ def get_current_pixmap(self):
38
+ return self.pixmap_item_current
39
+
40
+ def get_views(self):
41
+ return [self]
42
+
43
+ def get_scenes(self):
44
+ return [self.scene]
45
+
46
+ def get_pixmaps(self):
47
+ return {
48
+ self.pixmap_item_master: self,
49
+ self.pixmap_item_current: self
50
+ }
51
+
52
+ # pylint: disable=C0103
53
+ def mousePressEvent(self, event):
54
+ self.mouse_press_event(event)
55
+ super().mousePressEvent(event)
56
+
57
+ def mouseMoveEvent(self, event):
58
+ self.mouse_move_event(event)
59
+
60
+ def mouseReleaseEvent(self, event):
61
+ self.mouse_release_event(event)
62
+ super().mouseReleaseEvent(event)
63
+
64
+ # pylint: enable=R0801
65
+ def wheelEvent(self, event):
66
+ if self.empty() or self.gesture_active:
67
+ return
68
+ if event.source() == Qt.MouseEventNotSynthesized: # Physical mouse
69
+ if self.control_pressed:
70
+ self.brush_size_change_requested.emit(1 if event.angleDelta().y() > 0 else -1)
71
+ else:
72
+ zoom_in_factor = gui_constants.ZOOM_IN_FACTOR
73
+ zoom_out_factor = gui_constants.ZOOM_OUT_FACTOR
74
+ current_scale = self.get_current_scale()
75
+ if event.angleDelta().y() > 0: # Zoom in
76
+ new_scale = current_scale * zoom_in_factor
77
+ if new_scale <= self.max_scale():
78
+ self.scale(zoom_in_factor, zoom_in_factor)
79
+ self.set_zoom_factor(new_scale)
80
+ else: # Zoom out
81
+ new_scale = current_scale * zoom_out_factor
82
+ if new_scale >= self.min_scale():
83
+ self.scale(zoom_out_factor, zoom_out_factor)
84
+ self.set_zoom_factor(new_scale)
85
+ self.update_brush_cursor()
86
+ else: # Touchpad event - fallback for systems without gesture recognition
87
+ if not self.control_pressed:
88
+ delta = event.pixelDelta() or event.angleDelta() / 8
89
+ if delta:
90
+ self.scroll_view(self, delta.x(), delta.y())
91
+ else: # Control + touchpad scroll for zoom
92
+ zoom_in = event.angleDelta().y() > 0
93
+ if zoom_in:
94
+ self.zoom_in()
95
+ else:
96
+ self.zoom_out()
97
+ event.accept()
98
+ # pylint: disable=R0801
99
+
100
+ def enterEvent(self, event):
101
+ self.activateWindow()
102
+ self.setFocus()
103
+ if self.empty():
104
+ self.setCursor(Qt.ArrowCursor)
105
+ else:
106
+ self.setCursor(Qt.BlankCursor)
107
+ if self.brush_cursor:
108
+ self.brush_cursor.show()
109
+ super().enterEvent(event)
110
+ # pylint: enable=C0103
111
+
112
+ def event(self, event):
113
+ if event.type() == QEvent.Gesture:
114
+ return self.handle_gesture_event(event)
115
+ return super().event(event)
116
+
117
+ def set_master_image(self, qimage):
118
+ self.status.set_master_image(qimage)
119
+ pixmap = self.status.pixmap_master
120
+ self.setSceneRect(QRectF(pixmap.rect()))
121
+ img_width, img_height = pixmap.width(), pixmap.height()
122
+ self.set_max_min_scales(img_width, img_height)
123
+ self.set_zoom_factor(1.0)
124
+ self.resetTransform()
125
+ self.fitInView(self.pixmap_item_master, Qt.KeepAspectRatio)
126
+ self.set_zoom_factor(self.get_current_scale())
127
+ self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), self.zoom_factor())))
128
+ self.scale(self.zoom_factor(), self.zoom_factor())
129
+ self.centerOn(self.pixmap_item_master)
130
+ self.center_image(self)
131
+ self.update_cursor_pen_width()
132
+
133
+ def set_current_image(self, qimage):
134
+ self.status.set_current_image(qimage)
135
+ if self.empty():
136
+ self.setSceneRect(QRectF(self.status.pixmap_current.rect()))
137
+ self.update_cursor_pen_width()
138
+
139
+ def setup_brush_cursor(self):
140
+ super().setup_brush_cursor()
141
+ self.update_cursor_pen_width()
142
+
143
+ def show_master(self):
144
+ self.pixmap_item_master.setVisible(True)
145
+ self.pixmap_item_current.setVisible(False)
146
+ self.brush_preview.show()
147
+
148
+ def show_current(self):
149
+ self.pixmap_item_master.setVisible(False)
150
+ self.pixmap_item_current.setVisible(True)
151
+ self.brush_preview.hide()
152
+
153
+ def arrange_images(self):
154
+ if self.empty():
155
+ return
156
+ if self.pixmap_item_master.isVisible():
157
+ pixmap = self.pixmap_item_master.pixmap()
158
+ if not pixmap.isNull():
159
+ self.setSceneRect(QRectF(pixmap.rect()))
160
+ self.centerOn(self.pixmap_item_master)
161
+ self.center_image(self)
162
+ elif self.pixmap_item_current.isVisible():
163
+ pixmap = self.pixmap_item_current.pixmap()
164
+ if not pixmap.isNull():
165
+ self.setSceneRect(QRectF(pixmap.rect()))
166
+ self.centerOn(self.pixmap_item_current)
167
+ self.center_image(self)
168
+ current_scale = self.get_current_scale()
169
+ scale_factor = self.zoom_factor() / current_scale
170
+ self.scale(scale_factor, scale_factor)
171
+
172
+ def handle_key_press_event(self, event):
173
+ if event.key() in [Qt.Key_Up, Qt.Key_Down]:
174
+ return False
175
+ if event.key() == Qt.Key_X:
176
+ self.temp_view_requested.emit(True)
177
+ return False
178
+ return True
179
+
180
+ def handle_key_release_event(self, event):
181
+ if event.key() in [Qt.Key_Up, Qt.Key_Down]:
182
+ return False
183
+ if event.key() == Qt.Key_X:
184
+ self.temp_view_requested.emit(False)
185
+ return False
186
+ return True
187
+
188
+ def handle_gesture_event(self, event):
189
+ if self.empty():
190
+ return False
191
+ handled = False
192
+ pan_gesture = event.gesture(Qt.PanGesture)
193
+ if pan_gesture:
194
+ self.handle_pan_gesture(pan_gesture)
195
+ handled = True
196
+ pinch_gesture = event.gesture(Qt.PinchGesture)
197
+ if pinch_gesture:
198
+ self.handle_pinch_gesture(pinch_gesture)
199
+ handled = True
200
+ if handled:
201
+ event.accept()
202
+ return True
203
+ return False
204
+
205
+ def handle_pan_gesture(self, pan_gesture):
206
+ if pan_gesture.state() == Qt.GestureStarted:
207
+ self.last_scroll_pos = pan_gesture.delta()
208
+ self.gesture_active = True
209
+ elif pan_gesture.state() == Qt.GestureUpdated:
210
+ delta = pan_gesture.delta() - self.last_scroll_pos
211
+ self.last_scroll_pos = pan_gesture.delta()
212
+ scaled_delta = delta * (1.0 / self.get_current_scale())
213
+ self.scroll_view(self, int(scaled_delta.x()), int(scaled_delta.y()))
214
+ elif pan_gesture.state() == Qt.GestureFinished:
215
+ self.gesture_active = False
@@ -61,16 +61,26 @@ class ShortcutsHelp(QDialog):
61
61
  "Ctrl + +": "zoom in",
62
62
  "Ctrl + -": "zoom out",
63
63
  "Ctrl + 0": "adapt to screen",
64
- "Ctrl + =": "actual size",
64
+ "Ctrl + R": "actual size"
65
+ }
66
+
67
+ self.add_bold_label(left_layout, "Keyboard Shortcuts")
68
+ for k, v in shortcuts.items():
69
+ left_layout.addRow(f"<b>{k}</b>", QLabel(v))
70
+
71
+ shortcuts = {
72
+ "Ctrl + 1": "view mode: overlaid",
73
+ "Ctrl + 2": "view mode: side by side",
74
+ "Ctrl + 3": "view mode: top-bottom",
65
75
  "[": "increase brush size",
66
76
  "]": "decrease brush size",
67
77
  "{": "increase brush hardness",
68
78
  "}": "decrease brush hardness"
69
79
  }
70
80
 
71
- self.add_bold_label(left_layout, "Keyboard Shortcuts")
81
+ self.add_bold_label(right_layout, "Keyboard Shortcuts")
72
82
  for k, v in shortcuts.items():
73
- left_layout.addRow(f"<b>{k}</b>", QLabel(v))
83
+ right_layout.addRow(f"<b>{k}</b>", QLabel(v))
74
84
 
75
85
  mouse_controls = {
76
86
  "Space + Drag": "pan",