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.

@@ -1,6 +1,5 @@
1
1
  # pylint: disable=C0114, C0115, C0116, E0611, R0902, R0914, R0915, R0904, W0108
2
2
  from functools import partial
3
- import numpy as np
4
3
  from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QFrame, QLabel, QMenu,
5
4
  QListWidget, QSlider, QMainWindow, QMessageBox)
6
5
  from PySide6.QtGui import QShortcut, QKeySequence, QAction, QActionGroup
@@ -31,9 +30,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
31
30
  QMainWindow.__init__(self)
32
31
  LayerCollectionHandler.__init__(self, LayerCollection())
33
32
  self._recent_file_manager = RecentFileManager("shinestacker-recent-images-files.txt")
34
- self.thumbnail_highlight = gui_constants.THUMB_MASTER_HI_COLOR
35
33
  self.io_gui_handler = None
36
- self.display_manager = None
37
34
  self.brush = Brush()
38
35
  self.brush_tool = BrushTool()
39
36
  self.modified = False
@@ -43,13 +40,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
43
40
  self.undo_action = None
44
41
  self.redo_action = None
45
42
  self.undo_manager.stack_changed.connect(self.update_undo_redo_actions)
46
- self.filter_manager = FilterManager(self)
47
- self.filter_manager.register_filter("Denoise", DenoiseFilter)
48
- self.filter_manager.register_filter("Unsharp Mask", UnsharpMaskFilter)
49
- self.filter_manager.register_filter("White Balance", WhiteBalanceFilter)
50
- self.filter_manager.register_filter("Vignetting Correction", VignettingFilter)
51
43
  self.shortcuts_help_dialog = None
52
-
53
44
  self.update_title()
54
45
  self.resize(1400, 900)
55
46
  center = QGuiApplication.primaryScreen().geometry().center()
@@ -68,18 +59,15 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
68
59
  side_layout = QVBoxLayout(side_panel)
69
60
  side_layout.setContentsMargins(0, 0, 0, 0)
70
61
  side_layout.setSpacing(2)
71
-
72
62
  brush_panel = QFrame()
73
63
  brush_panel.setFrameShape(QFrame.StyledPanel)
74
64
  brush_panel.setContentsMargins(0, 0, 0, 0)
75
65
  brush_layout = QVBoxLayout(brush_panel)
76
66
  brush_layout.setContentsMargins(0, 0, 0, 0)
77
67
  brush_layout.setSpacing(2)
78
-
79
68
  brush_label = QLabel("Brush Size")
80
69
  brush_label.setAlignment(Qt.AlignCenter)
81
70
  brush_layout.addWidget(brush_label)
82
-
83
71
  self.brush_size_slider = QSlider(Qt.Horizontal)
84
72
  self.brush_size_slider.setRange(0, gui_constants.BRUSH_SIZE_SLIDER_MAX)
85
73
 
@@ -94,7 +82,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
94
82
 
95
83
  self.brush_size_slider.setValue(brush_size_to_slider(self.brush.size))
96
84
  brush_layout.addWidget(self.brush_size_slider)
97
-
98
85
  hardness_label = QLabel("Brush Hardness")
99
86
  hardness_label.setAlignment(Qt.AlignCenter)
100
87
  brush_layout.addWidget(hardness_label)
@@ -102,7 +89,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
102
89
  self.hardness_slider.setRange(0, 100)
103
90
  self.hardness_slider.setValue(self.brush.hardness)
104
91
  brush_layout.addWidget(self.hardness_slider)
105
-
106
92
  opacity_label = QLabel("Brush Opacity")
107
93
  opacity_label.setAlignment(Qt.AlignCenter)
108
94
  brush_layout.addWidget(opacity_label)
@@ -110,7 +96,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
110
96
  self.opacity_slider.setRange(0, 100)
111
97
  self.opacity_slider.setValue(self.brush.opacity)
112
98
  brush_layout.addWidget(self.opacity_slider)
113
-
114
99
  flow_label = QLabel("Brush Flow")
