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.

@@ -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))
@@ -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 QPixmap, QPen, QColor, QCursor
4
- from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QFrame, QGraphicsEllipseItem
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
 
@@ -11,6 +10,7 @@ class ImageGraphicsView(ImageGraphicsViewBase):
11
10
  mouse_moved = Signal(QEvent)
12
11
  mouse_released = Signal(QEvent)
13
12
  gesture_event = Signal(QEvent)
13
+ wheel_event = Signal(QEvent)
14
14
 
15
15
  # pylint: disable=C0103
16
16
  def event(self, event):
@@ -29,6 +29,10 @@ class ImageGraphicsView(ImageGraphicsViewBase):
29
29
  def mouseReleaseEvent(self, event):
30
30
  self.mouse_released.emit(event)
31
31
  super().mouseReleaseEvent(event)
32
+
33
+ def wheelEvent(self, event):
34
+ self.wheel_event.emit(event)
35
+ event.accept()
32
36
  # pylint: enable=C0103
33
37
 
34
38
 
@@ -46,7 +50,6 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
46
50
  self._connect_signals()
47
51
  self.panning_current = False
48
52
  self.brush_cursor = None
49
- self.setup_brush_cursor()
50
53
  self.setFocusPolicy(Qt.StrongFocus)
51
54
  self.pan_start = None
52
55
  self.pinch_start_scale = None
@@ -55,15 +58,13 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
55
58
  self.current_view.setFocusPolicy(Qt.NoFocus)
56
59
  self.master_view.setFocusPolicy(Qt.NoFocus)
57
60
  self.current_brush_cursor = None
58
- self.setup_current_brush_cursor()
59
61
 
60
62
  def setup_layout(self):
61
- """To be implemented by subclasses - creates layout and adds widgets"""
62
63
  raise NotImplementedError("Subclasses must implement setup_layout")
63
64
 
64
65
  def create_pixmaps(self):
65
- self.current_pixmap_item = self.create_pixmap(self.current_scene)
66
- self.master_pixmap_item = self.create_pixmap(self.master_scene)
66
+ self.pixmap_item_current = self.create_pixmap(self.current_scene)
67
+ self.pixmap_item_master = self.create_pixmap(self.master_scene)
67
68
 
68
69
  def _connect_signals(self):
69
70
  self.current_view.mouse_pressed.connect(self.handle_current_mouse_press)
@@ -82,6 +83,8 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
82
83
  self.current_view.horizontalScrollBar().setValue)
83
84
  self.master_view.verticalScrollBar().valueChanged.connect(
84
85
  self.current_view.verticalScrollBar().setValue)
86
+ self.current_view.wheel_event.connect(self.handle_wheel_event)
87
+ self.master_view.wheel_event.connect(self.handle_wheel_event)
85
88
  # pylint: disable=C0103, W0201
86
89
  self.current_view.enterEvent = self.current_view_enter_event
87
90
  self.current_view.leaveEvent = self.current_view_leave_event
@@ -92,31 +95,44 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
92
95
  def current_view_enter_event(self, event):
93
96
  self.activateWindow()
94
97
  self.setFocus()
95
- self.update_brush_cursor()
98
+ if not self.empty():
99
+ self.update_brush_cursor()
96
100
  super(ImageGraphicsView, self.current_view).enterEvent(event)
97
101
 
98
102
  def current_view_leave_event(self, event):
99
- self.update_brush_cursor()
103
+ if not self.empty():
104
+ self.update_brush_cursor()
100
105
  super(ImageGraphicsView, self.current_view).leaveEvent(event)
101
106
 
102
107
  def master_view_enter_event(self, event):
103
108
  self.activateWindow()
104
109
  self.setFocus()
105
- self.update_brush_cursor()
110
+ if not self.empty():
111
+ self.update_brush_cursor()
106
112
  super(ImageGraphicsView, self.master_view).enterEvent(event)
107
113
 
108
114
  def master_view_leave_event(self, event):
109
- self.update_brush_cursor()
115
+ if not self.empty():
116
+ self.update_brush_cursor()
110
117
  super(ImageGraphicsView, self.master_view).leaveEvent(event)
111
118
 
112
119
  def get_master_view(self):
113
120
  return self.master_view
114
121
 
