shinestacker 1.4.0__py3-none-any.whl → 1.5.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.
- shinestacker/_version.py +1 -1
- shinestacker/app/args.py +23 -0
- shinestacker/app/main.py +18 -9
- shinestacker/app/project.py +2 -3
- shinestacker/app/retouch.py +8 -4
- shinestacker/config/gui_constants.py +7 -2
- shinestacker/gui/new_project.py +17 -14
- shinestacker/retouch/base_filter.py +8 -10
- shinestacker/retouch/brush_preview.py +28 -15
- shinestacker/retouch/display_manager.py +42 -42
- shinestacker/retouch/image_editor_ui.py +67 -53
- shinestacker/retouch/image_view_status.py +4 -0
- shinestacker/retouch/image_viewer.py +8 -4
- shinestacker/retouch/io_gui_handler.py +0 -3
- shinestacker/retouch/layer_collection.py +3 -0
- shinestacker/retouch/overlaid_view.py +99 -84
- shinestacker/retouch/shortcuts_help.py +35 -31
- shinestacker/retouch/sidebyside_view.py +141 -178
- shinestacker/retouch/transformation_manager.py +43 -0
- shinestacker/retouch/undo_manager.py +22 -3
- shinestacker/retouch/view_strategy.py +234 -64
- {shinestacker-1.4.0.dist-info → shinestacker-1.5.1.dist-info}/METADATA +7 -7
- {shinestacker-1.4.0.dist-info → shinestacker-1.5.1.dist-info}/RECORD +27 -25
- {shinestacker-1.4.0.dist-info → shinestacker-1.5.1.dist-info}/WHEEL +0 -0
- {shinestacker-1.4.0.dist-info → shinestacker-1.5.1.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.4.0.dist-info → shinestacker-1.5.1.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.4.0.dist-info → shinestacker-1.5.1.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# pylint: disable=C0114, C0115, C0116, E0611, R0902, R0914, R0915, R0904
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, E0611, R0902, R0914, R0915, R0904, W0108
|
|
2
2
|
from functools import partial
|
|
3
3
|
import numpy as np
|
|
4
4
|
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QFrame, QLabel, QMenu,
|
|
@@ -8,6 +8,7 @@ from PySide6.QtCore import Qt
|
|
|
8
8
|
from PySide6.QtGui import QGuiApplication
|
|
9
9
|
from .. config.constants import constants
|
|
10
10
|
from .. config.gui_constants import gui_constants
|
|
11
|
+
from .. gui.recent_file_manager import RecentFileManager
|
|
11
12
|
from .image_viewer import ImageViewer
|
|
12
13
|
from .shortcuts_help import ShortcutsHelp
|
|
13
14
|
from .brush import Brush
|
|
@@ -22,7 +23,7 @@ from .denoise_filter import DenoiseFilter
|
|
|
22
23
|
from .unsharp_mask_filter import UnsharpMaskFilter
|
|
23
24
|
from .white_balance_filter import WhiteBalanceFilter
|
|
24
25
|
from .vignetting_filter import VignettingFilter
|
|
25
|
-
from
|
|
26
|
+
from .transformation_manager import TransfromationManager
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
@@ -31,23 +32,23 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
31
32
|
LayerCollectionHandler.__init__(self, LayerCollection())
|
|
32
33
|
self._recent_file_manager = RecentFileManager("shinestacker-recent-images-files.txt")
|
|
33
34
|
self.thumbnail_highlight = gui_constants.THUMB_MASTER_HI_COLOR
|
|
34
|
-
self.undo_manager = UndoManager()
|
|
35
|
-
self.undo_action = None
|
|
36
|
-
self.redo_action = None
|
|
37
|
-
self.undo_manager.stack_changed.connect(self.update_undo_redo_actions)
|
|
38
35
|
self.io_gui_handler = None
|
|
39
36
|
self.display_manager = None
|
|
40
37
|
self.brush = Brush()
|
|
41
38
|
self.brush_tool = BrushTool()
|
|
42
39
|
self.modified = False
|
|
43
40
|
self.mask_layer = None
|
|
41
|
+
self.transformation_manager = TransfromationManager(self)
|
|
42
|
+
self.undo_manager = UndoManager(self.transformation_manager)
|
|
43
|
+
self.undo_action = None
|
|
44
|
+
self.redo_action = None
|
|
45
|
+
self.undo_manager.stack_changed.connect(self.update_undo_redo_actions)
|
|
44
46
|
self.filter_manager = FilterManager(self)
|
|
45
47
|
self.filter_manager.register_filter("Denoise", DenoiseFilter)
|
|
46
48
|
self.filter_manager.register_filter("Unsharp Mask", UnsharpMaskFilter)
|
|
47
49
|
self.filter_manager.register_filter("White Balance", WhiteBalanceFilter)
|
|
48
50
|
self.filter_manager.register_filter("Vignetting Correction", VignettingFilter)
|
|
49
51
|
self.shortcuts_help_dialog = None
|
|
50
|
-
|
|
51
52
|
self.update_title()
|
|
52
53
|
self.resize(1400, 900)
|
|
53
54
|
center = QGuiApplication.primaryScreen().geometry().center()
|
|
@@ -66,18 +67,15 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
66
67
|
side_layout = QVBoxLayout(side_panel)
|
|
67
68
|
side_layout.setContentsMargins(0, 0, 0, 0)
|
|
68
69
|
side_layout.setSpacing(2)
|
|
69
|
-
|
|
70
70
|
brush_panel = QFrame()
|
|
71
71
|
brush_panel.setFrameShape(QFrame.StyledPanel)
|
|
72
72
|
brush_panel.setContentsMargins(0, 0, 0, 0)
|
|
73
73
|
brush_layout = QVBoxLayout(brush_panel)
|
|
74
74
|
brush_layout.setContentsMargins(0, 0, 0, 0)
|
|
75
75
|
brush_layout.setSpacing(2)
|
|
76
|
-
|
|
77
76
|
brush_label = QLabel("Brush Size")
|
|
78
77
|
brush_label.setAlignment(Qt.AlignCenter)
|
|
79
78
|
brush_layout.addWidget(brush_label)
|
|
80
|
-
|
|
81
79
|
self.brush_size_slider = QSlider(Qt.Horizontal)
|
|
82
80
|
self.brush_size_slider.setRange(0, gui_constants.BRUSH_SIZE_SLIDER_MAX)
|
|
83
81
|
|
|
@@ -92,7 +90,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
92
90
|
|
|
93
91
|
self.brush_size_slider.setValue(brush_size_to_slider(self.brush.size))
|
|
94
92
|
brush_layout.addWidget(self.brush_size_slider)
|
|
95
|
-
|
|
96
93
|
hardness_label = QLabel("Brush Hardness")
|
|
97
94
|
hardness_label.setAlignment(Qt.AlignCenter)
|
|
98
95
|
brush_layout.addWidget(hardness_label)
|
|
@@ -100,7 +97,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
100
97
|
self.hardness_slider.setRange(0, 100)
|
|
101
98
|
self.hardness_slider.setValue(self.brush.hardness)
|
|
102
99
|
brush_layout.addWidget(self.hardness_slider)
|
|
103
|
-
|
|
104
100
|
opacity_label = QLabel("Brush Opacity")
|
|
105
101
|
opacity_label.setAlignment(Qt.AlignCenter)
|
|
106
102
|
brush_layout.addWidget(opacity_label)
|
|
@@ -108,7 +104,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
108
104
|
self.opacity_slider.setRange(0, 100)
|
|
109
105
|
self.opacity_slider.setValue(self.brush.opacity)
|
|
110
106
|
brush_layout.addWidget(self.opacity_slider)
|
|
111
|
-
|
|
112
107
|
flow_label = QLabel("Brush Flow")
|
|
113
108
|
flow_label.setAlignment(Qt.AlignCenter)
|
|
114
109
|
brush_layout.addWidget(flow_label)
|
|
@@ -116,7 +111,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
116
111
|
self.flow_slider.setRange(1, 100)
|
|
117
112
|
self.flow_slider.setValue(self.brush.flow)
|
|
118
113
|
brush_layout.addWidget(self.flow_slider)
|
|
119
|
-
|
|
120
114
|
side_layout.addWidget(brush_panel)
|
|
121
115
|
self.brush_preview_widget = QLabel()
|
|
122
116
|
self.brush_preview_widget.setContentsMargins(0, 0, 0, 0)
|
|
@@ -133,7 +127,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
133
127
|
self.brush_preview_widget.setFixedHeight(100)
|
|
134
128
|
brush_layout.addWidget(self.brush_preview_widget)
|
|
135
129
|
side_layout.addWidget(brush_panel)
|
|
136
|
-
|
|
137
130
|
master_label = QLabel("Master")
|
|
138
131
|
master_label.setStyleSheet("""
|
|
139
132
|
QLabel {
|
|
@@ -238,8 +231,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
238
231
|
self.master_thumbnail_label, self.thumbnail_list, parent=self)
|
|
239
232
|
self.io_gui_handler = IOGuiHandler(self.layer_collection, self.undo_manager, parent=self)
|
|
240
233
|
self.display_manager.status_message_requested.connect(self.show_status_message)
|
|
241
|
-
self.display_manager.cursor_preview_state_changed.connect(
|
|
242
|
-
self.image_viewer.set_allow_cursor_preview)
|
|
243
234
|
self.io_gui_handler.status_message_requested.connect(self.show_status_message)
|
|
244
235
|
self.io_gui_handler.update_title_requested.connect(self.update_title)
|
|
245
236
|
self.io_gui_handler.mark_as_modified_requested.connect(self.mark_as_modified)
|
|
@@ -277,8 +268,8 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
277
268
|
|
|
278
269
|
file_menu.addAction("&Close", self.close_file, "Ctrl+W")
|
|
279
270
|
file_menu.addSeparator()
|
|
280
|
-
file_menu.addAction("&Import
|
|
281
|
-
file_menu.addAction("Import &EXIF
|
|
271
|
+
file_menu.addAction("&Import Frames", self.io_gui_handler.import_frames)
|
|
272
|
+
file_menu.addAction("Import &EXIF Data", self.io_gui_handler.select_exif_path)
|
|
282
273
|
|
|
283
274
|
edit_menu = menubar.addMenu("&Edit")
|
|
284
275
|
self.undo_action = QAction("Undo", self)
|
|
@@ -293,7 +284,21 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
293
284
|
edit_menu.addAction(self.redo_action)
|
|
294
285
|
edit_menu.addSeparator()
|
|
295
286
|
|
|
296
|
-
|
|
287
|
+
transf_menu = QMenu("&Transform")
|
|
288
|
+
rotate_90_cw_action = QAction(gui_constants.ROTATE_90_CW_LABEL, self)
|
|
289
|
+
transf_menu.addAction(rotate_90_cw_action)
|
|
290
|
+
rotate_90_cw_action.triggered.connect(lambda: self.transformation_manager.rotate_90_cw())
|
|
291
|
+
rotate_90_ccw_action = QAction(gui_constants.ROTATE_90_CCW_LABEL, self)
|
|
292
|
+
transf_menu.addAction(rotate_90_ccw_action)
|
|
293
|
+
rotate_90_ccw_action.triggered.connect(lambda: self.transformation_manager.rotate_90_ccw())
|
|
294
|
+
rotate_180_action = QAction(gui_constants.ROTATE_180_LABEL, self)
|
|
295
|
+
rotate_180_action.triggered.connect(lambda: self.transformation_manager.rotate_180())
|
|
296
|
+
transf_menu.addAction(rotate_180_action)
|
|
297
|
+
edit_menu.addMenu(transf_menu)
|
|
298
|
+
|
|
299
|
+
edit_menu.addSeparator()
|
|
300
|
+
|
|
301
|
+
copy_action = QAction("Copy Current Layer to Master", self)
|
|
297
302
|
copy_action.setShortcut("Ctrl+M")
|
|
298
303
|
copy_action.triggered.connect(self.copy_layer_to_master)
|
|
299
304
|
edit_menu.addAction(copy_action)
|
|
@@ -310,59 +315,58 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
310
315
|
|
|
311
316
|
view_strategy_menu = QMenu("View &Mode", view_menu)
|
|
312
317
|
|
|
313
|
-
self.
|
|
318
|
+
self.view_mode_actions = {
|
|
314
319
|
'overlaid': QAction("Overlaid", self),
|
|
315
|
-
'sidebyside': QAction("Side
|
|
320
|
+
'sidebyside': QAction("Side by Side", self),
|
|
316
321
|
'topbottom': QAction("Top-Bottom", self)
|
|
317
322
|
}
|
|
318
|
-
overlaid_mode = self.
|
|
323
|
+
overlaid_mode = self.view_mode_actions['overlaid']
|
|
319
324
|
overlaid_mode.setShortcut("Ctrl+1")
|
|
320
325
|
overlaid_mode.setCheckable(True)
|
|
321
|
-
overlaid_mode.triggered.connect(lambda: set_strategy('overlaid'))
|
|
326
|
+
overlaid_mode.triggered.connect(lambda: self.set_strategy('overlaid'))
|
|
322
327
|
view_strategy_menu.addAction(overlaid_mode)
|
|
323
|
-
side_by_side_mode = self.
|
|
328
|
+
side_by_side_mode = self.view_mode_actions['sidebyside']
|
|
324
329
|
side_by_side_mode.setShortcut("Ctrl+2")
|
|
325
330
|
side_by_side_mode.setCheckable(True)
|
|
326
|
-
side_by_side_mode.triggered.connect(lambda: set_strategy('sidebyside'))
|
|
331
|
+
side_by_side_mode.triggered.connect(lambda: self.set_strategy('sidebyside'))
|
|
327
332
|
view_strategy_menu.addAction(side_by_side_mode)
|
|
328
|
-
side_by_side_mode = self.
|
|
333
|
+
side_by_side_mode = self.view_mode_actions['topbottom']
|
|
329
334
|
side_by_side_mode.setShortcut("Ctrl+3")
|
|
330
335
|
side_by_side_mode.setCheckable(True)
|
|
331
|
-
side_by_side_mode.triggered.connect(lambda: set_strategy('topbottom'))
|
|
336
|
+
side_by_side_mode.triggered.connect(lambda: self.set_strategy('topbottom'))
|
|
332
337
|
view_strategy_menu.addAction(side_by_side_mode)
|
|
333
338
|
view_menu.addMenu(view_strategy_menu)
|
|
334
339
|
|
|
335
|
-
def set_strategy(strategy):
|
|
336
|
-
self.image_viewer.set_strategy(strategy)
|
|
337
|
-
enable_shortcuts = strategy == 'overlaid'
|
|
338
|
-
self.view_master_action.setEnabled(enable_shortcuts)
|
|
339
|
-
self.view_individual_action.setEnabled(enable_shortcuts)
|
|
340
|
-
self.toggle_view_master_individual_action.setEnabled(enable_shortcuts)
|
|
341
|
-
for label, mode in self.view_action_modes.items():
|
|
342
|
-
mode.setEnabled(label != strategy)
|
|
343
|
-
mode.setChecked(label == strategy)
|
|
344
|
-
|
|
345
340
|
cursor_menu = view_menu.addMenu("Cursor Style")
|
|
346
341
|
|
|
347
|
-
|
|
348
|
-
|
|
342
|
+
self.cursor_style_actions = {
|
|
343
|
+
'brush': QAction("Simple Brush", self),
|
|
344
|
+
'preview': QAction("Brush Preview", self),
|
|
345
|
+
'outline': QAction("Outline Only", self)
|
|
346
|
+
}
|
|
347
|
+
brush_action = self.cursor_style_actions['brush']
|
|
349
348
|
brush_action.setCheckable(True)
|
|
350
|
-
brush_action.
|
|
351
|
-
brush_action.triggered.connect(lambda: self.image_viewer.set_cursor_style('brush'))
|
|
349
|
+
brush_action.triggered.connect(lambda: set_cursor_style('brush'))
|
|
352
350
|
cursor_menu.addAction(brush_action)
|
|
353
351
|
|
|
354
|
-
preview_action =
|
|
352
|
+
preview_action = self.cursor_style_actions['preview']
|
|
355
353
|
preview_action.setCheckable(True)
|
|
356
|
-
preview_action.
|
|
357
|
-
preview_action.triggered.connect(lambda: self.image_viewer.set_cursor_style('preview'))
|
|
354
|
+
preview_action.triggered.connect(lambda: set_cursor_style('preview'))
|
|
358
355
|
cursor_menu.addAction(preview_action)
|
|
359
356
|
|
|
360
|
-
outline_action =
|
|
357
|
+
outline_action = self.cursor_style_actions['outline']
|
|
361
358
|
outline_action.setCheckable(True)
|
|
362
|
-
outline_action.
|
|
363
|
-
outline_action.triggered.connect(lambda: self.image_viewer.set_cursor_style('outline'))
|
|
359
|
+
outline_action.triggered.connect(lambda: set_cursor_style('outline'))
|
|
364
360
|
cursor_menu.addAction(outline_action)
|
|
365
361
|
|
|
362
|
+
def set_cursor_style(cursor_style):
|
|
363
|
+
self.image_viewer.set_cursor_style(cursor_style)
|
|
364
|
+
for label, style in self.cursor_style_actions.items():
|
|
365
|
+
style.setEnabled(label != cursor_style)
|
|
366
|
+
style.setChecked(label == cursor_style)
|
|
367
|
+
|
|
368
|
+
set_cursor_style(self.image_viewer.get_cursor_style())
|
|
369
|
+
|
|
366
370
|
cursor_group = QActionGroup(self)
|
|
367
371
|
cursor_group.addAction(preview_action)
|
|
368
372
|
cursor_group.addAction(outline_action)
|
|
@@ -409,7 +413,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
409
413
|
view_menu.addAction(self.toggle_view_master_individual_action)
|
|
410
414
|
view_menu.addSeparator()
|
|
411
415
|
|
|
412
|
-
set_strategy('overlaid')
|
|
416
|
+
self.set_strategy('overlaid')
|
|
413
417
|
|
|
414
418
|
sort_asc_action = QAction("Sort Layers A-Z", self)
|
|
415
419
|
sort_asc_action.triggered.connect(lambda: self.sort_layers('asc'))
|
|
@@ -432,13 +436,15 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
432
436
|
white_balance_action = QAction("White Balance", self)
|
|
433
437
|
white_balance_action.triggered.connect(self.white_balance)
|
|
434
438
|
filter_menu.addAction(white_balance_action)
|
|
435
|
-
vignetting_action = QAction("Vignetting
|
|
439
|
+
vignetting_action = QAction("Vignetting Correction", self)
|
|
436
440
|
vignetting_action.triggered.connect(self.vignetting_correction)
|
|
437
441
|
filter_menu.addAction(vignetting_action)
|
|
438
442
|
|
|
439
443
|
help_menu = menubar.addMenu("&Help")
|
|
440
444
|
help_menu.setObjectName("Help")
|
|
441
|
-
shortcuts_help_action = QAction("Shortcuts and
|
|
445
|
+
shortcuts_help_action = QAction("Shortcuts and Mouse", self)
|
|
446
|
+
|
|
447
|
+
self.statusBar().showMessage("Shine Stacker ready.", 2000)
|
|
442
448
|
|
|
443
449
|
def shortcuts_help():
|
|
444
450
|
self.shortcuts_help_dialog = ShortcutsHelp(self)
|
|
@@ -453,6 +459,16 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
453
459
|
next_layer.activated.connect(self.next_layer)
|
|
454
460
|
self.installEventFilter(self)
|
|
455
461
|
|
|
462
|
+
def set_strategy(self, strategy):
|
|
463
|
+
self.image_viewer.set_strategy(strategy)
|
|
464
|
+
enable_shortcuts = strategy == 'overlaid'
|
|
465
|
+
self.view_master_action.setEnabled(enable_shortcuts)
|
|
466
|
+
self.view_individual_action.setEnabled(enable_shortcuts)
|
|
467
|
+
self.toggle_view_master_individual_action.setEnabled(enable_shortcuts)
|
|
468
|
+
for label, mode in self.view_mode_actions.items():
|
|
469
|
+
mode.setEnabled(label != strategy)
|
|
470
|
+
mode.setChecked(label == strategy)
|
|
471
|
+
|
|
456
472
|
def update_title(self):
|
|
457
473
|
title = constants.APP_TITLE
|
|
458
474
|
if self.io_gui_handler is not None:
|
|
@@ -693,13 +709,11 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
693
709
|
|
|
694
710
|
def set_view_master(self):
|
|
695
711
|
self.display_manager.set_view_master()
|
|
696
|
-
self.display_manager.refresh_master_view()
|
|
697
712
|
self.thumbnail_highlight = gui_constants.THUMB_MASTER_HI_COLOR
|
|
698
713
|
self.highlight_master_thumbnail()
|
|
699
714
|
|
|
700
715
|
def set_view_individual(self):
|
|
701
716
|
self.display_manager.set_view_individual()
|
|
702
|
-
self.display_manager.refresh_current_view()
|
|
703
717
|
self.thumbnail_highlight = gui_constants.THUMB_MASTER_LO_COLOR
|
|
704
718
|
self.highlight_master_thumbnail()
|
|
705
719
|
|
|
@@ -35,8 +35,14 @@ class ImageViewer(QWidget):
|
|
|
35
35
|
self.strategy.show()
|
|
36
36
|
self.strategy.resize(self.size())
|
|
37
37
|
if not self.strategy.empty():
|
|
38
|
+
self.strategy.cleanup_brush_preview()
|
|
38
39
|
self.strategy.update_master_display()
|
|
39
40
|
self.strategy.update_current_display()
|
|
41
|
+
self.strategy.setup_brush_cursor()
|
|
42
|
+
self.strategy.update_brush_cursor()
|
|
43
|
+
self.strategy.show_master()
|
|
44
|
+
self.strategy.setFocus()
|
|
45
|
+
self.strategy.activateWindow()
|
|
40
46
|
|
|
41
47
|
def empty(self):
|
|
42
48
|
return self.strategy.empty()
|
|
@@ -81,9 +87,6 @@ class ImageViewer(QWidget):
|
|
|
81
87
|
def set_allow_cursor_preview(self, state):
|
|
82
88
|
self.strategy.set_allow_cursor_preview(state)
|
|
83
89
|
|
|
84
|
-
def setup_brush_cursor(self):
|
|
85
|
-
self.strategy.setup_brush_cursor()
|
|
86
|
-
|
|
87
90
|
def zoom_in(self):
|
|
88
91
|
self.strategy.zoom_in()
|
|
89
92
|
|
|
@@ -103,7 +106,8 @@ class ImageViewer(QWidget):
|
|
|
103
106
|
return self.strategy.get_cursor_style()
|
|
104
107
|
|
|
105
108
|
def set_cursor_style(self, style):
|
|
106
|
-
self.
|
|
109
|
+
for st in self._strategies.values():
|
|
110
|
+
st.set_cursor_style(style)
|
|
107
111
|
|
|
108
112
|
def position_on_image(self, pos):
|
|
109
113
|
return self.strategy.position_on_image(pos)
|
|
@@ -56,8 +56,6 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
56
56
|
self.set_layer_labels(labels)
|
|
57
57
|
self.set_master_layer(master_layer)
|
|
58
58
|
self.image_viewer.set_master_image_np(master_layer)
|
|
59
|
-
self.image_viewer.show_master()
|
|
60
|
-
self.image_viewer.update_master_display()
|
|
61
59
|
self.undo_manager.reset()
|
|
62
60
|
self.blank_layer = np.zeros(master_layer.shape[:2])
|
|
63
61
|
self.finish_loading_setup(f"Loaded: {self.current_file_path()}")
|
|
@@ -165,7 +163,6 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
165
163
|
self.display_manager.update_thumbnails()
|
|
166
164
|
self.mark_as_modified_requested.emit(True)
|
|
167
165
|
self.change_layer_requested.emit(0)
|
|
168
|
-
self.image_viewer.setup_brush_cursor()
|
|
169
166
|
self.status_message_requested.emit(message)
|
|
170
167
|
self.update_title_requested.emit()
|
|
171
168
|
self.add_recent_file_requested.emit(self.current_file_path_master)
|
|
@@ -143,6 +143,9 @@ class LayerCollectionHandler:
|
|
|
143
143
|
def has_master_layer(self):
|
|
144
144
|
return self.layer_collection.has_master_layer()
|
|
145
145
|
|
|
146
|
+
def set_layer(self, idx, img):
|
|
147
|
+
self.layer_collection.layer_stack[idx] = img
|
|
148
|
+
|
|
146
149
|
def set_layer_stack(self, stk):
|
|
147
150
|
self.layer_collection.set_layer_stack(stk)
|
|
148
151
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, E1101, R0904, R0912, R0914, R0902
|
|
2
|
-
from PySide6.QtGui import QPixmap
|
|
3
2
|
from PySide6.QtCore import Qt, QPointF, QEvent, QRectF
|
|
4
3
|
from .. config.gui_constants import gui_constants
|
|
5
4
|
from .view_strategy import ViewStrategy, ImageGraphicsViewBase, ViewSignals
|
|
@@ -23,12 +22,21 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
23
22
|
def get_master_view(self):
|
|
24
23
|
return self
|
|
25
24
|
|
|
25
|
+
def get_current_view(self):
|
|
26
|
+
return self
|
|
27
|
+
|
|
26
28
|
def get_master_scene(self):
|
|
27
29
|
return self.scene
|
|
28
30
|
|
|
31
|
+
def get_current_scene(self):
|
|
32
|
+
return self.scene
|
|
33
|
+
|
|
29
34
|
def get_master_pixmap(self):
|
|
30
35
|
return self.pixmap_item_master
|
|
31
36
|
|
|
37
|
+
def get_current_pixmap(self):
|
|
38
|
+
return self.pixmap_item_current
|
|
39
|
+
|
|
32
40
|
def get_views(self):
|
|
33
41
|
return [self]
|
|
34
42
|
|
|
@@ -41,66 +49,6 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
41
49
|
self.pixmap_item_current: self
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
def set_master_image(self, qimage):
|
|
45
|
-
self.status.set_master_image(qimage)
|
|
46
|
-
pixmap = self.status.pixmap_master
|
|
47
|
-
self.setSceneRect(QRectF(pixmap.rect()))
|
|
48
|
-
|
|
49
|
-
img_width, img_height = pixmap.width(), pixmap.height()
|
|
50
|
-
self.set_min_scale(min(gui_constants.MIN_ZOOMED_IMG_WIDTH / img_width,
|
|
51
|
-
gui_constants.MIN_ZOOMED_IMG_HEIGHT / img_height))
|
|
52
|
-
self.set_max_scale(gui_constants.MAX_ZOOMED_IMG_PX_SIZE)
|
|
53
|
-
self.set_zoom_factor(1.0)
|
|
54
|
-
self.fitInView(self.pixmap_item_master, Qt.KeepAspectRatio)
|
|
55
|
-
self.set_zoom_factor(self.get_current_scale())
|
|
56
|
-
self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), self.zoom_factor())))
|
|
57
|
-
self.scale(self.zoom_factor(), self.zoom_factor())
|
|
58
|
-
|
|
59
|
-
def set_current_image(self, qimage):
|
|
60
|
-
self.status.set_current_image(qimage)
|
|
61
|
-
if self.empty():
|
|
62
|
-
self.setSceneRect(QRectF(self.status.pixmap_current.rect()))
|
|
63
|
-
|
|
64
|
-
def show_master(self):
|
|
65
|
-
self.pixmap_item_master.setVisible(True)
|
|
66
|
-
self.pixmap_item_current.setVisible(False)
|
|
67
|
-
|
|
68
|
-
def show_current(self):
|
|
69
|
-
self.pixmap_item_master.setVisible(False)
|
|
70
|
-
self.pixmap_item_current.setVisible(True)
|
|
71
|
-
|
|
72
|
-
def update_master_display(self):
|
|
73
|
-
if not self.empty():
|
|
74
|
-
master_qimage = self.numpy_to_qimage(
|
|
75
|
-
self.master_layer())
|
|
76
|
-
if master_qimage:
|
|
77
|
-
self.pixmap_item_master.setPixmap(QPixmap.fromImage(master_qimage))
|
|
78
|
-
|
|
79
|
-
def update_current_display(self):
|
|
80
|
-
if not self.empty() and self.number_of_layers() > 0:
|
|
81
|
-
current_qimage = self.numpy_to_qimage(
|
|
82
|
-
self.current_layer())
|
|
83
|
-
if current_qimage:
|
|
84
|
-
self.pixmap_item_current.setPixmap(QPixmap.fromImage(current_qimage))
|
|
85
|
-
|
|
86
|
-
def set_view_state(self, state):
|
|
87
|
-
self.status.set_state(state)
|
|
88
|
-
if state:
|
|
89
|
-
self.resetTransform()
|
|
90
|
-
self.scale(state['zoom'], state['zoom'])
|
|
91
|
-
self.horizontalScrollBar().setValue(state['h_scroll'])
|
|
92
|
-
self.verticalScrollBar().setValue(state['v_scroll'])
|
|
93
|
-
self.set_zoom_factor(state['zoom'])
|
|
94
|
-
|
|
95
|
-
def handle_key_press_event(self, event):
|
|
96
|
-
if event.key() == Qt.Key_X:
|
|
97
|
-
self.temp_view_requested.emit(True)
|
|
98
|
-
self.update_brush_cursor()
|
|
99
|
-
|
|
100
|
-
def handle_key_release_event(self, event):
|
|
101
|
-
if event.key() == Qt.Key_X:
|
|
102
|
-
self.temp_view_requested.emit(False)
|
|
103
|
-
|
|
104
52
|
# pylint: disable=C0103
|
|
105
53
|
def mousePressEvent(self, event):
|
|
106
54
|
self.mouse_press_event(event)
|
|
@@ -113,6 +61,7 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
113
61
|
self.mouse_release_event(event)
|
|
114
62
|
super().mouseReleaseEvent(event)
|
|
115
63
|
|
|
64
|
+
# pylint: enable=R0801
|
|
116
65
|
def wheelEvent(self, event):
|
|
117
66
|
if self.empty() or self.gesture_active:
|
|
118
67
|
return
|
|
@@ -120,8 +69,8 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
120
69
|
if self.control_pressed:
|
|
121
70
|
self.brush_size_change_requested.emit(1 if event.angleDelta().y() > 0 else -1)
|
|
122
71
|
else:
|
|
123
|
-
zoom_in_factor =
|
|
124
|
-
zoom_out_factor =
|
|
72
|
+
zoom_in_factor = gui_constants.ZOOM_IN_FACTOR
|
|
73
|
+
zoom_out_factor = gui_constants.ZOOM_OUT_FACTOR
|
|
125
74
|
current_scale = self.get_current_scale()
|
|
126
75
|
if event.angleDelta().y() > 0: # Zoom in
|
|
127
76
|
new_scale = current_scale * zoom_in_factor
|
|
@@ -146,11 +95,14 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
146
95
|
else:
|
|
147
96
|
self.zoom_out()
|
|
148
97
|
event.accept()
|
|
98
|
+
# pylint: disable=R0801
|
|
149
99
|
|
|
150
100
|
def enterEvent(self, event):
|
|
151
101
|
self.activateWindow()
|
|
152
102
|
self.setFocus()
|
|
153
|
-
if
|
|
103
|
+
if self.empty():
|
|
104
|
+
self.setCursor(Qt.ArrowCursor)
|
|
105
|
+
else:
|
|
154
106
|
self.setCursor(Qt.BlankCursor)
|
|
155
107
|
if self.brush_cursor:
|
|
156
108
|
self.brush_cursor.show()
|
|
@@ -162,6 +114,89 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
162
114
|
return self.handle_gesture_event(event)
|
|
163
115
|
return super().event(event)
|
|
164
116
|
|
|
117
|
+
def setup_scene_image(self, pixmap, pixmap_item):
|
|
118
|
+
self.setSceneRect(QRectF(pixmap.rect()))
|
|
119
|
+
img_width, img_height = pixmap.width(), pixmap.height()
|
|
120
|
+
self.set_max_min_scales(img_width, img_height)
|
|
121
|
+
view_rect = self.viewport().rect()
|
|
122
|
+
scale_x = view_rect.width() / img_width
|
|
123
|
+
scale_y = view_rect.height() / img_height
|
|
124
|
+
scale_factor = min(scale_x, scale_y)
|
|
125
|
+
scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
|
|
126
|
+
self.set_zoom_factor(scale_factor)
|
|
127
|
+
self.resetTransform()
|
|
128
|
+
self.scale(scale_factor, scale_factor)
|
|
129
|
+
self.centerOn(pixmap_item)
|
|
130
|
+
self.center_image(self)
|
|
131
|
+
self.update_cursor_pen_width()
|
|
132
|
+
|
|
133
|
+
def set_master_image(self, qimage):
|
|
134
|
+
self.status.set_master_image(qimage)
|
|
135
|
+
self.setup_scene_image(self.status.pixmap_master, self.pixmap_item_master)
|
|
136
|
+
self.update_master_display()
|
|
137
|
+
|
|
138
|
+
def set_current_image(self, qimage):
|
|
139
|
+
self.status.set_current_image(qimage)
|
|
140
|
+
if self.empty():
|
|
141
|
+
self.setup_scene_image(self.status.pixmap_current, self.pixmap_item_current)
|
|
142
|
+
|
|
143
|
+
def setup_brush_cursor(self):
|
|
144
|
+
super().setup_brush_cursor()
|
|
145
|
+
self.update_cursor_pen_width()
|
|
146
|
+
|
|
147
|
+
def show_master(self):
|
|
148
|
+
self.pixmap_item_master.setVisible(True)
|
|
149
|
+
self.pixmap_item_current.setVisible(False)
|
|
150
|
+
self.brush_preview.show()
|
|
151
|
+
if self.brush_cursor:
|
|
152
|
+
self.scene.removeItem(self.brush_cursor)
|
|
153
|
+
self.brush_cursor = self.create_circle(self.scene)
|
|
154
|
+
self.update_brush_cursor()
|
|
155
|
+
|
|
156
|
+
def show_current(self):
|
|
157
|
+
self.pixmap_item_master.setVisible(False)
|
|
158
|
+
self.pixmap_item_current.setVisible(True)
|
|
159
|
+
self.brush_preview.hide()
|
|
160
|
+
if self.brush_cursor:
|
|
161
|
+
self.scene.removeItem(self.brush_cursor)
|
|
162
|
+
self.brush_cursor = self.create_alt_circle(self.scene)
|
|
163
|
+
self.update_brush_cursor()
|
|
164
|
+
|
|
165
|
+
def arrange_images(self):
|
|
166
|
+
if self.empty():
|
|
167
|
+
return
|
|
168
|
+
if self.pixmap_item_master.isVisible():
|
|
169
|
+
pixmap = self.pixmap_item_master.pixmap()
|
|
170
|
+
if not pixmap.isNull():
|
|
171
|
+
self.setSceneRect(QRectF(pixmap.rect()))
|
|
172
|
+
self.centerOn(self.pixmap_item_master)
|
|
173
|
+
self.center_image(self)
|
|
174
|
+
elif self.pixmap_item_current.isVisible():
|
|
175
|
+
pixmap = self.pixmap_item_current.pixmap()
|
|
176
|
+
if not pixmap.isNull():
|
|
177
|
+
self.setSceneRect(QRectF(pixmap.rect()))
|
|
178
|
+
self.centerOn(self.pixmap_item_current)
|
|
179
|
+
self.center_image(self)
|
|
180
|
+
current_scale = self.get_current_scale()
|
|
181
|
+
scale_factor = self.zoom_factor() / current_scale
|
|
182
|
+
self.scale(scale_factor, scale_factor)
|
|
183
|
+
|
|
184
|
+
def handle_key_press_event(self, event):
|
|
185
|
+
if event.key() in [Qt.Key_Up, Qt.Key_Down]:
|
|
186
|
+
return False
|
|
187
|
+
if event.key() == Qt.Key_X:
|
|
188
|
+
self.temp_view_requested.emit(True)
|
|
189
|
+
return False
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
def handle_key_release_event(self, event):
|
|
193
|
+
if event.key() in [Qt.Key_Up, Qt.Key_Down]:
|
|
194
|
+
return False
|
|
195
|
+
if event.key() == Qt.Key_X:
|
|
196
|
+
self.temp_view_requested.emit(False)
|
|
197
|
+
return False
|
|
198
|
+
return True
|
|
199
|
+
|
|
165
200
|
def handle_gesture_event(self, event):
|
|
166
201
|
if self.empty():
|
|
167
202
|
return False
|
|
@@ -190,23 +225,3 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
190
225
|
self.scroll_view(self, int(scaled_delta.x()), int(scaled_delta.y()))
|
|
191
226
|
elif pan_gesture.state() == Qt.GestureFinished:
|
|
192
227
|
self.gesture_active = False
|
|
193
|
-
|
|
194
|
-
def handle_pinch_gesture(self, pinch):
|
|
195
|
-
if pinch.state() == Qt.GestureStarted:
|
|
196
|
-
self.pinch_start_scale = self.get_current_scale()
|
|
197
|
-
self.pinch_center_view = pinch.centerPoint()
|
|
198
|
-
self.pinch_center_scene = self.mapToScene(self.pinch_center_view.toPoint())
|
|
199
|
-
self.gesture_active = True
|
|
200
|
-
elif pinch.state() == Qt.GestureUpdated:
|
|
201
|
-
new_scale = self.pinch_start_scale * pinch.totalScaleFactor()
|
|
202
|
-
new_scale = max(self.min_scale(), min(new_scale, self.max_scale()))
|
|
203
|
-
if abs(new_scale - self.get_current_scale()) > 0.01:
|
|
204
|
-
self.resetTransform()
|
|
205
|
-
self.scale(new_scale, new_scale)
|
|
206
|
-
self.set_zoom_factor(new_scale)
|
|
207
|
-
new_center = self.mapToScene(self.pinch_center_view.toPoint())
|
|
208
|
-
delta = self.pinch_center_scene - new_center
|
|
209
|
-
self.translate(delta.x(), delta.y())
|
|
210
|
-
self.update_brush_cursor()
|
|
211
|
-
elif pinch.state() in (Qt.GestureFinished, Qt.GestureCanceled):
|
|
212
|
-
self.gesture_active = False
|