shinestacker 1.4.0__py3-none-any.whl → 1.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shinestacker might be problematic. Click here for more details.

shinestacker/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '1.4.0'
1
+ __version__ = '1.5.0'
shinestacker/app/main.py CHANGED
@@ -102,7 +102,7 @@ class MainApp(QMainWindow):
102
102
  file_menu = action.menu()
103
103
  break
104
104
  if file_menu is not None:
105
- import_action = QAction("Import From Current Project", self)
105
+ import_action = QAction("Import from Current Project", self)
106
106
  import_action.triggered.connect(self.import_from_project)
107
107
  file_menu.addAction(import_action)
108
108
  else:
@@ -66,6 +66,11 @@ class _GuiConstants:
66
66
  ZOOM_IN_FACTOR = 1.10
67
67
  ZOOM_OUT_FACTOR = 1 / ZOOM_IN_FACTOR
68
68
 
69
+ ROTATE_LABEL = "Rotate"
70
+ ROTATE_90_CW_LABEL = f"{ROTATE_LABEL} 90° Clockwise"
71
+ ROTATE_90_CCW_LABEL = f"{ROTATE_LABEL} 90° Anticlockwise"
72
+ ROTATE_180_LABEL = f"{ROTATE_LABEL} 180°"
73
+
69
74
  def calculate_gamma(self):
70
75
  if self.BRUSH_SIZES['mid'] <= self.BRUSH_SIZES['min'] or self.BRUSH_SIZES['max'] <= 0:
71
76
  return 1.0
@@ -34,7 +34,6 @@ class BaseFilter(ABC):
34
34
  def run_with_preview(self, **kwargs):
35
35
  if self.editor.has_no_master_layer():
36
36
  return
37
-
38
37
  self.editor.copy_master_layer()
39
38
  dlg = QDialog(self.editor)
40
39
  layout = QVBoxLayout(dlg)
@@ -143,15 +142,14 @@ class BaseFilter(ABC):
143
142
  h, w = self.editor.master_layer().shape[:2]
144
143
  except Exception:
145
144
  h, w = self.editor.master_layer_copy().shape[:2]
146
- if hasattr(self.editor, "undo_manager"):
147
- try:
148
- self.editor.undo_manager.extend_undo_area(0, 0, w, h)
149
- self.editor.undo_manager.save_undo_state(
150
- self.editor.master_layer_copy(),
151
- self.name
152
- )
153
- except Exception:
154
- pass
145
+ try:
146
+ self.editor.undo_manager.extend_undo_area(0, 0, w, h)
147
+ self.editor.undo_manager.save_undo_state(
148
+ self.editor.master_layer_copy(),
149
+ self.name
150
+ )
151
+ except Exception:
152
+ pass
155
153
  final_img = self.apply(self.editor.master_layer_copy(), *params)
156
154
  self.editor.set_master_layer(final_img)
157
155
  self.editor.copy_master_layer()
@@ -113,7 +113,6 @@ class BrushPreviewItem(QGraphicsPixmapItem, LayerCollectionHandler):
113
113
  self.setPixmap(final_pixmap)
114
114
  x_start, y_start = max(0, x), max(0, y)
115
115
  self.setPos(x_start, y_start)
116
- self.show()
117
116
  except Exception:
118
117
  traceback.print_exc()
119
118
  self.hide()
@@ -25,7 +25,6 @@ class ClickableLabel(QLabel):
25
25
 
26
26
  class DisplayManager(QObject, LayerCollectionHandler):
27
27
  status_message_requested = Signal(str)
28
- cursor_preview_state_changed = Signal(bool)
29
28
 
30
29
  def __init__(self, layer_collection, image_viewer, master_thumbnail_label,
31
30
  thumbnail_list, parent=None):
@@ -35,7 +34,6 @@ class DisplayManager(QObject, LayerCollectionHandler):
35
34
  self.master_thumbnail_label = master_thumbnail_label
36
35
  self.thumbnail_list = thumbnail_list
37
36
  self.view_mode = 'master'
38
- self.temp_view_individual = False
39
37
  self.needs_update = False
40
38
  self.update_timer = QTimer()
41
39
  self.update_timer.setInterval(gui_constants.PAINT_REFRESH_TIMER)
@@ -47,21 +45,10 @@ class DisplayManager(QObject, LayerCollectionHandler):
47
45
  self.refresh_master_view()
48
46
  self.needs_update = False
49
47
 