122
+ def get_current_view(self):
123
+ return self.current_view
124
+
115
125
  def get_master_scene(self):
116
126
  return self.master_scene
117
127
 
128
+ def get_current_scene(self):
129
+ return self.current_scene
130
+
118
131
  def get_master_pixmap(self):
119
- return self.master_pixmap_item
132
+ return self.pixmap_item_master
133
+
134
+ def get_current_pixmap(self):
135
+ return self.pixmap_item_current
120
136
 
121
137
  def get_views(self):
122
138
  return [self.master_view, self.current_view]
@@ -126,8 +142,8 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
126
142
 
127
143
  def get_pixmaps(self):
128
144
  return {
129
- self.master_pixmap_item: self.master_view,
130
- self.current_pixmap_item: self.current_view
145
+ self.pixmap_item_master: self.master_view,
146
+ self.pixmap_item_current: self.current_view
131
147
  }
132
148
 
133
149
  # pylint: disable=C0103
@@ -153,22 +169,34 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
153
169
  def enterEvent(self, event):
154
170
  self.activateWindow()
155
171
  self.setFocus()
156
- if not self.empty():
172
+ if self.empty():
173
+ self.setCursor(Qt.ArrowCursor)
174
+ self.master_view.setCursor(Qt.ArrowCursor)
175
+ self.current_view.setCursor(Qt.ArrowCursor)
176
+ else:
157
177
  if self.space_pressed:
158
178
  self.master_view.setCursor(Qt.OpenHandCursor)
179
+ self.current_view.setCursor(Qt.OpenHandCursor)
159
180
  else:
181
+ if self.brush_cursor is None or self.current_brush_cursor is None:
182
+ self.setup_brush_cursor()
160
183
  self.master_view.setCursor(Qt.BlankCursor)
161
- if self.brush_cursor:
162
- self.brush_cursor.show()
184
+ self.current_view.setCursor(Qt.BlankCursor)
185
+ self.brush_cursor.show()
163
186
  super().enterEvent(event)
164
187
 
165
188
  def leaveEvent(self, event):
166
- if self.brush_cursor:
189
+ if self.empty():
190
+ self.setCursor(Qt.ArrowCursor)
191
+ self.master_view.setCursor(Qt.ArrowCursor)
192
+ self.current_view.setCursor(Qt.ArrowCursor)
193
+ else:
194
+ if self.brush_cursor is None or self.current_brush_cursor is None:
195
+ self.setup_brush_cursor()
167
196
  self.brush_cursor.hide()
168
- if self.current_brush_cursor:
169
197
  self.current_brush_cursor.hide()
170
- self.master_view.setCursor(Qt.ArrowCursor)
171
- self.current_view.setCursor(Qt.ArrowCursor)
198
+ self.master_view.setCursor(Qt.ArrowCursor)
199
+ self.current_view.setCursor(Qt.ArrowCursor)
172
200
  super().leaveEvent(event)
173
201
 
174
202
  def keyPressEvent(self, event):
@@ -182,97 +210,98 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
182
210
  self.update_brush_cursor()
183
211
  # pylint: enable=C0103
184
212
 
213
+ # pylint: enable=R0801
214
+ def handle_wheel_event(self, event):
215
+ if self.empty() or self.gesture_active:
216
+ return
217
+ if event.source() == Qt.MouseEventNotSynthesized: # Physical mouse
218
+ if self.control_pressed:
219
+ self.brush_size_change_requested.emit(1 if event.angleDelta().y() > 0 else -1)
220
+ else:
221
+ if event.angleDelta().y() > 0: # Zoom in
222
+ self.zoom_in()
223
+ else: # Zoom out
224
+ self.zoom_out()
225
+ else: # Touchpad event - handle scrolling
226
+ if not self.control_pressed:
227
+ delta = event.pixelDelta() or event.angleDelta() / 8
228
+ if delta:
229
+ self.scroll_view(self.master_view, delta.x(), delta.y())
230
+ self.scroll_view(self.current_view, delta.x(), delta.y())
231
+ # pylint: disable=R0801
232
+
233
+ def _apply_zoom_to_view(self, view, factor):
234
+ view.scale(factor, factor)
235
+ other_view = self.current_view if view == self.master_view else self.master_view
236
+ other_view.resetTransform()
237
+ other_view.scale(self.zoom_factor(), self.zoom_factor())
238
+
239
+ def show_master(self):
240
+ self.pixmap_item_master.setVisible(True)
241
+ self.pixmap_item_current.setVisible(True)
242
+
243
+ def show_current(self):
244
+ self.pixmap_item_master.setVisible(True)
245
+ self.pixmap_item_current.setVisible(True)
246
+
185
247
  def setup_brush_cursor(self):
