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