50
- def refresh_master_view(self):
51
- if self.has_no_master_layer():
52
- return
53
- self.image_viewer.update_master_display()
54
- self.update_master_thumbnail()
55
- self.image_viewer.refresh_display()
56
-
57
- def refresh_current_view(self):
58
- if self.number_of_layers() == 0:
59
- return
60
- self.image_viewer.update_current_display()
61
- self.image_viewer.refresh_display()
62
-
63
48
  def create_thumbnail(self, layer):
64
49
  source_layer = (layer // 256).astype(np.uint8) if layer.dtype == np.uint16 else layer
50
+ if not source_layer.flags.c_contiguous:
51
+ source_layer = np.ascontiguousarray(source_layer)
65
52
  height, width = source_layer.shape[:2]
66
53
  if layer.ndim == 3 and source_layer.shape[-1] == 3:
67
54
  qimg = QImage(source_layer.data, width, height, 3 * width, QImage.Format_RGB888)
@@ -157,48 +144,61 @@ class DisplayManager(QObject, LayerCollectionHandler):
157
144
  self.thumbnail_list.scrollToItem(
158
145
  self.thumbnail_list.item(index), QAbstractItemView.PositionAtCenter)
159
146
 
147
+ def _master_refresh_and_thumb(self):
148
+ self.image_viewer.show_master()
149
+ self.refresh_master_view()
150
+ self.thumbnail_highlight = gui_constants.THUMB_LO_COLOR
151
+ self.highlight_thumbnail(self.current_layer_idx())
152
+
153
+ def _current_refresh_and_thumb(self):
154
+ self.image_viewer.show_current()
155
+ self.refresh_current_view()
156
+ self.thumbnail_highlight = gui_constants.THUMB_HI_COLOR
157
+ self.highlight_thumbnail(self.current_layer_idx())
158
+
160
159
  def set_view_master(self):
161
160
  if self.has_no_master_layer():
162
161
  return
163
162
  self.view_mode = 'master'
164
- self.temp_view_individual = False
165
- self.refresh_master_view()
166
- self.thumbnail_highlight = gui_constants.THUMB_LO_COLOR
167
- self.highlight_thumbnail(self.current_layer_idx())
163
+ self._master_refresh_and_thumb()
168
164
  self.status_message_requested.emit("View mode: Master")
169
- self.cursor_preview_state_changed.emit(True)
170
165
 
171
166
  def set_view_individual(self):
172
167
  if self.has_no_master_layer():
173
168
  return
174
169
  self.view_mode = 'individual'
175
- self.temp_view_individual = False
176
- self.refresh_current_view()
177
- self.thumbnail_highlight = gui_constants.THUMB_HI_COLOR
178
- self.highlight_thumbnail(self.current_layer_idx())
170
+ self._current_refresh_and_thumb()
179
171
  self.status_message_requested.emit("View mode: Individual layers")
180
- self.cursor_preview_state_changed.emit(False)
172
+
173
+ def refresh_master_view(self):
174
+ if self.has_no_master_layer():
175
+ return
176
+ self.image_viewer.update_master_display()
177
+ self.image_viewer.refresh_display()
178
+ self.update_master_thumbnail()
179
+
180
+ def refresh_current_view(self):
181
+ if self.number_of_layers() == 0:
182
+ return
183
+ self.image_viewer.update_current_display()
184
+ self.image_viewer.refresh_display()
181
185
 
182
186
  def start_temp_view(self):
183
- if not self.temp_view_individual and self.view_mode == 'master':
184
- self.temp_view_individual = True
185
- self.image_viewer.update_brush_cursor()
186
- self.thumbnail_highlight = gui_constants.THUMB_HI_COLOR
187
- self.highlight_thumbnail(self.current_layer_idx())
188
- self.image_viewer.show_current()
189
- self.refresh_current_view()
190
- self.status_message_requested.emit("Temporary view: Individual layer (hold X)")
187
+ if self.view_mode == 'master':
188
+ self._current_refresh_and_thumb()
189
+ self.status_message_requested.emit("Temporary view: Individual layer")
190
+ else:
191
+ self._master_refresh_and_thumb()
192
+ self.image_viewer.strategy.brush_preview.hide()
193
+ self.status_message_requested.emit("Temporary view: Master")
191
194
 
192
195
  def end_temp_view(self):
193
- if self.temp_view_individual:
194
- self.temp_view_individual = False
195
- self.image_viewer.update_brush_cursor()
196
- self.thumbnail_highlight = gui_constants.THUMB_LO_COLOR
197
- self.highlight_thumbnail(self.current_layer_idx())
198
- self.image_viewer.show_master()
199
- self.refresh_master_view()
196
+ if self.view_mode == 'master':
197
+ self._master_refresh_and_thumb()
200
198
  self.status_message_requested.emit("View mode: Master")
201
- self.cursor_preview_state_changed.emit(True)
199
+ else:
200
+ self._current_refresh_and_thumb()
201
+ self.status_message_requested.emit("View: Individual layer")
202
202
 
203
203
  def allow_cursor_preview(self):
204
- return self.view_mode == 'master' and not self.temp_view_individual
204
+ return self.view_mode == 'master'
@@ -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 .. gui.recent_file_manager import RecentFileManager
26
+ from .transformation_manager import TransfromationManager
26
27
 
27
28
 
28
29
  class ImageEditorUI(QMainWindow, LayerCollectionHandler):
@@ -31,16 +32,17 @@ 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)
@@ -238,8 +240,6 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
238
240
  self.master_thumbnail_label, self.thumbnail_list, parent=self)