186
248
  super().setup_brush_cursor()
187
249
  self.setup_current_brush_cursor()
188
- self.update_cursor_pen_width()
189
250
 
190
251
  def setup_current_brush_cursor(self):
191
252
  if not self.brush:
192
253
  return
193
- for item in self.current_scene.items():
194
- if isinstance(item, QGraphicsEllipseItem) and item != self.brush_preview:
195
- self.current_scene.removeItem(item)
196
- pen_width = gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()
197
- pen = QPen(QColor(255, 0, 0), pen_width)
198
- brush = Qt.NoBrush
199
- self.current_brush_cursor = self.current_scene.addEllipse(
200
- 0, 0, self.brush.size, self.brush.size, pen, brush)
201
- self.current_brush_cursor.setZValue(1000)
202
- self.current_brush_cursor.hide()
203
-
204
- def update_current_brush_cursor(self, scene_pos):
205
- if not self.current_brush_cursor or not self.isVisible():
206
- return
207
- size = self.brush.size
208
- radius = size / 2
209
- self.current_brush_cursor.setRect(
210
- scene_pos.x() - radius, scene_pos.y() - radius, size, size)
211
- if self.brush_cursor and self.brush_cursor.isVisible():
212
- self.current_brush_cursor.show()
213
- else:
214
- self.current_brush_cursor.hide()
254
+ self.current_brush_cursor = self.create_alt_circle(
255
+ self.get_current_scene(), line_style=Qt.SolidLine)
215
256
 
216
257
  def update_cursor_pen_width(self):
217
- if not self.brush_cursor or not self.current_brush_cursor:
218
- return
219
- pen_width = gui_constants.BRUSH_LINE_WIDTH / self.zoom_factor()
220
- master_pen = self.brush_cursor.pen()
221
- master_pen.setWidthF(pen_width)
222
- self.brush_cursor.setPen(master_pen)
223
- current_pen = self.current_brush_cursor.pen()
224
- current_pen.setWidthF(pen_width)
225
- self.current_brush_cursor.setPen(current_pen)
258
+ pen_width = super().update_cursor_pen_width()
259
+ if self.current_brush_cursor:
260
+ current_pen = self.current_brush_cursor.pen()
261
+ current_pen.setWidthF(pen_width)
262
+ self.current_brush_cursor.setPen(current_pen)
263
+ return pen_width
226
264
 
227
265
  def update_brush_cursor(self):
228
266
  if self.empty():
229
267
  return
268
+ if self.brush_cursor is None or self.current_brush_cursor is None:
269
+ self.setup_brush_cursor()
230
270
  self.update_cursor_pen_width()
271
+ if self.space_pressed:
272
+ cursor_style = Qt.OpenHandCursor if not self.scrolling else Qt.ClosedHandCursor
273
+ self.master_view.setCursor(cursor_style)
274
+ self.current_view.setCursor(cursor_style)
275
+ self.brush_cursor.hide()
276
+ self.current_brush_cursor.hide()
277
+ return
278
+ self.master_view.setCursor(Qt.BlankCursor)
279
+ self.current_view.setCursor(Qt.BlankCursor)
231
280
  mouse_pos_global = QCursor.pos()
232
281
  mouse_pos_current = self.current_view.mapFromGlobal(mouse_pos_global)
233
282
  mouse_pos_master = self.master_view.mapFromGlobal(mouse_pos_global)
234
283
  current_has_mouse = self.current_view.rect().contains(mouse_pos_current)
235
284
  master_has_mouse = self.master_view.rect().contains(mouse_pos_master)
285
+ self.current_brush_cursor.hide()
236
286
  if master_has_mouse:
287
+ if self.cursor_style == 'preview':
288
+ self.brush_preview.show()
237
289
  super().update_brush_cursor()
