shinestacker 1.5.0__py3-none-any.whl → 1.5.2__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/app/args.py +23 -0
- shinestacker/app/main.py +17 -8
- shinestacker/app/project.py +2 -3
- shinestacker/app/retouch.py +8 -4
- shinestacker/config/gui_constants.py +2 -2
- shinestacker/gui/new_project.py +17 -14
- shinestacker/retouch/base_filter.py +59 -35
- shinestacker/retouch/brush_preview.py +29 -15
- shinestacker/retouch/denoise_filter.py +4 -3
- shinestacker/retouch/display_manager.py +14 -19
- shinestacker/retouch/filter_manager.py +11 -3
- shinestacker/retouch/image_editor_ui.py +48 -96
- shinestacker/retouch/image_viewer.py +31 -27
- shinestacker/retouch/io_gui_handler.py +0 -3
- shinestacker/retouch/overlaid_view.py +38 -17
- shinestacker/retouch/shortcuts_help.py +35 -31
- shinestacker/retouch/sidebyside_view.py +57 -76
- shinestacker/retouch/unsharp_mask_filter.py +5 -4
- shinestacker/retouch/view_strategy.py +176 -89
- shinestacker/retouch/vignetting_filter.py +4 -3
- shinestacker/retouch/white_balance_filter.py +62 -17
- {shinestacker-1.5.0.dist-info → shinestacker-1.5.2.dist-info}/METADATA +1 -1
- {shinestacker-1.5.0.dist-info → shinestacker-1.5.2.dist-info}/RECORD +28 -27
- {shinestacker-1.5.0.dist-info → shinestacker-1.5.2.dist-info}/WHEEL +0 -0
- {shinestacker-1.5.0.dist-info → shinestacker-1.5.2.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.5.0.dist-info → shinestacker-1.5.2.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.5.0.dist-info → shinestacker-1.5.2.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, R0904, R0915, E0611, R0902, R0911, R0914, E1003
|
|
2
2
|
from PySide6.QtCore import Qt, Signal, QEvent, QRectF
|
|
3
|
-
from PySide6.QtGui import
|
|
4
|
-
from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QFrame
|
|
5
|
-
from .. config.gui_constants import gui_constants
|
|
3
|
+
from PySide6.QtGui import QCursor
|
|
4
|
+
from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QFrame
|
|
6
5
|
from .view_strategy import ViewStrategy, ImageGraphicsViewBase, ViewSignals
|
|
7
6
|
|
|
8
7
|
|
|
@@ -59,7 +58,6 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
59
58
|
self.current_view.setFocusPolicy(Qt.NoFocus)
|
|
60
59
|
self.master_view.setFocusPolicy(Qt.NoFocus)
|
|
61
60
|
self.current_brush_cursor = None
|
|
62
|
-
self.setup_current_brush_cursor()
|
|
63
61
|
|
|
64
62
|
def setup_layout(self):
|
|
65
63
|
raise NotImplementedError("Subclasses must implement setup_layout")
|
|
@@ -148,6 +146,14 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
148
146
|
self.pixmap_item_current: self.current_view
|
|
149
147
|
}
|
|
150
148
|
|
|
149
|
+
def hide_brush_cursor(self):
|
|
150
|
+
super().hide_brush_cursor()
|
|
151
|
+
self.current_brush_cursor.hide()
|
|
152
|
+
|
|
153
|
+
def show_brush_cursor(self):
|
|
154
|
+
super().show_brush_cursor()
|
|
155
|
+
self.current_brush_cursor.show()
|
|
156
|
+
|
|
151
157
|
# pylint: disable=C0103
|
|
152
158
|
def focusInEvent(self, event):
|
|
153
159
|
super().focusInEvent(event)
|
|
@@ -180,10 +186,11 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
180
186
|
self.master_view.setCursor(Qt.OpenHandCursor)
|
|
181
187
|
self.current_view.setCursor(Qt.OpenHandCursor)
|
|
182
188
|
else:
|
|
189
|
+
if self.brush_cursor is None or self.current_brush_cursor is None:
|
|
190
|
+
self.setup_brush_cursor()
|
|
183
191
|
self.master_view.setCursor(Qt.BlankCursor)
|
|
184
192
|
self.current_view.setCursor(Qt.BlankCursor)
|
|
185
|
-
|
|
186
|
-
self.brush_cursor.show()
|
|
193
|
+
self.brush_cursor.show()
|
|
187
194
|
super().enterEvent(event)
|
|
188
195
|
|
|
189
196
|
def leaveEvent(self, event):
|
|
@@ -192,10 +199,9 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
192
199
|
self.master_view.setCursor(Qt.ArrowCursor)
|
|
193
200
|
self.current_view.setCursor(Qt.ArrowCursor)
|
|
194
201
|
else:
|
|
195
|
-
if self.brush_cursor is
|
|
196
|
-
self.
|
|
197
|
-
|
|
198
|
-
self.current_brush_cursor.hide()
|
|
202
|
+
if self.brush_cursor is None or self.current_brush_cursor is None:
|
|
203
|
+
self.setup_brush_cursor()
|
|
204
|
+
self.hide_brush_cursor()
|
|
199
205
|
self.master_view.setCursor(Qt.ArrowCursor)
|
|
200
206
|
self.current_view.setCursor(Qt.ArrowCursor)
|
|
201
207
|
super().leaveEvent(event)
|
|
@@ -209,6 +215,16 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
209
215
|
super().keyReleaseEvent(event)
|
|
210
216
|
if event.key() == Qt.Key_Space:
|
|
211
217
|
self.update_brush_cursor()
|
|
218
|
+
|
|
219
|
+
def get_mouse_callbacks(self):
|
|
220
|
+
return self.master_view.mousePressEvent, self.current_view.mousePressEvent
|
|
221
|
+
|
|
222
|
+
def set_mouse_callbacks(self, callbacks):
|
|
223
|
+
if isinstance(callbacks, tuple):
|
|
224
|
+
self.master_view.mousePressEvent, self.current_view.mousePressEvent = callbacks
|
|
225
|
+
else:
|
|
226
|
+
self.master_view.mousePressEvent = callbacks
|
|
227
|
+
self.current_view.mousePressEvent = callbacks
|
|
212
228
|
# pylint: enable=C0103
|
|
213
229
|
|
|
214
230
|
# pylint: enable=R0801
|
|
@@ -248,33 +264,12 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
248
264
|
def setup_brush_cursor(self):
|
|
249
265
|
super().setup_brush_cursor()
|
|
250
266
|
self.setup_current_brush_cursor()
|
|
251
|
-
self.update_cursor_pen_width()
|
|
252
267
|
|
|
253
268
|
def setup_current_brush_cursor(self):
|
|
254
269
|
if not self.brush:
|
|
255
270
|
return
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
self.current_scene.removeItem(item)
|
|
259
|
-
pen_width = gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()
|
|
260
|
-
pen = QPen(QColor(255, 0, 0), pen_width, Qt.DotLine)
|
|
261
|
-
brush = Qt.NoBrush
|
|
262
|
-
self.current_brush_cursor = self.current_scene.addEllipse(
|
|
263
|
-
0, 0, self.brush.size, self.brush.size, pen, brush)
|
|
264
|
-
self.current_brush_cursor.setZValue(1000)
|
|
265
|
-
self.current_brush_cursor.hide()
|
|
266
|
-
|
|
267
|
-
def update_current_brush_cursor(self, scene_pos):
|
|
268
|
-
if not self.current_brush_cursor or not self.isVisible():
|
|
269
|
-
return
|
|
270
|
-
size = self.brush.size
|
|
271
|
-
radius = size / 2
|
|
272
|
-
self.current_brush_cursor.setRect(
|
|
273
|
-
scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
274
|
-
if self.brush_cursor and self.brush_cursor.isVisible():
|
|
275
|
-
self.current_brush_cursor.show()
|
|
276
|
-
else:
|
|
277
|
-
self.current_brush_cursor.hide()
|
|
271
|
+
self.current_brush_cursor = self.create_alt_circle(
|
|
272
|
+
self.get_current_scene(), line_style=Qt.SolidLine)
|
|
278
273
|
|
|
279
274
|
def update_cursor_pen_width(self):
|
|
280
275
|
pen_width = super().update_cursor_pen_width()
|
|
@@ -287,48 +282,42 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
287
282
|
def update_brush_cursor(self):
|
|
288
283
|
if self.empty():
|
|
289
284
|
return
|
|
285
|
+
if self.brush_cursor is None or self.current_brush_cursor is None:
|
|
286
|
+
self.setup_brush_cursor()
|
|
290
287
|
self.update_cursor_pen_width()
|
|
288
|
+
if self.space_pressed:
|
|
289
|
+
cursor_style = Qt.OpenHandCursor if not self.scrolling else Qt.ClosedHandCursor
|
|
290
|
+
self.master_view.setCursor(cursor_style)
|
|
291
|
+
self.current_view.setCursor(cursor_style)
|
|
292
|
+
self.hide_brush_cursor()
|
|
293
|
+
return
|
|
294
|
+
self.master_view.setCursor(Qt.BlankCursor)
|
|
295
|
+
self.current_view.setCursor(Qt.BlankCursor)
|
|
291
296
|
mouse_pos_global = QCursor.pos()
|
|
292
297
|
mouse_pos_current = self.current_view.mapFromGlobal(mouse_pos_global)
|
|
293
298
|
mouse_pos_master = self.master_view.mapFromGlobal(mouse_pos_global)
|
|
294
299
|
current_has_mouse = self.current_view.rect().contains(mouse_pos_current)
|
|
295
300
|
master_has_mouse = self.master_view.rect().contains(mouse_pos_master)
|
|
301
|
+
self.current_brush_cursor.hide()
|
|
296
302
|
if master_has_mouse:
|
|
297
|
-
self.
|
|
303
|
+
if self.cursor_style == 'preview':
|
|
304
|
+
self.show_brush_preview()
|
|
298
305
|
super().update_brush_cursor()
|
|
299
306
|
self.sync_current_cursor_with_master()
|
|
300
|
-
if self.space_pressed:
|
|
301
|
-
cursor_style = Qt.OpenHandCursor if not self.scrolling else Qt.ClosedHandCursor
|
|
302
|
-
self.master_view.setCursor(cursor_style)
|
|
303
|
-
self.current_view.setCursor(cursor_style)
|
|
304
|
-
else:
|
|
305
|
-
self.master_view.setCursor(Qt.BlankCursor)
|
|
306
|
-
self.current_view.setCursor(Qt.BlankCursor)
|
|
307
307
|
elif current_has_mouse:
|
|
308
|
-
self.
|
|
308
|
+
self.hide_brush_preview()
|
|
309
309
|
scene_pos = self.current_view.mapToScene(mouse_pos_current)
|
|
310
310
|
size = self.brush.size
|
|
311
311
|
radius = size / 2
|
|
312
312
|
self.current_brush_cursor.setRect(
|
|
313
313
|
scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
314
314
|
self.current_brush_cursor.show()
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
self.brush_cursor.show()
|
|
319
|
-
if self.space_pressed:
|
|
320
|
-
cursor_style = Qt.OpenHandCursor \
|
|
321
|
-
if not self.panning_current else Qt.ClosedHandCursor
|
|
322
|
-
self.current_view.setCursor(cursor_style)
|
|
323
|
-
self.master_view.setCursor(cursor_style)
|
|
324
|
-
else:
|
|
325
|
-
self.current_view.setCursor(Qt.BlankCursor)
|
|
326
|
-
self.master_view.setCursor(Qt.BlankCursor)
|
|
315
|
+
self.brush_cursor.setRect(
|
|
316
|
+
scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
317
|
+
self.brush_cursor.show()
|
|
327
318
|
else:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if self.current_brush_cursor:
|
|
331
|
-
self.current_brush_cursor.hide()
|
|
319
|
+
self.brush_cursor.hide()
|
|
320
|
+
self.current_brush_cursor.hide()
|
|
332
321
|
self.master_view.setCursor(Qt.ArrowCursor)
|
|
333
322
|
self.current_view.setCursor(Qt.ArrowCursor)
|
|
334
323
|
|
|
@@ -379,12 +368,14 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
379
368
|
self.pixmap_item_master.setPixmap(pixmap)
|
|
380
369
|
img_width, img_height = pixmap.width(), pixmap.height()
|
|
381
370
|
self.set_max_min_scales(img_width, img_height)
|
|
382
|
-
self.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
371
|
+
view_rect = self.master_view.viewport().rect()
|
|
372
|
+
scale_x = view_rect.width() / img_width
|
|
373
|
+
scale_y = view_rect.height() / img_height
|
|
374
|
+
scale_factor = min(scale_x, scale_y)
|
|
375
|
+
scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
|
|
376
|
+
self.set_zoom_factor(scale_factor)
|
|
386
377
|
self.master_view.resetTransform()
|
|
387
|
-
self.
|
|
378
|
+
self.master_view.scale(scale_factor, scale_factor)
|
|
388
379
|
self.master_view.centerOn(self.pixmap_item_master)
|
|
389
380
|
center = self.master_scene.sceneRect().center()
|
|
390
381
|
self.brush_preview.setPos(max(0, min(center.x(), img_width)),
|
|
@@ -399,10 +390,9 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
399
390
|
self.current_scene.setSceneRect(QRectF(pixmap.rect()))
|
|
400
391
|
self.pixmap_item_current.setPixmap(pixmap)
|
|
401
392
|
self.current_view.resetTransform()
|
|
402
|
-
self.
|
|
393
|
+
self.master_view.scale(self.zoom_factor(), self.zoom_factor())
|
|
403
394
|
self.current_scene.setSceneRect(QRectF(self.pixmap_item_current.boundingRect()))
|
|
404
395
|
self.center_image(self.current_view)
|
|
405
|
-
self.update_cursor_pen_width()
|
|
406
396
|
|
|
407
397
|
def arrange_images(self):
|
|
408
398
|
if self.status.empty():
|
|
@@ -415,13 +405,6 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
415
405
|
self.center_image(self.master_view)
|
|
416
406
|
self.apply_zoom()
|
|
417
407
|
|
|
418
|
-
def set_brush(self, brush):
|
|
419
|
-
super().set_brush(brush)
|
|
420
|
-
if self.brush_cursor:
|
|
421
|
-
self.master_scene.removeItem(self.brush_cursor)
|
|
422
|
-
self.setup_brush_cursor()
|
|
423
|
-
self.setup_current_brush_cursor()
|
|
424
|
-
|
|
425
408
|
def clear_image(self):
|
|
426
409
|
super().clear_image()
|
|
427
410
|
self.setCursor(Qt.ArrowCursor)
|
|
@@ -439,10 +422,8 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
439
422
|
size = self.brush.size
|
|
440
423
|
radius = size / 2
|
|
441
424
|
self.current_brush_cursor.setRect(
|
|
442
|
-
scene_pos.x() - radius,
|
|
443
|
-
|
|
444
|
-
size, size
|
|
445
|
-
)
|
|
425
|
+
scene_pos.x() - radius, scene_pos.y() - radius,
|
|
426
|
+
size, size)
|
|
446
427
|
if self.brush_cursor.isVisible():
|
|
447
428
|
self.current_brush_cursor.show()
|
|
448
429
|
else:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# pylint: disable=C0114, C0115, C0116, E0611, W0221, R0902, R0914
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, E0611, W0221, R0902, R0914, R0913, R0917
|
|
2
2
|
from PySide6.QtWidgets import QHBoxLayout, QLabel, QSlider, QDialogButtonBox
|
|
3
3
|
from PySide6.QtCore import Qt, QTimer
|
|
4
4
|
from .. algorithms.sharpen import unsharp_mask
|
|
@@ -6,8 +6,9 @@ from .base_filter import BaseFilter
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class UnsharpMaskFilter(BaseFilter):
|
|
9
|
-
def __init__(self, name,
|
|
10
|
-
super().__init__(name,
|
|
9
|
+
def __init__(self, name, parent, image_viewer, layer_collection, undo_manager):
|
|
10
|
+
super().__init__(name, parent, image_viewer, layer_collection, undo_manager,
|
|
11
|
+
preview_at_startup=True)
|
|
11
12
|
self.max_range = 500.0
|
|
12
13
|
self.max_radius = 4.0
|
|
13
14
|
self.max_amount = 3.0
|
|
@@ -62,7 +63,7 @@ class UnsharpMaskFilter(BaseFilter):
|
|
|
62
63
|
self.threshold_slider.valueChanged.connect(
|
|
63
64
|
lambda v: update_value("Threshold", v, self.max_threshold, params["Threshold"][2]))
|
|
64
65
|
self.preview_timer.timeout.connect(do_preview)
|
|
65
|
-
self.
|
|
66
|
+
self.connect_preview_toggle(self.preview_check, do_preview, restore_original)
|
|
66
67
|
self.button_box.accepted.connect(dlg.accept)
|
|
67
68
|
self.button_box.rejected.connect(dlg.reject)
|
|
68
69
|
QTimer.singleShot(0, do_preview)
|
|
@@ -1,17 +1,79 @@
|
|
|
1
|
-
# pylint: disable=C0114, C0115, C0116, E0611, R0904, R0903, R0902, E1101, R0914
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, E0611, R0904, R0903, R0902, E1101, R0914, R0913, R0917
|
|
2
2
|
import math
|
|
3
3
|
from abc import abstractmethod
|
|
4
4
|
import numpy as np
|
|
5
5
|
from PySide6.QtCore import Qt, QPointF, QTime, QPoint, Signal, QRectF
|
|
6
|
-
from PySide6.QtGui import QImage, QPainter, QColor, QBrush, QPen, QCursor, QPixmap
|
|
6
|
+
from PySide6.QtGui import QImage, QPainter, QColor, QBrush, QPen, QCursor, QPixmap, QPainterPath
|
|
7
7
|
from PySide6.QtWidgets import (
|
|
8
|
-
QGraphicsEllipseItem, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
|
|
8
|
+
QGraphicsEllipseItem, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem,
|
|
9
|
+
QGraphicsItemGroup, QGraphicsPathItem)
|
|
9
10
|
from .. config.gui_constants import gui_constants
|
|
10
11
|
from .layer_collection import LayerCollectionHandler
|
|
11
12
|
from .brush_gradient import create_default_brush_gradient
|
|
12
13
|
from .brush_preview import BrushPreviewItem
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
class BrushCursor(QGraphicsItemGroup):
|
|
17
|
+
def __init__(self, x0, y0, size, pen, brush):
|
|
18
|
+
super().__init__()
|
|
19
|
+
self._pen = pen
|
|
20
|
+
self._radius = size / 2
|
|
21
|
+
self._brush = brush
|
|
22
|
+
self._rect = QRectF(x0 - self._radius, y0 - self._radius, size, size)
|
|
23
|
+
self._arc_items = []
|
|
24
|
+
self._create_arcs()
|
|
25
|
+
|
|
26
|
+
def _point_on_circle(self, phi_deg):
|
|
27
|
+
phi = phi_deg / 180.0 * math.pi
|
|
28
|
+
x0 = self._rect.x() + self._radius
|
|
29
|
+
y0 = self._rect.y() + self._radius
|
|
30
|
+
return x0 + self._radius * math.cos(phi), y0 - self._radius * math.sin(phi)
|
|
31
|
+
|
|
32
|
+
def _create_arcs(self):
|
|
33
|
+
for item in self._arc_items:
|
|
34
|
+
self.removeFromGroup(item)
|
|
35
|
+
if item.scene():
|
|
36
|
+
item.scene().removeItem(item)
|
|
37
|
+
self._arc_items = []
|
|
38
|
+
half_gap = 20
|
|
39
|
+
arcs = [half_gap, 90 + half_gap, 180 + half_gap, 270 + half_gap]
|
|
40
|
+
span_angle = 90 - 2 * half_gap
|
|
41
|
+
for start_angle in arcs:
|
|
42
|
+
path = QPainterPath()
|
|
43
|
+
path.moveTo(*self._point_on_circle(start_angle))
|
|
44
|
+
path.arcTo(self._rect, start_angle, span_angle)
|
|
45
|
+
arc_item = QGraphicsPathItem(path)
|
|
46
|
+
arc_item.setPen(self._pen)
|
|
47
|
+
arc_item.setBrush(Qt.NoBrush)
|
|
48
|
+
self.addToGroup(arc_item)
|
|
49
|
+
self._arc_items.append(arc_item)
|
|
50
|
+
|
|
51
|
+
# pylint: disable=C0103
|
|
52
|
+
def setPen(self, pen):
|
|
53
|
+
self._pen = pen
|
|
54
|
+
for item in self._arc_items:
|
|
55
|
+
item.setPen(pen)
|
|
56
|
+
|
|
57
|
+
def pen(self):
|
|
58
|
+
return self._pen
|
|
59
|
+
|
|
60
|
+
def setBrush(self, brush):
|
|
61
|
+
self._brush = brush
|
|
62
|
+
for item in self._arc_items:
|
|
63
|
+
item.setBrush(Qt.NoBrush)
|
|
64
|
+
|
|
65
|
+
def brush(self):
|
|
66
|
+
return self._brush
|
|
67
|
+
|
|
68
|
+
def setRect(self, x, y, w, h):
|
|
69
|
+
self._rect = QRectF(x, y, w, h)
|
|
70
|
+
self._create_arcs()
|
|
71
|
+
|
|
72
|
+
def rect(self):
|
|
73
|
+
return self._rect
|
|
74
|
+
# pylint: enable=C0103
|
|
75
|
+
|
|
76
|
+
|
|
15
77
|
class ViewSignals:
|
|
16
78
|
temp_view_requested = Signal(bool)
|
|
17
79
|
brush_operation_started = Signal(QPoint)
|
|
@@ -38,14 +100,11 @@ class ImageGraphicsViewBase(QGraphicsView):
|
|
|
38
100
|
class ViewStrategy(LayerCollectionHandler):
|
|
39
101
|
def __init__(self, layer_collection, status):
|
|
40
102
|
LayerCollectionHandler.__init__(self, layer_collection)
|
|
41
|
-
self.display_manager = None
|
|
42
103
|
self.status = status
|
|
43
104
|
self.brush = None
|
|
44
105
|
self.brush_cursor = None
|
|
45
|
-
self.display_manager = None
|
|
46
106
|
self.brush_preview = BrushPreviewItem(layer_collection)
|
|
47
107
|
self.cursor_style = gui_constants.DEFAULT_CURSOR_STYLE
|
|
48
|
-
self.allow_cursor_preview = True
|
|
49
108
|
self.control_pressed = False
|
|
50
109
|
self.space_pressed = False
|
|
51
110
|
self.gesture_active = False
|
|
@@ -118,38 +177,32 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
118
177
|
def arrange_images(self):
|
|
119
178
|
pass
|
|
120
179
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if master_qimage:
|
|
125
|
-
pixmap = QPixmap.fromImage(master_qimage)
|
|
126
|
-
self.get_master_pixmap().setPixmap(pixmap)
|
|
127
|
-
self.get_master_scene().setSceneRect(QRectF(pixmap.rect()))
|
|
128
|
-
self.get_master_view().horizontalScrollBar().setValue(self.status.h_scroll)
|
|
129
|
-
self.get_master_view().verticalScrollBar().setValue(self.status.v_scroll)
|
|
130
|
-
self.arrange_images()
|
|
180
|
+
@abstractmethod
|
|
181
|
+
def get_mouse_callbacks(self):
|
|
182
|
+
pass
|
|
131
183
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if current_qimage:
|
|
136
|
-
pixmap = QPixmap.fromImage(current_qimage)
|
|
137
|
-
self.get_current_pixmap().setPixmap(pixmap)
|
|
138
|
-
self.get_current_scene().setSceneRect(QRectF(pixmap.rect()))
|
|
139
|
-
self.get_current_view().horizontalScrollBar().setValue(self.status.h_scroll)
|
|
140
|
-
self.get_current_view().verticalScrollBar().setValue(self.status.v_scroll)
|
|
141
|
-
self.arrange_images()
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def set_mouse_callbacks(self, callbacks):
|
|
186
|
+
pass
|
|
142
187
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
188
|
+
def hide_brush_cursor(self):
|
|
189
|
+
if self.brush_cursor:
|
|
190
|
+
self.brush_cursor.hide()
|
|
191
|
+
|
|
192
|
+
def show_brush_cursor(self):
|
|
193
|
+
if self.brush_cursor:
|
|
194
|
+
self.brush_cursor.show()
|
|
195
|
+
|
|
196
|
+
def hide_brush_preview(self):
|
|
197
|
+
if self.brush_preview:
|
|
198
|
+
self.brush_preview.hide()
|
|
199
|
+
|
|
200
|
+
def show_brush_preview(self):
|
|
201
|
+
if self.brush_preview:
|
|
202
|
+
self.brush_preview.show()
|
|
150
203
|
|
|
151
|
-
def
|
|
152
|
-
|
|
204
|
+
def current_line_width(self):
|
|
205
|
+
return gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()
|
|
153
206
|
|
|
154
207
|
def zoom_factor(self):
|
|
155
208
|
return self.status.zoom_factor
|
|
@@ -181,13 +234,9 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
181
234
|
def set_preview_brush(self, brush):
|
|
182
235
|
self.brush_preview.brush = brush
|
|
183
236
|
|
|
184
|
-
def set_display_manager(self, dm):
|
|
185
|
-
self.display_manager = dm
|
|
186
|
-
|
|
187
237
|
def set_cursor_style(self, style):
|
|
188
238
|
self.cursor_style = style
|
|
189
|
-
|
|
190
|
-
self.update_brush_cursor()
|
|
239
|
+
self.update_brush_cursor()
|
|
191
240
|
|
|
192
241
|
def get_cursor_style(self):
|
|
193
242
|
return self.cursor_style
|
|
@@ -198,6 +247,42 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
198
247
|
def handle_key_release_event(self, _event):
|
|
199
248
|
return True
|
|
200
249
|
|
|
250
|
+
def update_view_display(self, layer, pixmap_item, scene, view):
|
|
251
|
+
if self.empty():
|
|
252
|
+
return
|
|
253
|
+
qimage = self.numpy_to_qimage(layer)
|
|
254
|
+
if qimage:
|
|
255
|
+
pixmap = QPixmap.fromImage(qimage)
|
|
256
|
+
pixmap_item.setPixmap(pixmap)
|
|
257
|
+
scene.setSceneRect(QRectF(pixmap.rect()))
|
|
258
|
+
view.horizontalScrollBar().setValue(self.status.h_scroll)
|
|
259
|
+
view.verticalScrollBar().setValue(self.status.v_scroll)
|
|
260
|
+
self.arrange_images()
|
|
261
|
+
|
|
262
|
+
def update_master_display(self):
|
|
263
|
+
self.update_view_display(
|
|
264
|
+
self.master_layer(),
|
|
265
|
+
self.get_master_pixmap(),
|
|
266
|
+
self.get_master_scene(),
|
|
267
|
+
self.get_master_view())
|
|
268
|
+
|
|
269
|
+
def update_current_display(self):
|
|
270
|
+
if self.number_of_layers() <= 0:
|
|
271
|
+
return
|
|
272
|
+
self.update_view_display(
|
|
273
|
+
self.current_layer(),
|
|
274
|
+
self.get_current_pixmap(),
|
|
275
|
+
self.get_current_view(),
|
|
276
|
+
self.get_current_view())
|
|
277
|
+
|
|
278
|
+
def update_cursor_pen_width(self):
|
|
279
|
+
width = self.current_line_width()
|
|
280
|
+
if self.brush_cursor is not None:
|
|
281
|
+
pen = self.brush_cursor.pen()
|
|
282
|
+
pen.setWidthF(width)
|
|
283
|
+
self.brush_cursor.setPen(pen)
|
|
284
|
+
return width
|
|
285
|
+
|
|
201
286
|
def clear_image(self):
|
|
202
287
|
for scene in self.get_scenes():
|
|
203
288
|
scene.clear()
|
|
@@ -207,16 +292,13 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
207
292
|
self.brush_preview = BrushPreviewItem(self.layer_collection)
|
|
208
293
|
self.get_master_scene().addItem(self.brush_preview)
|
|
209
294
|
self.setCursor(Qt.ArrowCursor)
|
|
210
|
-
|
|
211
|
-
self.brush_cursor.hide()
|
|
212
|
-
|
|
213
|
-
def cleanup_brush_preview(self):
|
|
214
|
-
if self.brush_cursor:
|
|
215
|
-
self.brush_cursor.hide()
|
|
216
|
-
self.brush_preview.hide()
|
|
295
|
+
self.hide_brush_cursor()
|
|
217
296
|
|
|
218
297
|
def set_master_image_np(self, img):
|
|
219
298
|
self.set_master_image(self.numpy_to_qimage(img))
|
|
299
|
+
if self.brush_cursor is None:
|
|
300
|
+
self.setup_brush_cursor()
|
|
301
|
+
self.show_master()
|
|
220
302
|
|
|
221
303
|
def numpy_to_qimage(self, array):
|
|
222
304
|
if array is None:
|
|
@@ -319,49 +401,65 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
319
401
|
self.update_brush_cursor()
|
|
320
402
|
self.update_cursor_pen_width()
|
|
321
403
|
|
|
322
|
-
def setup_outline_style(self):
|
|
323
|
-
self.brush_cursor.setPen(QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
324
|
-
gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()))
|
|
325
|
-
self.brush_cursor.setBrush(Qt.NoBrush)
|
|
326
|
-
|
|
327
404
|
def setup_simple_brush_style(self, center_x, center_y, radius):
|
|
328
405
|
gradient = create_default_brush_gradient(center_x, center_y, radius, self.brush)
|
|
329
406
|
self.brush_cursor.setPen(QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
330
|
-
|
|
407
|
+
self.current_line_width()))
|
|
331
408
|
self.brush_cursor.setBrush(QBrush(gradient))
|
|
332
409
|
|
|
333
|
-
def
|
|
334
|
-
if not self.brush:
|
|
335
|
-
return
|
|
336
|
-
scene = self.get_master_scene()
|
|
410
|
+
def create_circle(self, scene, line_style=Qt.SolidLine):
|
|
337
411
|
for item in scene.items():
|
|
338
412
|
if isinstance(item, QGraphicsEllipseItem) and item != self.brush_preview:
|
|
339
413
|
scene.removeItem(item)
|
|
340
|
-
pen = QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
414
|
+
pen = QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
415
|
+
self.current_line_width(), line_style)
|
|
416
|
+
brush = Qt.NoBrush
|
|
417
|
+
scene_center = scene.sceneRect().center()
|
|
418
|
+
brush_cursor = scene.addEllipse(
|
|
419
|
+
scene_center.x(), scene_center.y(),
|
|
420
|
+
self.brush.size, self.brush.size, pen, brush)
|
|
421
|
+
brush_cursor.setZValue(1000)
|
|
422
|
+
brush_cursor.hide()
|
|
423
|
+
return brush_cursor
|
|
424
|
+
|
|
425
|
+
def create_alt_circle(self, scene, line_style=Qt.SolidLine):
|
|
426
|
+
for item in scene.items():
|
|
427
|
+
if isinstance(item, BrushCursor) and item != self.brush_preview:
|
|
428
|
+
scene.removeItem(item)
|
|
429
|
+
pen = QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
|
|
430
|
+
self.current_line_width(), line_style)
|
|
341
431
|
brush = Qt.NoBrush
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
432
|
+
scene_center = scene.sceneRect().center()
|
|
433
|
+
brush_cursor = BrushCursor(
|
|
434
|
+
scene_center.x(), scene_center.y(),
|
|
435
|
+
self.brush.size, pen, brush
|
|
436
|
+
)
|
|
437
|
+
brush_cursor.setZValue(1000)
|
|
438
|
+
brush_cursor.hide()
|
|
439
|
+
scene.addItem(brush_cursor)
|
|
440
|
+
return brush_cursor
|
|
441
|
+
|
|
442
|
+
def setup_brush_cursor(self):
|
|
443
|
+
if not self.brush:
|
|
444
|
+
return
|
|
445
|
+
self.brush_cursor = self.create_circle(self.get_master_scene())
|
|
346
446
|
|
|
347
447
|
def update_brush_cursor(self):
|
|
348
|
-
if self.empty() or
|
|
448
|
+
if self.empty() or self.brush_cursor is None or not self.isVisible():
|
|
349
449
|
return
|
|
350
450
|
self.update_cursor_pen_width()
|
|
351
451
|
master_view = self.get_master_view()
|
|
352
452
|
mouse_pos = master_view.mapFromGlobal(QCursor.pos())
|
|
353
453
|
if not master_view.rect().contains(mouse_pos):
|
|
354
|
-
self.
|
|
454
|
+
self.hide_brush_cursor()
|
|
355
455
|
return
|
|
356
456
|
scene_pos = master_view.mapToScene(mouse_pos)
|
|
357
457
|
size = self.brush.size
|
|
358
458
|
radius = size / 2
|
|
359
459
|
self.brush_cursor.setRect(scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
360
|
-
allow_cursor_preview = self.display_manager.allow_cursor_preview()
|
|
361
460
|
if self.cursor_style == 'preview':
|
|
362
|
-
self.
|
|
363
|
-
|
|
364
|
-
self.brush_cursor.hide()
|
|
461
|
+
if self.brush_preview.isVisible():
|
|
462
|
+
self.hide_brush_cursor()
|
|
365
463
|
pos = QCursor.pos()
|
|
366
464
|
if isinstance(pos, QPointF):
|
|
367
465
|
scene_pos = pos
|
|
@@ -370,13 +468,10 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
370
468
|
scene_pos = master_view.mapToScene(cursor_pos)
|
|
371
469
|
self.brush_preview.update(scene_pos, int(size))
|
|
372
470
|
else:
|
|
373
|
-
self.
|
|
374
|
-
if self.cursor_style
|
|
375
|
-
self.setup_outline_style()
|
|
376
|
-
else:
|
|
471
|
+
self.hide_brush_preview()
|
|
472
|
+
if self.cursor_style != 'outline':
|
|
377
473
|
self.setup_simple_brush_style(scene_pos.x(), scene_pos.y(), radius)
|
|
378
|
-
|
|
379
|
-
self.brush_cursor.show()
|
|
474
|
+
self.show_brush_cursor()
|
|
380
475
|
|
|
381
476
|
def position_on_image(self, pos):
|
|
382
477
|
master_view = self.get_master_view()
|
|
@@ -417,8 +512,7 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
417
512
|
if event.key() == Qt.Key_Space and not self.scrolling:
|
|
418
513
|
self.space_pressed = True
|
|
419
514
|
self.get_master_view().setCursor(Qt.OpenHandCursor)
|
|
420
|
-
|
|
421
|
-
self.brush_cursor.hide()
|
|
515
|
+
self.hide_brush_cursor()
|
|
422
516
|
if self.handle_key_press_event(event):
|
|
423
517
|
if event.key() == Qt.Key_Control and not self.scrolling:
|
|
424
518
|
self.control_pressed = True
|
|
@@ -427,13 +521,11 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
427
521
|
def keyReleaseEvent(self, event):
|
|
428
522
|
if self.empty():
|
|
429
523
|
return
|
|
430
|
-
self.update_brush_cursor()
|
|
431
524
|
if event.key() == Qt.Key_Space:
|
|
432
525
|
self.space_pressed = False
|
|
433
526
|
if not self.scrolling:
|
|
434
527
|
self.get_master_view().setCursor(Qt.BlankCursor)
|
|
435
|
-
|
|
436
|
-
self.brush_cursor.show()
|
|
528
|
+
self.show_brush_cursor()
|
|
437
529
|
if self.handle_key_release_event(event):
|
|
438
530
|
if event.key() == Qt.Key_Control:
|
|
439
531
|
self.control_pressed = False
|
|
@@ -444,8 +536,7 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
444
536
|
self.setCursor(Qt.ArrowCursor)
|
|
445
537
|
else:
|
|
446
538
|
self.get_master_view().setCursor(Qt.ArrowCursor)
|
|
447
|
-
|
|
448
|
-
self.brush_cursor.hide()
|
|
539
|
+
self.hide_brush_cursor()
|
|
449
540
|
super().leaveEvent(event)
|
|
450
541
|
# pylint: enable=C0103
|
|
451
542
|
|
|
@@ -490,8 +581,7 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
490
581
|
master_view = self.get_master_view()
|
|
491
582
|
if self.space_pressed:
|
|
492
583
|
master_view.setCursor(Qt.ClosedHandCursor)
|
|
493
|
-
|
|
494
|
-
self.brush_cursor.hide()
|
|
584
|
+
self.hide_brush_cursor()
|
|
495
585
|
delta = position - self.last_mouse_pos
|
|
496
586
|
self.last_mouse_pos = position
|
|
497
587
|
self.scroll_view(master_view, delta.x(), delta.y())
|
|
@@ -508,8 +598,7 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
508
598
|
self.last_brush_pos = event.position()
|
|
509
599
|
self.brush_operation_started.emit(event.position().toPoint())
|
|
510
600
|
self.dragging = True
|
|
511
|
-
|
|
512
|
-
self.brush_cursor.show()
|
|
601
|
+
self.show_brush_cursor()
|
|
513
602
|
|
|
514
603
|
def mouse_release_event(self, event):
|
|
515
604
|
if self.empty():
|
|
@@ -517,12 +606,10 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
517
606
|
master_view = self.get_master_view()
|
|
518
607
|
if self.space_pressed:
|
|
519
608
|
master_view.setCursor(Qt.OpenHandCursor)
|
|
520
|
-
|
|
521
|
-
self.brush_cursor.hide()
|
|
609
|
+
self.hide_brush_cursor()
|
|
522
610
|
else:
|
|
523
611
|
master_view.setCursor(Qt.BlankCursor)
|
|
524
|
-
|
|
525
|
-
self.brush_cursor.show()
|
|
612
|
+
self.show_brush_cursor()
|
|
526
613
|
if event.button() == Qt.LeftButton:
|
|
527
614
|
if self.scrolling:
|
|
528
615
|
self.scrolling = False
|