239
241
  self.io_gui_handler = IOGuiHandler(self.layer_collection, self.undo_manager, parent=self)
240
242
  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
243
  self.io_gui_handler.status_message_requested.connect(self.show_status_message)
244
244
  self.io_gui_handler.update_title_requested.connect(self.update_title)
245
245
  self.io_gui_handler.mark_as_modified_requested.connect(self.mark_as_modified)
@@ -277,8 +277,8 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
277
277
 
278
278
  file_menu.addAction("&Close", self.close_file, "Ctrl+W")
279
279
  file_menu.addSeparator()
280
- file_menu.addAction("&Import frames", self.io_gui_handler.import_frames)
281
- file_menu.addAction("Import &EXIF data", self.io_gui_handler.select_exif_path)
280
+ file_menu.addAction("&Import Frames", self.io_gui_handler.import_frames)
281
+ file_menu.addAction("Import &EXIF Data", self.io_gui_handler.select_exif_path)
282
282
 
283
283
  edit_menu = menubar.addMenu("&Edit")
284
284
  self.undo_action = QAction("Undo", self)
@@ -293,7 +293,21 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
293
293
  edit_menu.addAction(self.redo_action)
294
294
  edit_menu.addSeparator()
295
295
 
296
- copy_action = QAction("Copy Layer to Master", self)
296
+ transf_menu = QMenu("&Transform")
297
+ rotate_90_cw_action = QAction(gui_constants.ROTATE_90_CW_LABEL, self)
298
+ transf_menu.addAction(rotate_90_cw_action)
299
+ rotate_90_cw_action.triggered.connect(lambda: self.transformation_manager.rotate_90_cw())
300
+ rotate_90_ccw_action = QAction(gui_constants.ROTATE_90_CCW_LABEL, self)
301
+ transf_menu.addAction(rotate_90_ccw_action)
302
+ rotate_90_ccw_action.triggered.connect(lambda: self.transformation_manager.rotate_90_ccw())
303
+ rotate_180_action = QAction(gui_constants.ROTATE_180_LABEL, self)
304
+ rotate_180_action.triggered.connect(lambda: self.transformation_manager.rotate_180())
305
+ transf_menu.addAction(rotate_180_action)
306
+ edit_menu.addMenu(transf_menu)
307
+
308
+ edit_menu.addSeparator()
309
+
310
+ copy_action = QAction("Copy Current Layer to Master", self)
297
311
  copy_action.setShortcut("Ctrl+M")
298
312
  copy_action.triggered.connect(self.copy_layer_to_master)
299
313
  edit_menu.addAction(copy_action)
@@ -310,22 +324,22 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
310
324
 
311
325
  view_strategy_menu = QMenu("View &Mode", view_menu)
312
326
 