238
290
  self.sync_current_cursor_with_master()
239
- if self.space_pressed:
240
- cursor_style = Qt.OpenHandCursor if not self.scrolling else Qt.ClosedHandCursor
241
- self.master_view.setCursor(cursor_style)
242
- self.current_view.setCursor(cursor_style)
243
- else:
244
- self.master_view.setCursor(Qt.BlankCursor)
245
- self.current_view.setCursor(Qt.BlankCursor)
246
291
  elif current_has_mouse:
292
+ self.brush_preview.hide()
247
293
  scene_pos = self.current_view.mapToScene(mouse_pos_current)
248
294
  size = self.brush.size
249
295
  radius = size / 2
250
296
  self.current_brush_cursor.setRect(
251
- scene_pos.x() - radius,
252
- scene_pos.y() - radius,
253
- size, size
254
- )
297
+ scene_pos.x() - radius, scene_pos.y() - radius, size, size)
255
298
  self.current_brush_cursor.show()
256
- if self.brush_cursor:
257
- self.brush_cursor.setRect(
258
- scene_pos.x() - radius,
259
- scene_pos.y() - radius,
260
- size, size
261
- )
262
- self.brush_cursor.show()
263
- if self.space_pressed:
264
- cursor_style = Qt.OpenHandCursor \
265
- if not self.panning_current else Qt.ClosedHandCursor
266
- self.current_view.setCursor(cursor_style)
267
- self.master_view.setCursor(cursor_style)
268
- else:
269
- self.current_view.setCursor(Qt.BlankCursor)
270
- self.master_view.setCursor(Qt.BlankCursor)
299
+ self.brush_cursor.setRect(
300
+ scene_pos.x() - radius, scene_pos.y() - radius, size, size)
301
+ self.brush_cursor.show()
271
302
  else:
272
- if self.brush_cursor:
273
- self.brush_cursor.hide()
274
- if self.current_brush_cursor:
275
- self.current_brush_cursor.hide()
303
+ self.brush_cursor.hide()
304
+ self.current_brush_cursor.hide()
276
305
  self.master_view.setCursor(Qt.ArrowCursor)
277
306
  self.current_view.setCursor(Qt.ArrowCursor)
278
307
 
@@ -316,103 +345,55 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
316
345
  self.handle_pinch_gesture(pinch_gesture)
317
346
  event.accept()
318
347
 
319
- def handle_pinch_gesture(self, pinch):
320
- if pinch.state() == Qt.GestureStarted:
321
- self.pinch_start_scale = self.zoom_factor()
322
- self.pinch_center_view = pinch.centerPoint()
323
- self.pinch_center_scene = self.master_view.mapToScene(self.pinch_center_view.toPoint())
324
- elif pinch.state() == Qt.GestureUpdated:
325
- new_scale = self.pinch_start_scale * pinch.totalScaleFactor()
326
- new_scale = max(self.min_scale(), min(new_scale, self.max_scale()))
327
- if abs(new_scale - self.zoom_factor()) > 0.01:
328
- self.set_zoom_factor(new_scale)
329
- self._apply_zoom()
330
- new_center = self.master_view.mapToScene(self.pinch_center_view.toPoint())
331
- delta = self.pinch_center_scene - new_center
332
- h_scroll = self.master_view.horizontalScrollBar().value()
333
- v_scroll = self.master_view.verticalScrollBar().value()
334
- self.master_view.horizontalScrollBar().setValue(
335
- h_scroll + int(delta.x() * self.zoom_factor()))
336
- self.master_view.verticalScrollBar().setValue(
337
- v_scroll + int(delta.y() * self.zoom_factor()))
338
-
339
348
  def set_master_image(self, qimage):
340
349
  self.status.set_master_image(qimage)
341
350
  pixmap = self.status.pixmap_master
342
- img_width, img_height = pixmap.width(), pixmap.height()
343
351
  self.master_view.setSceneRect(QRectF(pixmap.rect()))