115
100
  flow_label.setAlignment(Qt.AlignCenter)
116
101
  brush_layout.addWidget(flow_label)
@@ -118,7 +103,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
118
103
  self.flow_slider.setRange(1, 100)
119
104
  self.flow_slider.setValue(self.brush.flow)
120
105
  brush_layout.addWidget(self.flow_slider)
121
-
122
106
  side_layout.addWidget(brush_panel)
123
107
  self.brush_preview_widget = QLabel()
124
108
  self.brush_preview_widget.setContentsMargins(0, 0, 0, 0)
@@ -135,7 +119,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
135
119
  self.brush_preview_widget.setFixedHeight(100)
136
120
  brush_layout.addWidget(self.brush_preview_widget)
137
121
  side_layout.addWidget(brush_panel)
138
-
139
122
  master_label = QLabel("Master")
140
123
  master_label.setStyleSheet("""
141
124
  QLabel {
@@ -153,7 +136,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
153
136
  self.master_thumbnail_frame = QFrame()
154
137
  self.master_thumbnail_frame.setObjectName("thumbnailContainer")
155
138
  self.master_thumbnail_frame.setStyleSheet(
156
- f"#thumbnailContainer{{ border: 2px solid {self.thumbnail_highlight}; }}")
139
+ f"#thumbnailContainer{{ border: 2px solid {gui_constants.THUMB_MASTER_HI_COLOR}; }}")
157
140
  self.master_thumbnail_frame.setFrameShape(QFrame.StyledPanel)
158
141
  master_thumbnail_layout = QVBoxLayout(self.master_thumbnail_frame)
159
142
  master_thumbnail_layout.setContentsMargins(8, 8, 8, 8)
@@ -238,6 +221,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
238
221
  self.display_manager = DisplayManager(
239
222
  self.layer_collection, self.image_viewer,
240
223
  self.master_thumbnail_label, self.thumbnail_list, parent=self)
224
+ self.filter_manager = FilterManager(self)
241
225
  self.io_gui_handler = IOGuiHandler(self.layer_collection, self.undo_manager, parent=self)
242
226
  self.display_manager.status_message_requested.connect(self.show_status_message)
243
227
  self.io_gui_handler.status_message_requested.connect(self.show_status_message)
@@ -252,8 +236,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
252
236
  self.image_viewer.set_preview_brush(self.brush_tool.brush)
253
237
  self.brush_tool.update_brush_thumb()
254
238
  self.io_gui_handler.setup_ui(self.display_manager, self.image_viewer)
255
- self.image_viewer.set_display_manager(self.display_manager)
256
-
257
239
  menubar = self.menuBar()
258
240
  file_menu = menubar.addMenu("&File")
259
241
  file_menu.addAction("&Open...", self.io_gui_handler.open_file, "Ctrl+O")
@@ -322,7 +304,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
322
304
 
323
305
  view_menu.addSeparator()
324
306
 
325
- view_strategy_menu = QMenu("View &Mode", view_menu)
307
+ self.view_strategy_menu = QMenu("View &Mode", view_menu)
326
308
 
327
309
  self.view_mode_actions = {
328
310
  'overlaid': QAction("Overlaid", self),
@@ -332,29 +314,33 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
332
314
  overlaid_mode = self.view_mode_actions['overlaid']
333
315
  overlaid_mode.setShortcut("Ctrl+1")
334
316
  overlaid_mode.setCheckable(True)
335
- overlaid_mode.triggered.connect(lambda: set_strategy('overlaid'))
336
- view_strategy_menu.addAction(overlaid_mode)
317
+ overlaid_mode.triggered.connect(lambda: self.set_strategy('overlaid'))
318
+ self.view_strategy_menu.addAction(overlaid_mode)
337
319
  side_by_side_mode = self.view_mode_actions['sidebyside']
338
320
  side_by_side_mode.setShortcut("Ctrl+2")
339
321
  side_by_side_mode.setCheckable(True)
340
- side_by_side_mode.triggered.connect(lambda: set_strategy('sidebyside'))
341
- view_strategy_menu.addAction(side_by_side_mode)
322
+ side_by_side_mode.triggered.connect(lambda: self.set_strategy('sidebyside'))
323
+ self.view_strategy_menu.addAction(side_by_side_mode)
342
324
  side_by_side_mode = self.view_mode_actions['topbottom']
343
325
  side_by_side_mode.setShortcut("Ctrl+3")
344
326
  side_by_side_mode.setCheckable(True)
345
- side_by_side_mode.triggered.connect(lambda: set_strategy('topbottom'))
346
- view_strategy_menu.addAction(side_by_side_mode)
347
- view_menu.addMenu(view_strategy_menu)
348
-
349
- def set_strategy(strategy):
350
- self.image_viewer.set_strategy(strategy)
351
- enable_shortcuts = strategy == 'overlaid'
352
- self.view_master_action.setEnabled(enable_shortcuts)
353
- self.view_individual_action.setEnabled(enable_shortcuts)
354
- self.toggle_view_master_individual_action.setEnabled(enable_shortcuts)
355
- for label, mode in self.view_mode_actions.items():
356
- mode.setEnabled(label != strategy)
357
- mode.setChecked(label == strategy)
327
+ side_by_side_mode.triggered.connect(lambda: self.set_strategy('topbottom'))
328
+ self.view_strategy_menu.addAction(side_by_side_mode)
329
+ view_menu.addMenu(self.view_strategy_menu)
330
+
331
+ filter_handles = (
332
+ self.display_manager.update_master_thumbnail,
333
+ self.mark_as_modified,
334
+ self.view_strategy_menu.setEnabled
335
+ )
336
+ self.filter_manager.register_filter(
337
+ "Denoise", DenoiseFilter, *filter_handles)
338
+ self.filter_manager.register_filter(
339
+ "Unsharp Mask", UnsharpMaskFilter, *filter_handles)
340
+ self.filter_manager.register_filter(
341
+ "White Balance", WhiteBalanceFilter, *filter_handles)
342
+ self.filter_manager.register_filter(
343
+ "Vignetting Correction", VignettingFilter, *filter_handles)
358
344
 
359
345
  cursor_menu = view_menu.addMenu("Cursor Style")
360
346
 
@@ -432,7 +418,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
432
418
  view_menu.addAction(self.toggle_view_master_individual_action)
433
419
  view_menu.addSeparator()
434
420
 
435
- set_strategy('overlaid')
421
+ self.set_strategy('overlaid')
436
422
 
437
423
  sort_asc_action = QAction("Sort Layers A-Z", self)
438
424
  sort_asc_action.triggered.connect(lambda: self.sort_layers('asc'))
@@ -463,6 +449,8 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
463
449
  help_menu.setObjectName("Help")
464
450
  shortcuts_help_action = QAction("Shortcuts and Mouse", self)
465
451
 
452
+ self.statusBar().showMessage("Shine Stacker ready.", 2000)
453
+
466
454
  def shortcuts_help():
467
455
  self.shortcuts_help_dialog = ShortcutsHelp(self)
468
456
  self.shortcuts_help_dialog.exec()
@@ -476,6 +464,18 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
476
464
  next_layer.activated.connect(self.next_layer)
477
465
  self.installEventFilter(self)
478
466
 
467
+ def set_strategy(self, strategy):
468
+ self.image_viewer.set_strategy(strategy)
469
+ enable_shortcuts = strategy == 'overlaid'
470
+ self.display_manager.view_mode = 'master'
471
+ self.highlight_master_thumbnail(gui_constants.THUMB_MASTER_HI_COLOR)
472
+ self.view_master_action.setEnabled(enable_shortcuts)
473
+ self.view_individual_action.setEnabled(enable_shortcuts)
474
+ self.toggle_view_master_individual_action.setEnabled(enable_shortcuts)
475
+ for label, mode in self.view_mode_actions.items():
476
+ mode.setEnabled(label != strategy)
477
+ mode.setChecked(label == strategy)
478
+
479
479
  def update_title(self):
480
480
  title = constants.APP_TITLE
481
481
  if self.io_gui_handler is not None:
@@ -591,7 +591,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
591
591
 
592
592
  def copy_brush_area_to_master(self, view_pos):
593
593
  if self.layer_stack() is None or self.number_of_layers() == 0 \
594
- or not self.display_manager.allow_cursor_preview():
594
+ or self.display_manager.view_mode != 'master':
595
595
  return
596
596
  area = self.brush_tool.apply_brush_operation(
597
597
  self.master_layer_copy(),
@@ -601,7 +601,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
601
601
  self.undo_manager.extend_undo_area(*area)
602
602
 
603
603
  def begin_copy_brush_area(self, pos):
604
- if self.display_manager.allow_cursor_preview():
604
+ if self.display_manager.view_mode == 'master':
605
605
  self.mask_layer = self.io_gui_handler.blank_layer.copy()
606
606
  self.copy_master_layer()
607
607
  self.undo_manager.reset_undo_area()
@@ -612,7 +612,7 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
612
612
  self.mark_as_modified()
613
613
 
614
614
  def continue_copy_brush_area(self, pos):
615
- if self.display_manager.allow_cursor_preview():
615
+ if self.display_manager.view_mode == 'master':
616
616
  self.copy_brush_area_to_master(pos)
617
617
  self.display_manager.needs_update = True
618
618
  if not self.display_manager.update_timer.isActive():
@@ -654,53 +654,9 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
654
654
  def vignetting_correction(self):
655
655
  self.filter_manager.apply("Vignetting Correction")
656
656
 
657
- def connect_preview_toggle(self, preview_check, do_preview, restore_original):
658
- def on_toggled(checked):
659
- if checked:
660
- do_preview()
661
- else:
662
- restore_original()
663
- preview_check.toggled.connect(on_toggled)
664
-
665
- def get_pixel_color_at(self, pos, radius=None):
666
- item_pos = self.image_viewer.position_on_image(pos)
667
- x = int(item_pos.x())
668
- y = int(item_pos.y())
669
- master_layer = self.master_layer()
670
- if (0 <= x < self.master_layer().shape[1]) and \
671
- (0 <= y < self.master_layer().shape[0]):
672
- if radius is None:
673
- radius = int(self.brush.size)
674
- if radius > 0:
675
- y_indices, x_indices = np.ogrid[-radius:radius + 1, -radius:radius + 1]
676
- mask = x_indices**2 + y_indices**2 <= radius**2
677
- x0 = max(0, x - radius)
678
- x1 = min(master_layer.shape[1], x + radius + 1)
679
- y0 = max(0, y - radius)
680
- y1 = min(master_layer.shape[0], y + radius + 1)
681
- mask = mask[radius - (y - y0): radius + (y1 - y),
682
- radius - (x - x0): radius + (x1 - x)]
683
- region = master_layer[y0:y1, x0:x1]
684
- if region.size == 0:
685
- pixel = master_layer[y, x]
686
- else:
687
- if region.ndim == 3:
688
- pixel = [region[:, :, c][mask].mean() for c in range(region.shape[2])]
689
- else:
690
- pixel = region[mask].mean()
691
- else:
692
- pixel = self.master_layer()[y, x]
693
- if np.isscalar(pixel):
694
- pixel = [pixel, pixel, pixel]
695
- pixel = [np.float32(x) for x in pixel]
696
- if master_layer.dtype == np.uint16:
697
- pixel = [x / 256.0 for x in pixel]
698
- return tuple(int(v) for v in pixel)
699
- return (0, 0, 0)
700
-
701
- def highlight_master_thumbnail(self):
657
+ def highlight_master_thumbnail(self, color):
702
658
  self.master_thumbnail_frame.setStyleSheet(
703
- f"#thumbnailContainer{{ border: 2px solid {self.thumbnail_highlight}; }}")
659
+ f"#thumbnailContainer{{ border: 2px solid {color}; }}")
704
660
 
705
661
  def save_actions_set_enabled(self, enabled):
706
662
  self.save_action.setEnabled(enabled)
@@ -716,13 +672,11 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
716
672
 
717
673
  def set_view_master(self):
718
674
  self.display_manager.set_view_master()
719
- self.thumbnail_highlight = gui_constants.THUMB_MASTER_HI_COLOR
720
- self.highlight_master_thumbnail()
675
+ self.highlight_master_thumbnail(gui_constants.THUMB_MASTER_HI_COLOR)
721
676
 
722
677
  def set_view_individual(self):
723
678
  self.display_manager.set_view_individual()
724
- self.thumbnail_highlight = gui_constants.THUMB_MASTER_LO_COLOR
725
- self.highlight_master_thumbnail()
679
+ self.highlight_master_thumbnail(gui_constants.THUMB_MASTER_LO_COLOR)
726
680
 
727
681
  def toggle_view_master_individual(self):
728
682
  if self.display_manager.view_mode == 'master':
@@ -757,12 +711,10 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
757
711
  def handle_temp_view(self, start):
758
712
  if start:
759
713
  self.display_manager.start_temp_view()
760
- self.thumbnail_highlight = gui_constants.THUMB_MASTER_LO_COLOR
761
- self.highlight_master_thumbnail()
714
+ self.highlight_master_thumbnail(gui_constants.THUMB_MASTER_LO_COLOR)
762
715
  else:
763
716
  self.display_manager.end_temp_view()
764
- self.thumbnail_highlight = gui_constants.THUMB_MASTER_HI_COLOR
765
- self.highlight_master_thumbnail()
717
+ self.highlight_master_thumbnail(gui_constants.THUMB_MASTER_HI_COLOR)
766
718
 
767
719
  def handle_brush_size_change(self, delta):
768
720
  if delta > 0:
@@ -20,7 +20,6 @@ class ImageViewer(QWidget):
20
20
  self.strategy = self._strategies['overlaid']
21
21
  self.layout = QVBoxLayout(self)
22
22
  self.layout.setContentsMargins(0, 0, 0, 0)
23
- self.strategy = self._strategies['overlaid']
24
23
  self.layout.addWidget(self.strategy)
25
24
  self.strategy.show()
26
25
 
@@ -35,7 +34,6 @@ class ImageViewer(QWidget):
35
34
  self.strategy.show()
36
35
  self.strategy.resize(self.size())
37
36
  if not self.strategy.empty():
38
- self.strategy.cleanup_brush_preview()
39
37
  self.strategy.update_master_display()
40
38
  self.strategy.update_current_display()
41
39
  self.strategy.setup_brush_cursor()
@@ -50,10 +48,6 @@ class ImageViewer(QWidget):
50
48
  def set_master_image_np(self, img):
51
49
  self.strategy.set_master_image_np(img)
52
50
 
53
- def clear_image(self):
54
- for st in self._strategies.values():
55
- st.clear_image()
56
-
57
51
  def show_master(self):
58
52
  self.strategy.show_master()
59
53
 
@@ -72,24 +66,6 @@ class ImageViewer(QWidget):
72
66
  def refresh_display(self):
73
67
  self.strategy.refresh_display()
74
68
 
75
- def set_brush(self, brush):
76
- for st in self._strategies.values():
77
- st.set_brush(brush)
78
-
79
- def set_preview_brush(self, brush):
80
- for st in self._strategies.values():
81
- st.set_preview_brush(brush)
82
-
83
- def set_display_manager(self, dm):
84
- for st in self._strategies.values():
85
- st.set_display_manager(dm)
86
-
87
- def set_allow_cursor_preview(self, state):
88
- self.strategy.set_allow_cursor_preview(state)
89
-
90
- def setup_brush_cursor(self):
91
- self.strategy.setup_brush_cursor()
92
-
93
69
  def zoom_in(self):
94
70
  self.strategy.zoom_in()
95
71
 
@@ -102,21 +78,49 @@ class ImageViewer(QWidget):
102
78
  def actual_size(self):
103
79
  self.strategy.actual_size()
104
80
 
81
+ def get_brush(self):
82
+ return self.strategy.brush
83
+
105
84
  def get_current_scale(self):
106
85
  return self.strategy.get_current_scale()
107
86
 
108
87
  def get_cursor_style(self):
109
88
  return self.strategy.get_cursor_style()
110
89
 
111
- def set_cursor_style(self, style):
112
- self.strategy.set_cursor_style(style)
113
-
114
90
  def position_on_image(self, pos):
115
91
  return self.strategy.position_on_image(pos)
116
92
 
117
93
  def get_visible_image_portion(self):
118
94
  return self.strategy.get_visible_image_portion()
119
95
 
96
+ def hide_brush_cursor(self):
97
+ self.strategy.hide_brush_cursor()
98
+
99
+ def show_brush_cursor(self):
100
+ self.strategy.show_brush_cursor()
101
+
102
+ def hide_brush_preview(self):
103
+ self.strategy.hide_brush_preview()
104
+
105
+ def show_brush_preview(self):
106
+ self.strategy.show_brush_preview()
107
+
108
+ def clear_image(self):
109
+ for st in self._strategies.values():
110
+ st.clear_image()
111
+
112
+ def set_brush(self, brush):
113
+ for st in self._strategies.values():
114
+ st.set_brush(brush)
115
+
116
+ def set_preview_brush(self, brush):
117
+ for st in self._strategies.values():
118
+ st.set_preview_brush(brush)
119
+
120
+ def set_cursor_style(self, style):
121
+ for st in self._strategies.values():
122
+ st.set_cursor_style(style)
123
+
120
124
  def connect_signals(
121
125
  self, handle_temp_view, begin_copy_brush_area, continue_copy_brush_area,
122
126
  end_copy_brush_area, handle_brush_size_change):
@@ -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)
@@ -1,4 +1,4 @@
1
- # pylint: disable=C0114, C0115, C0116, E0611, E1101, R0904, R0912, R0914, R0902
1
+ # pylint: disable=C0114, C0115, C0116, E0611, E1101, R0904, R0912, R0914, R0902, E0202
2
2
  from PySide6.QtCore import Qt, QPointF, QEvent, QRectF
3
3
  from .. config.gui_constants import gui_constants
4
4
  from .view_strategy import ViewStrategy, ImageGraphicsViewBase, ViewSignals
@@ -107,6 +107,16 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
107
107
  if self.brush_cursor:
108
108
  self.brush_cursor.show()
109
109
  super().enterEvent(event)
110
+
111
+ def get_mouse_callbacks(self):
112
+ return self.mousePressEvent
113
+
114
+ def set_mouse_callbacks(self, callbacks):
115
+ self.mousePressEvent = callbacks
116
+
117
+ def show(self):
118
+ self.show_master()
119
+ super().show()
110
120
  # pylint: enable=C0103
111
121
 
112
122
  def event(self, event):
@@ -114,27 +124,31 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
114
124
  return self.handle_gesture_event(event)
115
125
  return super().event(event)
116
126
 
117
- def set_master_image(self, qimage):
118
- self.status.set_master_image(qimage)
119
- pixmap = self.status.pixmap_master
127
+ def setup_scene_image(self, pixmap, pixmap_item):
120
128
  self.setSceneRect(QRectF(pixmap.rect()))
121
129
  img_width, img_height = pixmap.width(), pixmap.height()
122
130
  self.set_max_min_scales(img_width, img_height)
123
- self.set_zoom_factor(1.0)
131
+ view_rect = self.viewport().rect()
132
+ scale_x = view_rect.width() / img_width
133
+ scale_y = view_rect.height() / img_height
134
+ scale_factor = min(scale_x, scale_y)
135
+ scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
136
+ self.set_zoom_factor(scale_factor)
124
137
  self.resetTransform()
125
- self.fitInView(self.pixmap_item_master, Qt.KeepAspectRatio)
126
- self.set_zoom_factor(self.get_current_scale())
127
- self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), self.zoom_factor())))
128
- self.scale(self.zoom_factor(), self.zoom_factor())
129
- self.centerOn(self.pixmap_item_master)
138
+ self.scale(scale_factor, scale_factor)
139
+ self.centerOn(pixmap_item)
130
140
  self.center_image(self)
131
141
  self.update_cursor_pen_width()
132
142
 
143
+ def set_master_image(self, qimage):
144
+ self.status.set_master_image(qimage)
145
+ self.setup_scene_image(self.status.pixmap_master, self.pixmap_item_master)
146
+ self.update_master_display()
147
+
133
148
  def set_current_image(self, qimage):
134
149
  self.status.set_current_image(qimage)
135
150
  if self.empty():
136
- self.setSceneRect(QRectF(self.status.pixmap_current.rect()))
137
- self.update_cursor_pen_width()
151
+ self.setup_scene_image(self.status.pixmap_current, self.pixmap_item_current)
138
152
 
139
153
  def setup_brush_cursor(self):
140
154
  super().setup_brush_cursor()
@@ -143,12 +157,20 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
143
157
  def show_master(self):
144
158
  self.pixmap_item_master.setVisible(True)
145
159
  self.pixmap_item_current.setVisible(False)
146
- self.brush_preview.show()
160
+ self.show_brush_preview()
161
+ if self.brush_cursor:
162
+ self.scene.removeItem(self.brush_cursor)
163
+ self.brush_cursor = self.create_circle(self.scene)
164
+ self.update_brush_cursor()
147
165
 
148
166
  def show_current(self):
149
167
  self.pixmap_item_master.setVisible(False)
150
168
  self.pixmap_item_current.setVisible(True)
151
- self.brush_preview.hide()
169
+ self.hide_brush_preview()
170
+ if self.brush_cursor:
171
+ self.scene.removeItem(self.brush_cursor)
172
+ self.brush_cursor = self.create_alt_circle(self.scene)
173
+ self.update_brush_cursor()
152
174
 
153
175
  def arrange_images(self):
154
176
  if self.empty():
@@ -199,8 +221,7 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
199
221
  handled = True
200
222
  if handled:
201
223
  event.accept()
202
- return True
203
- return False
224
+ return handled
204
225
 
205
226
  def handle_pan_gesture(self, pan_gesture):
206
227
  if pan_gesture.state() == Qt.GestureStarted:
@@ -209,7 +230,7 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
209
230
  elif pan_gesture.state() == Qt.GestureUpdated:
210
231
  delta = pan_gesture.delta() - self.last_scroll_pos
211
232
  self.last_scroll_pos = pan_gesture.delta()
212
- scaled_delta = delta * (1.0 / self.get_current_scale())
233
+ scaled_delta = delta / self.get_current_scale()
213
234
  self.scroll_view(self, int(scaled_delta.x()), int(scaled_delta.y()))
214
235
  elif pan_gesture.state() == Qt.GestureFinished:
215
236
  self.gesture_active = False
@@ -39,7 +39,7 @@ class ShortcutsHelp(QDialog):
39
39
  ok_button.clicked.connect(self.accept)
40
40
 
41
41
  def add_bold_label(self, layout, label):
42
- label = QLabel(label)
42
+ label = QLabel(f"{label}:")
43
43
  label.setStyleSheet("font-weight: bold")
44
44
  layout.addRow(label)
45
45
 
@@ -47,21 +47,21 @@ class ShortcutsHelp(QDialog):
47
47
  self.main_layout.insertWidget(0, icon_container())
48
48
 
49
49
  shortcuts = {
50
- "M": "show master layer",
51
- "L": "show selected layer",
52
- "T": "toggle master/selected layer",
53
- "X": "temp. toggle between master and source layer",
54
- "↑": "select one layer up",
55
- "↓": "selcet one layer down",
56
- "Ctrl + O": "open file",
57
- "Ctrl + S": "save multilayer tiff",
58
- "Crtl + Z": "undo brush draw",
59
- "Ctrl + M": "copy selected layer to master",
60
- "Ctrl + Cmd + F": "full screen mode",
61
- "Ctrl + +": "zoom in",
62
- "Ctrl + -": "zoom out",
63
- "Ctrl + 0": "adapt to screen",
64
- "Ctrl + R": "actual size"
50
+ "M": "Show master layer",
51
+ "L": "Show selected layer",
52
+ "T": "Toggle master/selected layer",
53
+ "X": "Temporarily toggle between master and source layer",
54
+ "↑": "Select one layer up",
55
+ "↓": "Select one layer down",
56
+ "Ctrl + O": "Open file",
57
+ "Ctrl + S": "Save multilayer tiff",
58
+ "Ctrl + Z": "Undo brush draw",
59
+ "Ctrl + M": "Copy selected layer to master",
60
+ "Ctrl + Cmd + F": "Full screen mode",
61
+ "Ctrl + +": "Zoom in",
62
+ "Ctrl + -": "Zoom out",
63
+ "Ctrl + 0": "Fit to screen",
64
+ "Ctrl + R": "Actual size"
65
65
  }
66
66
 
67
67
  self.add_bold_label(left_layout, "Keyboard Shortcuts")
@@ -69,13 +69,13 @@ class ShortcutsHelp(QDialog):
69
69
  left_layout.addRow(f"<b>{k}</b>", QLabel(v))
70
70
 
71
71
  shortcuts = {
72
- "Ctrl + 1": "view mode: overlaid",
73
- "Ctrl + 2": "view mode: side by side",
74
- "Ctrl + 3": "view mode: top-bottom",
75
- "[": "increase brush size",
76
- "]": "decrease brush size",
77
- "{": "increase brush hardness",
78
- "}": "decrease brush hardness"
72
+ "Ctrl + 1": "View: overlaid",
73
+ "Ctrl + 2": "View: side by side",
74
+ "Ctrl + 3": "View: top-bottom",
75
+ "[": "Increase brush size",
76
+ "]": "Decrease brush size",
77
+ "{": "Increase brush hardness",
78
+ "}": "Decrease brush hardness"
79
79
  }
80
80
 
81
81
  self.add_bold_label(right_layout, "Keyboard Shortcuts")
@@ -83,22 +83,26 @@ class ShortcutsHelp(QDialog):
83
83
  right_layout.addRow(f"<b>{k}</b>", QLabel(v))
84
84
 
85
85
  mouse_controls = {
86
- "Space + Drag": "pan",
87
- "Wheel": "zoom in/out",
88
- "Ctrl + Wheel": "adjust brush size",
89
- "Left Click": "brush action",
86
+ "Space + Drag": "Move",
87
+ "Wheel": "Zoom in/out",
88
+ "Ctrl + Wheel": "Adjust brush size",
89
+ "Left Click": "Use brush to copy from selected layer to master",
90
90
  }
91
91
 
92
+ spacer = QLabel("")
93
+ spacer.setFixedHeight(10)
94
+ right_layout.addWidget(spacer)
92
95
  self.add_bold_label(right_layout, "Mouse Controls")
93
96
  for k, v in mouse_controls.items():
94
97
  right_layout.addRow(f"<b>{k}</b>", QLabel(v))
95
98
 
96
99
  touchpad_controls = {
97
- "Two fingers": "pan",
98
- "Pinch": "zoom in/out",
99
- "Ctrl + two fingers": "zoom in/out",
100
+ "Two-finger drag": "Move",
101
+ "Pinch two fingers": "Zoom in/out"
100
102
  }
101
- self.add_bold_label(right_layout, " ")
103
+ spacer = QLabel("")
104
+ spacer.setFixedHeight(10)
105
+ right_layout.addWidget(spacer)
102
106
  self.add_bold_label(right_layout, "Touchpad Controls")
103
107
  for k, v in touchpad_controls.items():
104
108
  right_layout.addRow(f"<b>{k}</b>", QLabel(v))