shinestacker 1.3.1__py3-none-any.whl → 1.4.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.
- shinestacker/_version.py +1 -1
- shinestacker/algorithms/align.py +198 -18
- shinestacker/algorithms/align_parallel.py +17 -1
- shinestacker/algorithms/balance.py +23 -13
- shinestacker/algorithms/noise_detection.py +3 -1
- shinestacker/algorithms/utils.py +21 -10
- shinestacker/algorithms/vignetting.py +2 -0
- shinestacker/config/gui_constants.py +2 -2
- shinestacker/core/core_utils.py +10 -1
- shinestacker/gui/action_config.py +172 -7
- shinestacker/gui/action_config_dialog.py +246 -285
- shinestacker/gui/gui_run.py +2 -2
- shinestacker/gui/main_window.py +14 -5
- shinestacker/gui/menu_manager.py +26 -2
- shinestacker/gui/project_controller.py +4 -0
- shinestacker/gui/recent_file_manager.py +93 -0
- shinestacker/retouch/base_filter.py +5 -5
- shinestacker/retouch/brush_preview.py +3 -0
- shinestacker/retouch/brush_tool.py +11 -11
- shinestacker/retouch/display_manager.py +21 -37
- shinestacker/retouch/image_editor_ui.py +129 -71
- shinestacker/retouch/image_view_status.py +61 -0
- shinestacker/retouch/image_viewer.py +89 -431
- shinestacker/retouch/io_gui_handler.py +12 -2
- shinestacker/retouch/overlaid_view.py +212 -0
- shinestacker/retouch/shortcuts_help.py +13 -3
- shinestacker/retouch/sidebyside_view.py +479 -0
- shinestacker/retouch/view_strategy.py +466 -0
- {shinestacker-1.3.1.dist-info → shinestacker-1.4.0.dist-info}/METADATA +1 -1
- {shinestacker-1.3.1.dist-info → shinestacker-1.4.0.dist-info}/RECORD +34 -29
- {shinestacker-1.3.1.dist-info → shinestacker-1.4.0.dist-info}/WHEEL +0 -0
- {shinestacker-1.3.1.dist-info → shinestacker-1.4.0.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.3.1.dist-info → shinestacker-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.3.1.dist-info → shinestacker-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, E0611, R0904, R0903, R0902, E1101, R0914
|
|
2
|
+
import math
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
import numpy as np
|
|
5
|
+
from PySide6.QtCore import Qt, QPointF, QTime, QPoint, Signal
|
|
6
|
+
from PySide6.QtGui import QImage, QPainter, QColor, QBrush, QPen, QCursor
|
|
7
|
+
from PySide6.QtWidgets import (
|
|
8
|
+
QGraphicsEllipseItem, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem)
|
|
9
|
+
from .. config.gui_constants import gui_constants
|
|
10
|
+
from .layer_collection import LayerCollectionHandler
|
|
11
|
+
from .brush_gradient import create_default_brush_gradient
|
|
12
|
+
from .brush_preview import BrushPreviewItem
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ViewSignals:
|
|
16
|
+
temp_view_requested = Signal(bool)
|
|
17
|
+
brush_operation_started = Signal(QPoint)
|
|
18
|
+
brush_operation_continued = Signal(QPoint)
|
|
19
|
+
brush_operation_ended = Signal()
|
|
20
|
+
brush_size_change_requested = Signal(int) # +1 or -1
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ImageGraphicsViewBase(QGraphicsView):
|
|
24
|
+
def __init__(self, parent=None):
|
|
25
|
+
super().__init__(parent)
|
|
26
|
+
self.setTransformationAnchor(QGraphicsView.AnchorViewCenter)
|
|
27
|
+
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
|
|
28
|
+
self.setInteractive(False)
|
|
29
|
+
self.grabGesture(Qt.PinchGesture)
|
|
30
|
+
self.grabGesture(Qt.PanGesture)
|
|
31
|
+
self.setMouseTracking(True)
|
|
32
|
+
self.setDragMode(QGraphicsView.NoDrag)
|
|
33
|
+
self.setRenderHint(QPainter.Antialiasing)
|
|
34
|
+
self.setRenderHint(QPainter.SmoothPixmapTransform)
|
|
35
|
+
self.setCursor(Qt.BlankCursor)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ViewStrategy(LayerCollectionHandler):
|
|
39
|
+
def __init__(self, layer_collection, status):
|
|
40
|
+
LayerCollectionHandler.__init__(self, layer_collection)
|
|
41
|
+
self.display_manager = None
|
|
42
|
+
self.status = status
|
|
43
|
+
self.brush = None
|
|
44
|
+
self.brush_cursor = None
|
|
45
|
+
self.display_manager = None
|
|
46
|
+
self.brush_preview = BrushPreviewItem(layer_collection)
|
|
47
|
+
self.cursor_style = gui_constants.DEFAULT_CURSOR_STYLE
|
|
48
|
+
self.allow_cursor_preview = True
|
|
49
|
+
self.control_pressed = False
|
|
50
|
+
self.space_pressed = False
|
|
51
|
+
self.gesture_active = False
|
|
52
|
+
self.pinch_center_view = None
|
|
53
|
+
self.pinch_center_scene = None
|
|
54
|
+
self.pinch_start_scale = None
|
|
55
|
+
self.last_scroll_pos = None
|
|
56
|
+
self.scrolling = False
|
|
57
|
+
self.dragging = False
|
|
58
|
+
self.last_brush_pos = None
|
|
59
|
+
self.last_mouse_pos = None
|
|
60
|
+
self.last_update_time = QTime.currentTime()
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def create_pixmaps(self):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def set_master_image(self, qimage):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def set_current_image(self, qimage):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def update_master_display(self):
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def update_current_display(self):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def get_master_view(self):
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def get_master_scene(self):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def get_views(self):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def get_scenes(self):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def get_pixmaps(self):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def get_master_pixmap(self):
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
def show_master(self):
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def show_current(self):
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
def set_allow_cursor_preview(self, state):
|
|
113
|
+
self.allow_cursor_preview = state
|
|
114
|
+
|
|
115
|
+
def zoom_factor(self):
|
|
116
|
+
return self.status.zoom_factor
|
|
117
|
+
|
|
118
|
+
def set_zoom_factor(self, zoom_factor):
|
|
119
|
+
self.status.set_zoom_factor(zoom_factor)
|
|
120
|
+
|
|
121
|
+
def get_current_scale(self):
|
|
122
|
+
return self.get_master_view().transform().m11()
|
|
123
|
+
|
|
124
|
+
def min_scale(self):
|
|
125
|
+
return self.status.min_scale
|
|
126
|
+
|
|
127
|
+
def max_scale(self):
|
|
128
|
+
return self.status.max_scale
|
|
129
|
+
|
|
130
|
+
def set_min_scale(self, scale):
|
|
131
|
+
self.status.set_min_scale(scale)
|
|
132
|
+
|
|
133
|
+
def set_max_scale(self, scale):
|
|
134
|
+
self.status.set_max_scale(scale)
|
|
135
|
+
|
|
136
|
+
def empty(self):
|
|
137
|
+
return self.status.empty()
|
|
138
|
+
|
|
139
|
+
def set_brush(self, brush):
|
|
140
|
+
self.brush = brush
|
|
141
|
+
|
|
142
|
+
def set_preview_brush(self, brush):
|
|
143
|
+
self.brush_preview.brush = brush
|
|
144
|
+
|
|
145
|
+
def set_display_manager(self, dm):
|
|
146
|
+
self.display_manager = dm
|
|
147
|
+
|
|
148
|
+
def set_cursor_style(self, style):
|
|
149
|
+
self.cursor_style = style
|
|
150
|
+
if self.brush_cursor:
|
|
151
|
+
self.update_brush_cursor()
|
|
152
|
+
|
|
153
|
+
def get_cursor_style(self):
|
|
154
|
+
return self.cursor_style
|
|
155
|
+
|
|
156
|
+
def handle_key_press_event(self, _event):
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
def handle_key_release_event(self, _event):
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
def clear_image(self):
|
|
163
|
+
for scene in self.get_scenes():
|
|
164
|
+
scene.clear()
|
|
165
|
+
self.create_pixmaps()
|
|
166
|
+
self.status.clear()
|
|
167
|
+
self.setup_brush_cursor()
|
|
168
|
+
self.brush_preview = BrushPreviewItem(self.layer_collection)
|
|
169
|
+
self.get_master_scene().addItem(self.brush_preview)
|
|
170
|
+
self.setCursor(Qt.ArrowCursor)
|
|
171
|
+
if self.brush_cursor:
|
|
172
|
+
self.brush_cursor.hide()
|
|
173
|
+
|
|
174
|
+
def set_master_image_np(self, img):
|
|
175
|
+
self.set_master_image(self.numpy_to_qimage(img))
|
|
176
|
+
|
|
177
|
+
def numpy_to_qimage(self, array):
|
|
178
|
+
if array is None:
|
|
179
|
+
return None
|
|
180
|
+
if array.dtype == np.uint16:
|
|
181
|
+
array = np.right_shift(array, 8).astype(np.uint8)
|
|
182
|
+
if array.ndim == 2:
|
|
183
|
+
height, width = array.shape
|
|
184
|
+
return QImage(memoryview(array), width, height, width, QImage.Format_Grayscale8)
|
|
185
|
+
if array.ndim == 3:
|
|
186
|
+
height, width, _ = array.shape
|
|
187
|
+
if not array.flags['C_CONTIGUOUS']:
|
|
188
|
+
array = np.ascontiguousarray(array)
|
|
189
|
+
return QImage(memoryview(array), width, height, 3 * width, QImage.Format_RGB888)
|
|
190
|
+
return QImage()
|
|
191
|
+
|
|
192
|
+
def create_scene(self, view):
|
|
193
|
+
scene = QGraphicsScene()
|
|
194
|
+
view.setScene(scene)
|
|
195
|
+
scene.setBackgroundBrush(QBrush(QColor(120, 120, 120)))
|
|
196
|
+
return scene
|
|
197
|
+
|
|
198
|
+
def create_pixmap(self, scene):
|
|
199
|
+
pixmap_item = QGraphicsPixmapItem()
|
|
200
|
+
scene.addItem(pixmap_item)
|
|
201
|
+
return pixmap_item
|
|
202
|
+
|
|
203
|
+
def refresh_display(self):
|
|
204
|
+
self.update_brush_cursor()
|
|
205
|
+
for scene in self.get_scenes():
|
|
206
|
+
scene.update()
|
|
207
|
+
|
|
208
|
+
def zoom_in(self):
|
|
209
|
+
if self.empty():
|
|
210
|
+
return
|
|
211
|
+
master_view = self.get_master_view()
|
|
212
|
+
old_center = master_view.mapToScene(master_view.viewport().rect().center())
|
|
213
|
+
current_scale = self.get_current_scale()
|
|
214
|
+
new_scale = current_scale * gui_constants.ZOOM_IN_FACTOR
|
|
215
|
+
if new_scale <= self.max_scale():
|
|
216
|
+
for view in self.get_views():
|
|
217
|
+
view.scale(gui_constants.ZOOM_IN_FACTOR, gui_constants.ZOOM_IN_FACTOR)
|
|
218
|
+
self.set_zoom_factor(new_scale)
|
|
219
|
+
master_view.centerOn(old_center)
|
|
220
|
+
self.update_brush_cursor()
|
|
221
|
+
|
|
222
|
+
def zoom_out(self):
|
|
223
|
+
if self.empty():
|
|
224
|
+
return
|
|
225
|
+
master_view = self.get_master_view()
|
|
226
|
+
old_center = master_view.mapToScene(master_view.viewport().rect().center())
|
|
227
|
+
current_scale = self.get_current_scale()
|
|
228
|
+
new_scale = current_scale * gui_constants.ZOOM_OUT_FACTOR
|
|
229
|
+
if new_scale >= self.min_scale():
|
|
230
|
+
for view in self.get_views():
|
|
231
|
+
view.scale(gui_constants.ZOOM_OUT_FACTOR, gui_constants.ZOOM_OUT_FACTOR)
|
|
232
|
+
self.set_zoom_factor(new_scale)
|
|
233
|
+
master_view.centerOn(old_center)
|
|
234
|
+
self.update_brush_cursor()
|
|
235
|
+
|
|
236
|
+
def reset_zoom(self):
|
|
237
|
+
if self.empty():
|
|
238
|
+
return
|
|
239
|
+
self.pinch_start_scale = 1.0
|
|
240
|
+
self.last_scroll_pos = QPointF()
|
|
241
|
+
self.gesture_active = False
|
|
242
|
+
self.pinch_center_view = None
|
|
243
|
+
self.pinch_center_scene = None
|
|
244
|
+
for pixmap, view in self.get_pixmaps().items():
|
|
245
|
+
view.fitInView(pixmap, Qt.KeepAspectRatio)
|
|
246
|
+
self.set_zoom_factor(self.get_current_scale())
|
|
247
|
+
self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), self.zoom_factor())))
|
|
248
|
+
for view in self.get_views():
|
|
249
|
+
view.resetTransform()
|
|
250
|
+
view.scale(self.zoom_factor(), self.zoom_factor())
|
|
251
|
+
self.update_brush_cursor()
|
|
252
|
+
|
|
253
|
+
def actual_size(self):
|
|
254
|
+
if self.empty():
|
|
255
|
+
return
|
|
256
|
+
self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), 1.0)))
|
|
257
|
+
for view in self.get_views():
|
|
258
|
+
view.resetTransform()
|
|
259
|
+
view.scale(self.zoom_factor(), self.zoom_factor())
|
|
260
|
+
self.update_brush_cursor()
|
|
261
|
+
|
|
262
|
+
def setup_outline_style(self):
|
|
263
|
+
self.brush_cursor.setPen(QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
264
|
+
gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()))
|
|
265
|
+
self.brush_cursor.setBrush(Qt.NoBrush)
|
|
266
|
+
|
|
267
|
+
def setup_simple_brush_style(self, center_x, center_y, radius):
|
|
268
|
+
gradient = create_default_brush_gradient(center_x, center_y, radius, self.brush)
|
|
269
|
+
self.brush_cursor.setPen(QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
270
|
+
gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()))
|
|
271
|
+
self.brush_cursor.setBrush(QBrush(gradient))
|
|
272
|
+
|
|
273
|
+
def setup_brush_cursor(self):
|
|
274
|
+
if not self.brush:
|
|
275
|
+
return
|
|
276
|
+
scene = self.get_master_scene()
|
|
277
|
+
for item in scene.items():
|
|
278
|
+
if isinstance(item, QGraphicsEllipseItem) and item != self.brush_preview:
|
|
279
|
+
scene.removeItem(item)
|
|
280
|
+
pen = QPen(QColor(*gui_constants.BRUSH_COLORS['pen']), 1)
|
|
281
|
+
brush = QBrush(QColor(*gui_constants.BRUSH_COLORS['cursor_inner']))
|
|
282
|
+
self.brush_cursor = scene.addEllipse(
|
|
283
|
+
0, 0, self.brush.size, self.brush.size, pen, brush)
|
|
284
|
+
self.brush_cursor.setZValue(1000)
|
|
285
|
+
self.brush_cursor.hide()
|
|
286
|
+
|
|
287
|
+
def update_brush_cursor(self):
|
|
288
|
+
if self.empty():
|
|
289
|
+
return
|
|
290
|
+
if not self.brush_cursor or not self.isVisible():
|
|
291
|
+
return
|
|
292
|
+
master_view = self.get_master_view()
|
|
293
|
+
mouse_pos = master_view.mapFromGlobal(QCursor.pos())
|
|
294
|
+
if not master_view.rect().contains(mouse_pos):
|
|
295
|
+
self.brush_cursor.hide()
|
|
296
|
+
return
|
|
297
|
+
scene_pos = master_view.mapToScene(mouse_pos)
|
|
298
|
+
size = self.brush.size
|
|
299
|
+
radius = size / 2
|
|
300
|
+
self.brush_cursor.setRect(scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
301
|
+
allow_cursor_preview = self.display_manager.allow_cursor_preview()
|
|
302
|
+
if self.cursor_style == 'preview' and allow_cursor_preview:
|
|
303
|
+
self.setup_outline_style()
|
|
304
|
+
self.brush_cursor.hide()
|
|
305
|
+
pos = QCursor.pos()
|
|
306
|
+
if isinstance(pos, QPointF):
|
|
307
|
+
scene_pos = pos
|
|
308
|
+
else:
|
|
309
|
+
cursor_pos = master_view.mapFromGlobal(pos)
|
|
310
|
+
scene_pos = master_view.mapToScene(cursor_pos)
|
|
311
|
+
self.brush_preview.update(scene_pos, int(size))
|
|
312
|
+
else:
|
|
313
|
+
self.brush_preview.hide()
|
|
314
|
+
if self.cursor_style == 'outline' or not allow_cursor_preview:
|
|
315
|
+
self.setup_outline_style()
|
|
316
|
+
else:
|
|
317
|
+
self.setup_simple_brush_style(scene_pos.x(), scene_pos.y(), radius)
|
|
318
|
+
if not self.brush_cursor.isVisible():
|
|
319
|
+
self.brush_cursor.show()
|
|
320
|
+
|
|
321
|
+
def position_on_image(self, pos):
|
|
322
|
+
master_view = self.get_master_view()
|
|
323
|
+
pixmap = self.get_master_pixmap()
|
|
324
|
+
scene_pos = master_view.mapToScene(pos)
|
|
325
|
+
item_pos = pixmap.mapFromScene(scene_pos)
|
|
326
|
+
return item_pos
|
|
327
|
+
|
|
328
|
+
def get_visible_image_region(self):
|
|
329
|
+
if self.empty():
|
|
330
|
+
return None
|
|
331
|
+
master_view = self.get_master_view()
|
|
332
|
+
master_pixmap = self.get_master_pixmap()
|
|
333
|
+
pixmap = self.get_master_pixmap()
|
|
334
|
+
view_rect = master_view.viewport().rect()
|
|
335
|
+
scene_rect = master_view.mapToScene(view_rect).boundingRect()
|
|
336
|
+
image_rect = master_pixmap.mapFromScene(scene_rect).boundingRect().toRect()
|
|
337
|
+
return image_rect.intersected(pixmap.boundingRect().toRect())
|
|
338
|
+
|
|
339
|
+
def get_visible_image_portion(self):
|
|
340
|
+
if self.has_no_master_layer():
|
|
341
|
+
return None
|
|
342
|
+
visible_rect = self.get_visible_image_region()
|
|
343
|
+
if not visible_rect:
|
|
344
|
+
return self.master_layer()
|
|
345
|
+
x, y = int(visible_rect.x()), int(visible_rect.y())
|
|
346
|
+
w, h = int(visible_rect.width()), int(visible_rect.height())
|
|
347
|
+
master_img = self.master_layer()
|
|
348
|
+
return master_img[y:y + h, x:x + w], (x, y, w, h)
|
|
349
|
+
|
|
350
|
+
def map_to_scene(self, pos):
|
|
351
|
+
return self.get_master_view().mapToScene(pos)
|
|
352
|
+
|
|
353
|
+
# pylint: disable=C0103
|
|
354
|
+
def keyPressEvent(self, event):
|
|
355
|
+
if self.empty():
|
|
356
|
+
return
|
|
357
|
+
master_view = self.get_master_view()
|
|
358
|
+
if event.key() == Qt.Key_Space and not self.scrolling:
|
|
359
|
+
self.space_pressed = True
|
|
360
|
+
master_view.setCursor(Qt.OpenHandCursor)
|
|
361
|
+
if self.brush_cursor:
|
|
362
|
+
self.brush_cursor.hide()
|
|
363
|
+
self.handle_key_press_event(event)
|
|
364
|
+
if event.key() == Qt.Key_Control and not self.scrolling:
|
|
365
|
+
self.control_pressed = True
|
|
366
|
+
super().keyPressEvent(event)
|
|
367
|
+
|
|
368
|
+
def keyReleaseEvent(self, event):
|
|
369
|
+
if self.empty():
|
|
370
|
+
return
|
|
371
|
+
master_view = self.get_master_view()
|
|
372
|
+
self.update_brush_cursor()
|
|
373
|
+
if event.key() == Qt.Key_Space:
|
|
374
|
+
self.space_pressed = False
|
|
375
|
+
if not self.scrolling:
|
|
376
|
+
master_view.setCursor(Qt.BlankCursor)
|
|
377
|
+
if self.brush_cursor:
|
|
378
|
+
self.brush_cursor.show()
|
|
379
|
+
self.handle_key_release_event(event)
|
|
380
|
+
if event.key() == Qt.Key_Control:
|
|
381
|
+
self.control_pressed = False
|
|
382
|
+
super().keyReleaseEvent(event)
|
|
383
|
+
|
|
384
|
+
def leaveEvent(self, event):
|
|
385
|
+
if not self.empty():
|
|
386
|
+
self.get_master_view().setCursor(Qt.ArrowCursor)
|
|
387
|
+
if self.brush_cursor:
|
|
388
|
+
self.brush_cursor.hide()
|
|
389
|
+
super().leaveEvent(event)
|
|
390
|
+
# pylint: enable=C0103
|
|
391
|
+
|
|
392
|
+
def mouse_move_event(self, event):
|
|
393
|
+
if self.empty():
|
|
394
|
+
return
|
|
395
|
+
position = event.position()
|
|
396
|
+
brush_size = self.brush.size
|
|
397
|
+
if not self.space_pressed:
|
|
398
|
+
self.update_brush_cursor()
|
|
399
|
+
if self.dragging and event.buttons() & Qt.LeftButton:
|
|
400
|
+
current_time = QTime.currentTime()
|
|
401
|
+
if self.last_update_time.msecsTo(current_time) >= gui_constants.PAINT_REFRESH_TIMER:
|
|
402
|
+
min_step = brush_size * \
|
|
403
|
+
gui_constants.MIN_MOUSE_STEP_BRUSH_FRACTION * self.zoom_factor()
|
|
404
|
+
x, y = position.x(), position.y()
|
|
405
|
+
xp, yp = self.last_brush_pos.x(), self.last_brush_pos.y()
|
|
406
|
+
distance = math.sqrt((x - xp)**2 + (y - yp)**2)
|
|
407
|
+
n_steps = int(float(distance) / min_step)
|
|
408
|
+
if n_steps > 0:
|
|
409
|
+
delta_x = (position.x() - self.last_brush_pos.x()) / n_steps
|
|
410
|
+
delta_y = (position.y() - self.last_brush_pos.y()) / n_steps
|
|
411
|
+
for i in range(0, n_steps + 1):
|
|
412
|
+
pos = QPoint(self.last_brush_pos.x() + i * delta_x,
|
|
413
|
+
self.last_brush_pos.y() + i * delta_y)
|
|
414
|
+
self.brush_operation_continued.emit(pos)
|
|
415
|
+
self.last_brush_pos = position
|
|
416
|
+
self.last_update_time = current_time
|
|
417
|
+
if self.scrolling and event.buttons() & Qt.LeftButton:
|
|
418
|
+
master_view = self.get_master_view()
|
|
419
|
+
if self.space_pressed:
|
|
420
|
+
master_view.setCursor(Qt.ClosedHandCursor)
|
|
421
|
+
if self.brush_cursor:
|
|
422
|
+
self.brush_cursor.hide()
|
|
423
|
+
delta = position - self.last_mouse_pos
|
|
424
|
+
self.last_mouse_pos = position
|
|
425
|
+
self.scroll_view(master_view, delta.x(), delta.y())
|
|
426
|
+
|
|
427
|
+
def scroll_view(self, view, delta_x, delta_y):
|
|
428
|
+
view.horizontalScrollBar().setValue(
|
|
429
|
+
view.horizontalScrollBar().value() - delta_x)
|
|
430
|
+
view.verticalScrollBar().setValue(
|
|
431
|
+
view.verticalScrollBar().value() - delta_y)
|
|
432
|
+
|
|
433
|
+
def mouse_press_event(self, event):
|
|
434
|
+
if self.empty():
|
|
435
|
+
return
|
|
436
|
+
if event.button() == Qt.LeftButton and self.has_master_layer():
|
|
437
|
+
if self.space_pressed:
|
|
438
|
+
self.scrolling = True
|
|
439
|
+
self.last_mouse_pos = event.position()
|
|
440
|
+
self.setCursor(Qt.ClosedHandCursor)
|
|
441
|
+
else:
|
|
442
|
+
self.last_brush_pos = event.position()
|
|
443
|
+
self.brush_operation_started.emit(event.position().toPoint())
|
|
444
|
+
self.dragging = True
|
|
445
|
+
if self.brush_cursor:
|
|
446
|
+
self.brush_cursor.show()
|
|
447
|
+
|
|
448
|
+
def mouse_release_event(self, event):
|
|
449
|
+
if self.empty():
|
|
450
|
+
return
|
|
451
|
+
master_view = self.get_master_view()
|
|
452
|
+
if self.space_pressed:
|
|
453
|
+
master_view.setCursor(Qt.OpenHandCursor)
|
|
454
|
+
if self.brush_cursor:
|
|
455
|
+
self.brush_cursor.hide()
|
|
456
|
+
else:
|
|
457
|
+
master_view.setCursor(Qt.BlankCursor)
|
|
458
|
+
if self.brush_cursor:
|
|
459
|
+
self.brush_cursor.show()
|
|
460
|
+
if event.button() == Qt.LeftButton:
|
|
461
|
+
if self.scrolling:
|
|
462
|
+
self.scrolling = False
|
|
463
|
+
self.last_mouse_pos = None
|
|
464
|
+
elif self.dragging:
|
|
465
|
+
self.dragging = False
|
|
466
|
+
self.brush_operation_ended.emit()
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
shinestacker/__init__.py,sha256=uq2fjAw2z_6TpH3mOcWFZ98GoEPRsNhTAK8N0MMm_e8,448
|
|
2
|
-
shinestacker/_version.py,sha256=
|
|
2
|
+
shinestacker/_version.py,sha256=R4yO1-T_1mGmXG30xDxIA44z6cxCh-PK6l33V9MTqxk,21
|
|
3
3
|
shinestacker/algorithms/__init__.py,sha256=1FwVJ3w9GGbFFkjYJRUedTvcdE4j0ieSgaH9RC9iCY4,877
|
|
4
|
-
shinestacker/algorithms/align.py,sha256=
|
|
4
|
+
shinestacker/algorithms/align.py,sha256=mb44u-YxZI1TTSHz81nRpX_2c8awlOhnGrK0LyfTQeQ,33543
|
|
5
5
|
shinestacker/algorithms/align_auto.py,sha256=pJetw6zZEWQLouzcelkI8gD4cPiOp887ePXzVbm0E6Q,3800
|
|
6
|
-
shinestacker/algorithms/align_parallel.py,sha256=
|
|
7
|
-
shinestacker/algorithms/balance.py,sha256=
|
|
6
|
+
shinestacker/algorithms/align_parallel.py,sha256=5ru4HDcUObLNDP8bPUgsgwpLyflpcyVA16pPqHpYZfs,17671
|
|
7
|
+
shinestacker/algorithms/balance.py,sha256=aoqnc1u5A2C3R7fKaOoKnzudRiOT8GRIu4LEP-uzyZQ,24053
|
|
8
8
|
shinestacker/algorithms/base_stack_algo.py,sha256=RzxvLDHqxqhnAl83u2onjvfsRea1qGK_blbh2Vo0kxA,2670
|
|
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
12
|
shinestacker/algorithms/multilayer.py,sha256=WlB4L5oY9qra3w7Qahg-tqO6S_s3pMB_LmGR8PPR_7w,9904
|
|
13
|
-
shinestacker/algorithms/noise_detection.py,sha256=
|
|
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
18
|
shinestacker/algorithms/stack.py,sha256=V9YX0CbNWrgAo7_uti64rmmuwU6RsRcjDoBpsES4aSE,5137
|
|
19
19
|
shinestacker/algorithms/stack_framework.py,sha256=OMrjD5dKquHQXhM7TfLRExDsqN1n938WWhGAfkPYLZM,13883
|
|
20
|
-
shinestacker/algorithms/utils.py,sha256=
|
|
21
|
-
shinestacker/algorithms/vignetting.py,sha256=
|
|
20
|
+
shinestacker/algorithms/utils.py,sha256=l6GJpEXpzDr_ml9Not03a1_F7wYvPwn8JkEWDuNwL9o,12116
|
|
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
|
|
@@ -31,30 +31,31 @@ shinestacker/app/retouch.py,sha256=dpSozNWSxL6wIO0SMjoviDbXZbbfRN_rVLjeL324c54,2
|
|
|
31
31
|
shinestacker/config/__init__.py,sha256=aXxi-LmAvXd0daIFrVnTHE5OCaYeK1uf1BKMr7oaXQs,197
|
|
32
32
|
shinestacker/config/config.py,sha256=eBko2D3ADhLTIm9X6hB_a_WsIjwgfE-qmBVkhP1XSvc,1636
|
|
33
33
|
shinestacker/config/constants.py,sha256=EEdr7pZg4JpbIjUWaP7kJQfTuBB85FN739myDNAfn8A,8301
|
|
34
|
-
shinestacker/config/gui_constants.py,sha256=
|
|
34
|
+
shinestacker/config/gui_constants.py,sha256=7oZtBaeUxzU7WmbcR7yvQVlzQyRUaIMRcxY3SrKRGOk,2589
|
|
35
35
|
shinestacker/core/__init__.py,sha256=IUEIx6SQ3DygDEHN3_E6uKpHjHtUa4a_U_1dLd_8yEU,484
|
|
36
36
|
shinestacker/core/colors.py,sha256=kr_tJA1iRsdck2JaYDb2lS-codZ4Ty9gdu3kHfiWvuM,1340
|
|
37
|
-
shinestacker/core/core_utils.py,sha256=
|
|
37
|
+
shinestacker/core/core_utils.py,sha256=1LYj19Dfc9jZN9-4dlf1paximDH5WZYa7DXvKr7R7QY,1719
|
|
38
38
|
shinestacker/core/exceptions.py,sha256=2-noG-ORAGdvDhL8jBQFs0xxZS4fI6UIkMqrWekgk2c,1618
|
|
39
39
|
shinestacker/core/framework.py,sha256=QaTfnzEUHwzlbyFG7KzeyteckTSWHWEEJE4d5Tc8H18,11015
|
|
40
40
|
shinestacker/core/logging.py,sha256=9SuSSy9Usbh7zqmLYMqkmy-VBkOJW000lwqAR0XQs30,3067
|
|
41
41
|
shinestacker/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
-
shinestacker/gui/action_config.py,sha256=
|
|
43
|
-
shinestacker/gui/action_config_dialog.py,sha256=
|
|
42
|
+
shinestacker/gui/action_config.py,sha256=HeDbRy3YSl9IldLZBCdXj7eWIfTTHmUqC_AW9LJU2Pk,25859
|
|
43
|
+
shinestacker/gui/action_config_dialog.py,sha256=Jp0LVhE0gdz70ve47LCL5N2sSLuj8_arZb4xL50_pkk,41101
|
|
44
44
|
shinestacker/gui/base_form_dialog.py,sha256=KAUQNtmJazttmOIe4E4pFifbtvcByTAhtCmcIYeA4UE,766
|
|
45
45
|
shinestacker/gui/colors.py,sha256=-HaFprDuzRSKjXoZfX1rdOuvawQAkazqdgLBEiZcFII,1476
|
|
46
46
|
shinestacker/gui/flow_layout.py,sha256=3yBU_z7VtvHKpx1H97CHVd81eq9pe1Dcja2EZBGGKcI,3791
|
|
47
47
|
shinestacker/gui/folder_file_selection.py,sha256=IYWfZQFkoD5iO7zJ7BxVVDP9F3Dc0EXLILAhL4q-Cb8,4117
|
|
48
48
|
shinestacker/gui/gui_images.py,sha256=k39DpdsxcmYoRdHNNZj6OpFAas0GOHS4JSG542wfheg,5728
|
|
49
49
|
shinestacker/gui/gui_logging.py,sha256=kiZcrC2AFYCWgPZo0O5SKw-E5cFrezwf4anS3HjPuNw,8168
|
|
50
|
-
shinestacker/gui/gui_run.py,sha256=
|
|
51
|
-
shinestacker/gui/main_window.py,sha256=
|
|
52
|
-
shinestacker/gui/menu_manager.py,sha256=
|
|
50
|
+
shinestacker/gui/gui_run.py,sha256=zr7x4BVmM0n_ZRsSEaJVVKvHSWHuwhftgkUvgeg90gU,15767
|
|
51
|
+
shinestacker/gui/main_window.py,sha256=5k_9TiZT9idKCmovUFYpUTSEQQj-DMQrlyq9dAgY1MU,24800
|
|
52
|
+
shinestacker/gui/menu_manager.py,sha256=b5Cxh6uddOlio8i7fRISbGDJI-oe0ds6LIF5dWM7leI,11263
|
|
53
53
|
shinestacker/gui/new_project.py,sha256=VSUaq1xm9CR0gimKHRKfCdQOQ-ErE1sxGmu6x14nlAQ,16113
|
|
54
|
-
shinestacker/gui/project_controller.py,sha256=
|
|
54
|
+
shinestacker/gui/project_controller.py,sha256=W4sbBGEPVtfF9F1rC-6Y0oKLq_y94HuFBvZRj87xNKQ,16272
|
|
55
55
|
shinestacker/gui/project_converter.py,sha256=Gmna0HwbvACcXiX74TaQYumif8ZV8sZ2APLTMM-L1mU,7436
|
|
56
56
|
shinestacker/gui/project_editor.py,sha256=lSgQ42IoaobHs-NQQWT88Qhg5l7nu5ejxAO5VgIupr8,25498
|
|
57
57
|
shinestacker/gui/project_model.py,sha256=eRUmH3QmRzDtPtZoxgT6amKzN8_5XzwjHgEJeL-_JOE,4263
|
|
58
|
+
shinestacker/gui/recent_file_manager.py,sha256=FtatDGUqwmXXchepZdA0YRNzqCRS9Qp4EL9R9Wsg5qE,3731
|
|
58
59
|
shinestacker/gui/select_path_widget.py,sha256=HSwgSr702w5Et4c-6nkRXnIpm1KFqKJetAF5xQNa5zI,1017
|
|
59
60
|
shinestacker/gui/sys_mon.py,sha256=zU41YYVeqQ1-v6oGIh2_BFzQWq87keN-398Wdm59-Nk,3526
|
|
60
61
|
shinestacker/gui/tab_widget.py,sha256=VgRmuktWXCgbXbV7c1Tho0--W5_EmmzXPfzRZgwhGfg,2965
|
|
@@ -69,30 +70,34 @@ shinestacker/gui/img/forward-button-icon.png,sha256=lNw86T4TOEd_uokHYF8myGSGUXzd
|
|
|
69
70
|
shinestacker/gui/img/play-button-round-icon.png,sha256=9j6Ks9mOGa-2cXyRFpimepAAvSaHzqJKBfxShRb4_dE,4595
|
|
70
71
|
shinestacker/gui/img/plus-round-line-icon.png,sha256=LS068Hlu-CeBvJuB3dwwdJg1lZq6D5MUIv53lu1yKJA,7534
|
|
71
72
|
shinestacker/retouch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
|
-
shinestacker/retouch/base_filter.py,sha256=
|
|
73
|
+
shinestacker/retouch/base_filter.py,sha256=l8sdqKJ8OS2EEwMVVF8DLLLqE_kvYudQ_nUBo_GnC-0,10346
|
|
73
74
|
shinestacker/retouch/brush.py,sha256=dzD2FzSpBIPdJRmTZobcrQ1FrVd3tF__ZPnUplNE72s,357
|
|
74
75
|
shinestacker/retouch/brush_gradient.py,sha256=F5SFhyzl8YTMqjJU3jK8BrIlLCYLUvITd5wz3cQE4xk,1453
|
|
75
|
-
shinestacker/retouch/brush_preview.py,sha256=
|
|
76
|
-
shinestacker/retouch/brush_tool.py,sha256=
|
|
76
|
+
shinestacker/retouch/brush_preview.py,sha256=V-6yRH-paXOEC9bTCJ1NiE6xkTPUiJQGo1N61MMG1AI,4969
|
|
77
|
+
shinestacker/retouch/brush_tool.py,sha256=8uVncTA375uC3Nhp2YM0eZjpOR-nN47i2eGjN8tJzOU,8714
|
|
77
78
|
shinestacker/retouch/denoise_filter.py,sha256=TDUHzhRKlKvCa3D5SCYCZKTpjcl81kGwmONsgSDtO1k,440
|
|
78
|
-
shinestacker/retouch/display_manager.py,sha256=
|
|
79
|
+
shinestacker/retouch/display_manager.py,sha256=9HKCU8HFs3OyenEa2sk1FanaRCWpmPt77x6G4JfhJ-c,8888
|
|
79
80
|
shinestacker/retouch/exif_data.py,sha256=LF-fRXW-reMq-xJ_QRE5j8DC2LVGKIlC6MR3QbC1cdg,1896
|
|
80
81
|
shinestacker/retouch/file_loader.py,sha256=z02-A8_uDZxayI1NFTxT2GVUvEBWStchX9hlN1o5-0U,4784
|
|
81
82
|
shinestacker/retouch/filter_manager.py,sha256=SdYIZkZBUvuB6wDG0moGWav5sfEvIcB9ioUJR5wJFts,388
|
|
82
83
|
shinestacker/retouch/icon_container.py,sha256=6gw1HO1bC2FrdB4dc_iH81DQuLjzuvRGksZ2hKLT9yA,585
|
|
83
|
-
shinestacker/retouch/image_editor_ui.py,sha256=
|
|
84
|
-
shinestacker/retouch/
|
|
85
|
-
shinestacker/retouch/
|
|
84
|
+
shinestacker/retouch/image_editor_ui.py,sha256=Y7gzbgkWa3ehQ9dUahiY4TGoQPkwI00BHTGwobdm8L4,32442
|
|
85
|
+
shinestacker/retouch/image_view_status.py,sha256=8M1IA0T2cATWhOx1Zv0K9u8yMkujSnWLqy5zlEEy5c4,1745
|
|
86
|
+
shinestacker/retouch/image_viewer.py,sha256=TV4RsEGQC4nhpfS7MCjQDd-ASojcVZpbcFKbi-TuDhQ,4187
|
|
87
|
+
shinestacker/retouch/io_gui_handler.py,sha256=Upt8VWwDUrB-UnqCI54SQQwfR4w9kfznCdRL2UwupWQ,12010
|
|
86
88
|
shinestacker/retouch/io_manager.py,sha256=JUAA--AK0mVa1PTErJTnBFjaXIle5Qs7Ow0Wkd8at0o,2437
|
|
87
89
|
shinestacker/retouch/layer_collection.py,sha256=Q7zoCYRn__jYkfrEC2lY1uKHWfOUbsJ27xaYHIoKVxo,5730
|
|
88
|
-
shinestacker/retouch/
|
|
90
|
+
shinestacker/retouch/overlaid_view.py,sha256=J4r9DqHzhPrPgJ2IUGuAB26UGxkr2Kf50NUo_NM6w98,8468
|
|
91
|
+
shinestacker/retouch/shortcuts_help.py,sha256=EDxwR7MZwUC9NHLjvqAlh5iEHT9g3g8Tzl18VUGErI4,4130
|
|
92
|
+
shinestacker/retouch/sidebyside_view.py,sha256=tS50VzmTc0Iqf9k3fIbQ3jeIIkDyOO_6TJoNiGg7pLI,19423
|
|
89
93
|
shinestacker/retouch/undo_manager.py,sha256=_ekbcOLcPbQLY7t-o8wf-b1uA6OPY9rRyLM-KqMlQRo,3257
|
|
90
94
|
shinestacker/retouch/unsharp_mask_filter.py,sha256=uFnth8fpZFGhdIgJCnS8x5v6lBQgJ3hX0CBke9pFXeM,3510
|
|
95
|
+
shinestacker/retouch/view_strategy.py,sha256=DjBXWsrJJPU4UmdHIeqy_AI6ltDWHBr-RRaFwwYwc04,17061
|
|
91
96
|
shinestacker/retouch/vignetting_filter.py,sha256=MA97rQkSL0D-Nh-n2L4AiPR064RoTROkvza4tw84g9U,3658
|
|
92
97
|
shinestacker/retouch/white_balance_filter.py,sha256=glMBYlmrF-i_OrB3sGUpjZE6X4FQdyLC4GBy2bWtaFc,6056
|
|
93
|
-
shinestacker-1.
|
|
94
|
-
shinestacker-1.
|
|
95
|
-
shinestacker-1.
|
|
96
|
-
shinestacker-1.
|
|
97
|
-
shinestacker-1.
|
|
98
|
-
shinestacker-1.
|
|
98
|
+
shinestacker-1.4.0.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
|
|
99
|
+
shinestacker-1.4.0.dist-info/METADATA,sha256=gqcYTHpTNsHmYEUz76mZzWXREe3d0D_iVmfesRQsyM0,6972
|
|
100
|
+
shinestacker-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
101
|
+
shinestacker-1.4.0.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
|
|
102
|
+
shinestacker-1.4.0.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
|
|
103
|
+
shinestacker-1.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|