344
- self.master_pixmap_item.setPixmap(pixmap)
345
- self.set_min_scale(min(gui_constants.MIN_ZOOMED_IMG_WIDTH / img_width,
346
- gui_constants.MIN_ZOOMED_IMG_HEIGHT / img_height))
347
- self.set_max_scale(gui_constants.MAX_ZOOMED_IMG_PX_SIZE)
348
- self.set_zoom_factor(1.0)
349
- self.master_view.fitInView(self.master_pixmap_item, Qt.KeepAspectRatio)
350
- self.set_zoom_factor(self.get_current_scale())
351
- self.set_zoom_factor(max(self.min_scale(), min(self.max_scale(), self.zoom_factor())))
352
+ self.pixmap_item_master.setPixmap(pixmap)
353
+ img_width, img_height = pixmap.width(), pixmap.height()
354
+ self.set_max_min_scales(img_width, img_height)
355
+ view_rect = self.master_view.viewport().rect()
356
+ scale_x = view_rect.width() / img_width
357
+ scale_y = view_rect.height() / img_height
358
+ scale_factor = min(scale_x, scale_y)
359
+ scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
360
+ self.set_zoom_factor(scale_factor)
352
361
  self.master_view.resetTransform()
353
- self.master_scene.scale(self.zoom_factor(), self.zoom_factor())
354
- self.master_view.centerOn(self.master_pixmap_item)
362
+ self.master_view.scale(scale_factor, scale_factor)
363
+ self.master_view.centerOn(self.pixmap_item_master)
355
364
  center = self.master_scene.sceneRect().center()
356
365
  self.brush_preview.setPos(max(0, min(center.x(), img_width)),
357
366
  max(0, min(center.y(), img_height)))
358
- self.master_scene.setSceneRect(QRectF(self.master_pixmap_item.boundingRect()))
367
+ self.master_scene.setSceneRect(QRectF(self.pixmap_item_master.boundingRect()))
368
+ self.center_image(self.master_view)
369
+ self.update_cursor_pen_width()
359
370
 
360
371
  def set_current_image(self, qimage):
361
372
  self.status.set_current_image(qimage)
362
373
  pixmap = self.status.pixmap_current
363
374
  self.current_scene.setSceneRect(QRectF(pixmap.rect()))
364
- self.current_pixmap_item.setPixmap(pixmap)
375
+ self.pixmap_item_current.setPixmap(pixmap)
365
376
  self.current_view.resetTransform()
366
- self.current_scene.scale(self.zoom_factor(), self.zoom_factor())
367
- # self.current_view.centerOn(self.current_pixmap_item)
368
- self.current_scene.setSceneRect(QRectF(self.current_pixmap_item.boundingRect()))
377
+ self.master_view.scale(self.zoom_factor(), self.zoom_factor())
378
+ self.current_scene.setSceneRect(QRectF(self.pixmap_item_current.boundingRect()))
379
+ self.center_image(self.current_view)
369
380
 
370
- def _arrange_images(self):
381
+ def arrange_images(self):
371
382
  if self.status.empty():
372
383
  return
373
- if self.master_pixmap_item.pixmap().height() == 0:
384
+ if self.pixmap_item_master.pixmap().height() == 0:
374
385
  self.update_master_display()
375
386
  self.update_current_display()
376
387
  self.reset_zoom()
377
- self._apply_zoom()
378
-
379
- def update_master_display(self):
380
- if not self.status.empty():
381
- master_qimage = self.numpy_to_qimage(self.master_layer())
382
- if master_qimage:
383
- pixmap = QPixmap.fromImage(master_qimage)
384
- self.master_pixmap_item.setPixmap(pixmap)
385
- self.master_scene.setSceneRect(QRectF(pixmap.rect()))
386
- self._arrange_images()
387
-
388
- def update_current_display(self):
389
- if not self.status.empty() and self.number_of_layers() > 0:
390
- current_qimage = self.numpy_to_qimage(self.current_layer())
391
- if current_qimage:
392
- pixmap = QPixmap.fromImage(current_qimage)
393
- self.current_pixmap_item.setPixmap(pixmap)
394
- self.current_scene.setSceneRect(QRectF(pixmap.rect()))
395
- self._arrange_images()
396
-
397
- def _apply_zoom(self):
398
- if not self.current_pixmap_item.pixmap().isNull():
399
- self.current_view.resetTransform()
400
- self.current_view.scale(self.zoom_factor(), self.zoom_factor())
401
- # self.current_view.centerOn(self.current_pixmap_item)
402
- if not self.master_pixmap_item.pixmap().isNull():
403
- self.master_view.resetTransform()
404
- self.master_view.scale(self.zoom_factor(), self.zoom_factor())
405
- # self.master_view.centerOn(self.master_pixmap_item)
406
-
407
- def set_brush(self, brush):
408
- super().set_brush(brush)
409
- if self.brush_cursor:
410
- self.master_scene.removeItem(self.brush_cursor)
411
- self.setup_brush_cursor()
412
- self.setup_current_brush_cursor()
388
+ else:
389
+ self.center_image(self.master_view)
390
+ self.apply_zoom()
413
391
 