313
- self.view_action_modes = {
327
+ self.view_mode_actions = {
314
328
  'overlaid': QAction("Overlaid", self),
315
- 'sidebyside': QAction("Side By Side", self),
329
+ 'sidebyside': QAction("Side by Side", self),
316
330
  'topbottom': QAction("Top-Bottom", self)
317
331
  }
318
- overlaid_mode = self.view_action_modes['overlaid']
332
+ overlaid_mode = self.view_mode_actions['overlaid']
319
333
  overlaid_mode.setShortcut("Ctrl+1")
320
334
  overlaid_mode.setCheckable(True)
321
335
  overlaid_mode.triggered.connect(lambda: set_strategy('overlaid'))
322
336
  view_strategy_menu.addAction(overlaid_mode)
323
- side_by_side_mode = self.view_action_modes['sidebyside']
337
+ side_by_side_mode = self.view_mode_actions['sidebyside']
324
338
  side_by_side_mode.setShortcut("Ctrl+2")
325
339
  side_by_side_mode.setCheckable(True)
326
340
  side_by_side_mode.triggered.connect(lambda: set_strategy('sidebyside'))
327
341
  view_strategy_menu.addAction(side_by_side_mode)
328
- side_by_side_mode = self.view_action_modes['topbottom']
342
+ side_by_side_mode = self.view_mode_actions['topbottom']
329
343
  side_by_side_mode.setShortcut("Ctrl+3")
330
344
  side_by_side_mode.setCheckable(True)
331
345
  side_by_side_mode.triggered.connect(lambda: set_strategy('topbottom'))
@@ -338,31 +352,40 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
338
352
  self.view_master_action.setEnabled(enable_shortcuts)
339
353
  self.view_individual_action.setEnabled(enable_shortcuts)
340
354
  self.toggle_view_master_individual_action.setEnabled(enable_shortcuts)
341
- for label, mode in self.view_action_modes.items():
355
+ for label, mode in self.view_mode_actions.items():
342
356
  mode.setEnabled(label != strategy)
343
357
  mode.setChecked(label == strategy)
344
358
 
345
359
  cursor_menu = view_menu.addMenu("Cursor Style")
346
360
 
347
- cursor_stype = self.image_viewer.get_cursor_style()
348
- brush_action = QAction("Simple Brush", self)
361
+ self.cursor_style_actions = {
362
+ 'brush': QAction("Simple Brush", self),
363
+ 'preview': QAction("Brush Preview", self),
364
+ 'outline': QAction("Outline Only", self)
365
+ }
366
+ brush_action = self.cursor_style_actions['brush']
349
367
  brush_action.setCheckable(True)
350
- brush_action.setChecked(cursor_stype == 'brush')
351
- brush_action.triggered.connect(lambda: self.image_viewer.set_cursor_style('brush'))
368
+ brush_action.triggered.connect(lambda: set_cursor_style('brush'))
352
369
  cursor_menu.addAction(brush_action)
353
370
 
354
- preview_action = QAction("Brush Preview", self)
371
+ preview_action = self.cursor_style_actions['preview']
355
372
  preview_action.setCheckable(True)
356
- preview_action.setChecked(cursor_stype == 'preview')
357
- preview_action.triggered.connect(lambda: self.image_viewer.set_cursor_style('preview'))
373
+ preview_action.triggered.connect(lambda: set_cursor_style('preview'))
358
374
  cursor_menu.addAction(preview_action)
359
375
 
360
- outline_action = QAction("Outline Only", self)
376
+ outline_action = self.cursor_style_actions['outline']
361
377
  outline_action.setCheckable(True)
362
- outline_action.setChecked(cursor_stype == 'outline')
363
- outline_action.triggered.connect(lambda: self.image_viewer.set_cursor_style('outline'))
378
+ outline_action.triggered.connect(lambda: set_cursor_style('outline'))
364
379
  cursor_menu.addAction(outline_action)
365
380
 
381
+ def set_cursor_style(cursor_style):
382
+ self.image_viewer.set_cursor_style(cursor_style)
383
+ for label, style in self.cursor_style_actions.items():
384
+ style.setEnabled(label != cursor_style)
385
+ style.setChecked(label == cursor_style)
386
+
387
+ set_cursor_style(self.image_viewer.get_cursor_style())
388
+
366
389
  cursor_group = QActionGroup(self)
367
390
  cursor_group.addAction(preview_action)
368
391
  cursor_group.addAction(outline_action)
@@ -432,13 +455,13 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
432
455
  white_balance_action = QAction("White Balance", self)
433
456
  white_balance_action.triggered.connect(self.white_balance)
434
457
  filter_menu.addAction(white_balance_action)
435
- vignetting_action = QAction("Vignetting correction", self)
458
+ vignetting_action = QAction("Vignetting Correction", self)
436
459
  vignetting_action.triggered.connect(self.vignetting_correction)
437
460
  filter_menu.addAction(vignetting_action)
438
461
 
439
462
  help_menu = menubar.addMenu("&Help")
440
463
  help_menu.setObjectName("Help")
441
- shortcuts_help_action = QAction("Shortcuts and mouse", self)
464
+ shortcuts_help_action = QAction("Shortcuts and Mouse", self)
442
465
 
443
466
  def shortcuts_help():
444
467
  self.shortcuts_help_dialog = ShortcutsHelp(self)
@@ -693,13 +716,11 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
693
716
 
694
717
  def set_view_master(self):
695
718
  self.display_manager.set_view_master()
696
- self.display_manager.refresh_master_view()
697
719
  self.thumbnail_highlight = gui_constants.THUMB_MASTER_HI_COLOR
698
720
  self.highlight_master_thumbnail()
699
721
 
700
722
  def set_view_individual(self):
701
723
  self.display_manager.set_view_individual()
702
- self.display_manager.refresh_current_view()
703
724
  self.thumbnail_highlight = gui_constants.THUMB_MASTER_LO_COLOR
704
725
  self.highlight_master_thumbnail()
705
726
 
@@ -59,3 +59,7 @@ class ImageViewStatus(QObject):
59
59
 
60
60
  def set_max_scale(self, min_scale):
61
61
  self.max_scale = min_scale
62
+
63
+ def set_scroll(self, h_scroll, v_scroll):
64
+ self.h_scroll = h_scroll
65
+ self.v_scroll = v_scroll
@@ -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()
@@ -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 = 1.10
124
- zoom_out_factor = 1 / zoom_in_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 not self.empty():
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,77 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
162
114
  return self.handle_gesture_event(event)
163
115
  return super().event(event)
164
116
 
117
+ def set_master_image(self, qimage):
118
+ self.status.set_master_image(qimage)
119
+ pixmap = self.status.pixmap_master
120
+ self.setSceneRect(QRectF(pixmap.rect()))
121
+ img_width, img_height = pixmap.width(), pixmap.height()
122
+ self.set_max_min_scales(img_width, img_height)
123
+ self.set_zoom_factor(1.0)
124
+ self.resetTransform()
125
+ self.fitInView(self.pixmap_item_master, Qt.KeepAspectRatio)
126
+ self.set_zoom_factor(self.get_current_scale())
127
+ self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), self.zoom_factor())))
128
+ self.scale(self.zoom_factor(), self.zoom_factor())
129
+ self.centerOn(self.pixmap_item_master)
130
+ self.center_image(self)
131
+ self.update_cursor_pen_width()
132
+
133
+ def set_current_image(self, qimage):
134
+ self.status.set_current_image(qimage)
135
+ if self.empty():
136
+ self.setSceneRect(QRectF(self.status.pixmap_current.rect()))
137
+ self.update_cursor_pen_width()
138
+
139
+ def setup_brush_cursor(self):
140
+ super().setup_brush_cursor()
141
+ self.update_cursor_pen_width()
142
+
143
+ def show_master(self):
144
+ self.pixmap_item_master.setVisible(True)
145
+ self.pixmap_item_current.setVisible(False)
146
+ self.brush_preview.show()
147
+
148
+ def show_current(self):
149
+ self.pixmap_item_master.setVisible(False)
150
+ self.pixmap_item_current.setVisible(True)
151
+ self.brush_preview.hide()
152
+
153
+ def arrange_images(self):
154
+ if self.empty():
155
+ return
156
+ if self.pixmap_item_master.isVisible():
157
+ pixmap = self.pixmap_item_master.pixmap()
158
+ if not pixmap.isNull():
159
+ self.setSceneRect(QRectF(pixmap.rect()))
160
+ self.centerOn(self.pixmap_item_master)
161
+ self.center_image(self)
162
+ elif self.pixmap_item_current.isVisible():
163
+ pixmap = self.pixmap_item_current.pixmap()
164
+ if not pixmap.isNull():
165
+ self.setSceneRect(QRectF(pixmap.rect()))
166
+ self.centerOn(self.pixmap_item_current)
167
+ self.center_image(self)
168
+ current_scale = self.get_current_scale()
169
+ scale_factor = self.zoom_factor() / current_scale
170
+ self.scale(scale_factor, scale_factor)
171
+
172
+ def handle_key_press_event(self, event):
173
+ if event.key() in [Qt.Key_Up, Qt.Key_Down]:
174
+ return False
175
+ if event.key() == Qt.Key_X:
176
+ self.temp_view_requested.emit(True)
177
+ return False
178
+ return True
179
+
180
+ def handle_key_release_event(self, event):
181
+ if event.key() in [Qt.Key_Up, Qt.Key_Down]:
182
+ return False
183
+ if event.key() == Qt.Key_X:
184
+ self.temp_view_requested.emit(False)
185
+ return False
186
+ return True
187
+
165
188
  def handle_gesture_event(self, event):
166
189
  if self.empty():
167
190
  return False
@@ -190,23 +213,3 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
190
213
  self.scroll_view(self, int(scaled_delta.x()), int(scaled_delta.y()))
191
214
  elif pan_gesture.state() == Qt.GestureFinished:
192
215
  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