414
392
  def clear_image(self):
415
393
  super().clear_image()
394
+ self.setCursor(Qt.ArrowCursor)
395
+ self.master_view.setCursor(Qt.ArrowCursor)
396
+ self.current_view.setCursor(Qt.ArrowCursor)
416
397
  if self.current_brush_cursor:
417
398
  self.current_scene.removeItem(self.current_brush_cursor)
418
399
  self.current_brush_cursor = None
@@ -425,31 +406,13 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
425
406
  size = self.brush.size
426
407
  radius = size / 2
427
408
  self.current_brush_cursor.setRect(
428
- scene_pos.x() - radius,
429
- scene_pos.y() - radius,
430
- size, size
431
- )
409
+ scene_pos.x() - radius, scene_pos.y() - radius,
410
+ size, size)
432
411
  if self.brush_cursor.isVisible():
433
412
  self.current_brush_cursor.show()
434
413
  else:
435
414
  self.current_brush_cursor.hide()
436
415
 
437
- def zoom_in(self):
438
- super().zoom_in()
439
- self.update_cursor_pen_width()
440
-
441
- def zoom_out(self):
442
- super().zoom_out()
443
- self.update_cursor_pen_width()
444
-
445
- def reset_zoom(self):
446
- super().reset_zoom()
447
- self.update_cursor_pen_width()
448
-
449
- def actual_size(self):
450
- super().actual_size()
451
- self.update_cursor_pen_width()
452
-
453
416
 
454
417
  class SideBySideView(DoubleViewBase):
455
418
  def setup_layout(self):
@@ -0,0 +1,43 @@
1
+ # pylint: disable=C0114, C0115, C0116, E1101, W0718
2
+ import traceback
3
+ import cv2
4
+ from .. config.gui_constants import gui_constants
5
+ from .layer_collection import LayerCollectionHandler
6
+
7
+
8
+ class TransfromationManager(LayerCollectionHandler):
9
+ def __init__(self, editor):
10
+ super().__init__(editor.layer_collection)
11
+ self.editor = editor
12
+
13
+ def transform(self, transf_func, label, undoable=True):
14
+ if self.has_no_master_layer():
15
+ return
16
+ if undoable:
17
+ try:
18
+ undo = self.editor.undo_manager
19
+ undo.x_start, undo.x_stop = 0, 1
20
+ undo.y_start, undo.y_stop = 0, 1
21
+ undo.save_undo_state(self.editor.master_layer(), label)
22
+ except Exception as e:
23
+ traceback.print_tb(e.__traceback__)
24
+ self.set_master_layer(transf_func(self.master_layer()))
25
+ self.set_layer_stack([transf_func(layer) for layer in self.layer_stack()])
26
+ self.copy_master_layer()
27
+ self.editor.image_viewer.update_master_display()
28
+ self.editor.image_viewer.update_current_display()
29
+ self.editor.image_viewer.refresh_display()
30
+ self.editor.display_manager.update_thumbnails()
31
+ self.editor.mark_as_modified()
32
+
33
+ def rotate_90_cw(self, undoable=True):
34
+ self.transform(lambda img: cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE),
35
+ gui_constants.ROTATE_90_CW_LABEL, undoable)
36
+
37
+ def rotate_90_ccw(self, undoable=True):
38
+ self.transform(lambda img: cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE),
39
+ gui_constants.ROTATE_90_CCW_LABEL, undoable)
40
+
41
+ def rotate_180(self, undoable=True):
42
+ self.transform(lambda img: cv2.rotate(img, cv2.ROTATE_180),
43
+ gui_constants.ROTATE_180_LABEL, undoable)