src/eric7/Snapshot/SnapshotRegionGrabber.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
7 Module implementing a grabber widget for a rectangular snapshot region. 7 Module implementing a grabber widget for a rectangular snapshot region.
8 """ 8 """
9 9
10 from PyQt6.QtCore import pyqtSignal, Qt, QRect, QPoint, QTimer, QLocale 10 from PyQt6.QtCore import pyqtSignal, Qt, QRect, QPoint, QTimer, QLocale
11 from PyQt6.QtGui import ( 11 from PyQt6.QtGui import (
12 QPixmap, QColor, QRegion, QPainter, QPalette, QPaintEngine, QPen, QBrush, 12 QPixmap,
13 QGuiApplication, QCursor 13 QColor,
14 QRegion,
15 QPainter,
16 QPalette,
17 QPaintEngine,
18 QPen,
19 QBrush,
20 QGuiApplication,
21 QCursor,
14 ) 22 )
15 from PyQt6.QtWidgets import QWidget, QToolTip 23 from PyQt6.QtWidgets import QWidget, QToolTip
16 24
17 import Globals 25 import Globals
18 26
19 27
20 def drawRect(painter, rect, outline, fill=None): 28 def drawRect(painter, rect, outline, fill=None):
21 """ 29 """
22 Module function to draw a rectangle with the given parameters. 30 Module function to draw a rectangle with the given parameters.
23 31
24 @param painter reference to the painter to be used (QPainter) 32 @param painter reference to the painter to be used (QPainter)
25 @param rect rectangle to be drawn (QRect) 33 @param rect rectangle to be drawn (QRect)
26 @param outline color of the outline (QColor) 34 @param outline color of the outline (QColor)
27 @param fill fill color (QColor) 35 @param fill fill color (QColor)
28 """ 36 """
29 clip = QRegion(rect) 37 clip = QRegion(rect)
30 clip = clip.subtracted(QRegion(rect.adjusted(1, 1, -1, -1))) 38 clip = clip.subtracted(QRegion(rect.adjusted(1, 1, -1, -1)))
31 39
32 painter.save() 40 painter.save()
33 painter.setClipRegion(clip) 41 painter.setClipRegion(clip)
34 painter.setPen(Qt.PenStyle.NoPen) 42 painter.setPen(Qt.PenStyle.NoPen)
35 painter.setBrush(outline) 43 painter.setBrush(outline)
36 painter.drawRect(rect) 44 painter.drawRect(rect)
42 50
43 51
44 class SnapshotRegionGrabber(QWidget): 52 class SnapshotRegionGrabber(QWidget):
45 """ 53 """
46 Class implementing a grabber widget for a rectangular snapshot region. 54 Class implementing a grabber widget for a rectangular snapshot region.
47 55
48 @signal grabbed(QPixmap) emitted after the region was grabbed 56 @signal grabbed(QPixmap) emitted after the region was grabbed
49 """ 57 """
58
50 grabbed = pyqtSignal(QPixmap) 59 grabbed = pyqtSignal(QPixmap)
51 60
52 StrokeMask = 0 61 StrokeMask = 0
53 FillMask = 1 62 FillMask = 1
54 63
55 Rectangle = 0 64 Rectangle = 0
56 Ellipse = 1 65 Ellipse = 1
57 66
58 def __init__(self, mode=Rectangle): 67 def __init__(self, mode=Rectangle):
59 """ 68 """
60 Constructor 69 Constructor
61 70
62 @param mode region grabber mode (SnapshotRegionGrabber.Rectangle or 71 @param mode region grabber mode (SnapshotRegionGrabber.Rectangle or
63 SnapshotRegionGrabber.Ellipse) 72 SnapshotRegionGrabber.Ellipse)
64 @exception ValueError raised to indicate a bad value for the 'mode' 73 @exception ValueError raised to indicate a bad value for the 'mode'
65 parameter 74 parameter
66 """ 75 """
67 super().__init__( 76 super().__init__(
68 None, 77 None,
69 Qt.WindowType.X11BypassWindowManagerHint | 78 Qt.WindowType.X11BypassWindowManagerHint
70 Qt.WindowType.WindowStaysOnTopHint | 79 | Qt.WindowType.WindowStaysOnTopHint
71 Qt.WindowType.FramelessWindowHint | 80 | Qt.WindowType.FramelessWindowHint
72 Qt.WindowType.Tool 81 | Qt.WindowType.Tool,
73 ) 82 )
74 83
75 if mode not in [SnapshotRegionGrabber.Rectangle, 84 if mode not in [SnapshotRegionGrabber.Rectangle, SnapshotRegionGrabber.Ellipse]:
76 SnapshotRegionGrabber.Ellipse]:
77 raise ValueError("Bad value for 'mode' parameter.") 85 raise ValueError("Bad value for 'mode' parameter.")
78 self.__mode = mode 86 self.__mode = mode
79 87
80 self.__selection = QRect() 88 self.__selection = QRect()
81 self.__mouseDown = False 89 self.__mouseDown = False
82 self.__newSelection = False 90 self.__newSelection = False
83 self.__handleSize = 10 91 self.__handleSize = 10
84 self.__mouseOverHandle = None 92 self.__mouseOverHandle = None
85 self.__showHelp = True 93 self.__showHelp = True
86 self.__grabbing = False 94 self.__grabbing = False
87 self.__dragStartPoint = QPoint() 95 self.__dragStartPoint = QPoint()
88 self.__selectionBeforeDrag = QRect() 96 self.__selectionBeforeDrag = QRect()
89 self.__locale = QLocale() 97 self.__locale = QLocale()
90 98
91 # naming conventions for handles 99 # naming conventions for handles
92 # T top, B bottom, R Right, L left 100 # T top, B bottom, R Right, L left
93 # 2 letters: a corner 101 # 2 letters: a corner
94 # 1 letter: the handle on the middle of the corresponding side 102 # 1 letter: the handle on the middle of the corresponding side
95 self.__TLHandle = QRect(0, 0, self.__handleSize, self.__handleSize) 103 self.__TLHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
98 self.__BRHandle = QRect(0, 0, self.__handleSize, self.__handleSize) 106 self.__BRHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
99 self.__LHandle = QRect(0, 0, self.__handleSize, self.__handleSize) 107 self.__LHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
100 self.__THandle = QRect(0, 0, self.__handleSize, self.__handleSize) 108 self.__THandle = QRect(0, 0, self.__handleSize, self.__handleSize)
101 self.__RHandle = QRect(0, 0, self.__handleSize, self.__handleSize) 109 self.__RHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
102 self.__BHandle = QRect(0, 0, self.__handleSize, self.__handleSize) 110 self.__BHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
103 self.__handles = [self.__TLHandle, self.__TRHandle, self.__BLHandle, 111 self.__handles = [
104 self.__BRHandle, self.__LHandle, self.__THandle, 112 self.__TLHandle,
105 self.__RHandle, self.__BHandle] 113 self.__TRHandle,
114 self.__BLHandle,
115 self.__BRHandle,
116 self.__LHandle,
117 self.__THandle,
118 self.__RHandle,
119 self.__BHandle,
120 ]
106 self.__helpTextRect = QRect() 121 self.__helpTextRect = QRect()
107 self.__helpText = self.tr( 122 self.__helpText = self.tr(
108 "Select a region using the mouse. To take the snapshot, press" 123 "Select a region using the mouse. To take the snapshot, press"
109 " the Enter key or double click. Press Esc to quit.") 124 " the Enter key or double click. Press Esc to quit."
110 125 )
126
111 self.__pixmap = QPixmap() 127 self.__pixmap = QPixmap()
112 128
113 self.setMouseTracking(True) 129 self.setMouseTracking(True)
114 130
115 QTimer.singleShot(200, self.__initialize) 131 QTimer.singleShot(200, self.__initialize)
116 132
117 def __initialize(self): 133 def __initialize(self):
118 """ 134 """
119 Private slot to initialize the rest of the widget. 135 Private slot to initialize the rest of the widget.
120 """ 136 """
121 if Globals.isMacPlatform(): 137 if Globals.isMacPlatform():
122 # macOS variant 138 # macOS variant
123 screen = QGuiApplication.screenAt(QCursor.pos()) 139 screen = QGuiApplication.screenAt(QCursor.pos())
124 geom = screen.geometry() 140 geom = screen.geometry()
125 self.__pixmap = screen.grabWindow( 141 self.__pixmap = screen.grabWindow(
126 0, geom.x(), geom.y(), geom.width(), geom.height()) 142 0, geom.x(), geom.y(), geom.width(), geom.height()
143 )
127 else: 144 else:
128 # Linux variant 145 # Linux variant
129 # Windows variant 146 # Windows variant
130 screen = QGuiApplication.screens()[0] 147 screen = QGuiApplication.screens()[0]
131 geom = screen.availableVirtualGeometry() 148 geom = screen.availableVirtualGeometry()
132 self.__pixmap = screen.grabWindow( 149 self.__pixmap = screen.grabWindow(
133 0, geom.x(), geom.y(), geom.width(), geom.height()) 150 0, geom.x(), geom.y(), geom.width(), geom.height()
151 )
134 self.resize(self.__pixmap.size()) 152 self.resize(self.__pixmap.size())
135 self.move(geom.x(), geom.y()) 153 self.move(geom.x(), geom.y())
136 self.setCursor(Qt.CursorShape.CrossCursor) 154 self.setCursor(Qt.CursorShape.CrossCursor)
137 self.show() 155 self.show()
138 156
139 self.grabMouse() 157 self.grabMouse()
140 self.grabKeyboard() 158 self.grabKeyboard()
141 self.activateWindow() 159 self.activateWindow()
142 160
143 def paintEvent(self, evt): 161 def paintEvent(self, evt):
144 """ 162 """
145 Protected method handling paint events. 163 Protected method handling paint events.
146 164
147 @param evt paint event (QPaintEvent) 165 @param evt paint event (QPaintEvent)
148 """ 166 """
149 if self.__grabbing: # grabWindow() should just get the background 167 if self.__grabbing: # grabWindow() should just get the background
150 return 168 return
151 169
152 painter = QPainter(self) 170 painter = QPainter(self)
153 pal = QPalette(QToolTip.palette()) 171 pal = QPalette(QToolTip.palette())
154 font = QToolTip.font() 172 font = QToolTip.font()
155 173
156 handleColor = pal.color(QPalette.ColorGroup.Active, 174 handleColor = pal.color(
157 QPalette.ColorRole.Highlight) 175 QPalette.ColorGroup.Active, QPalette.ColorRole.Highlight
176 )
158 handleColor.setAlpha(160) 177 handleColor.setAlpha(160)
159 overlayColor = QColor(0, 0, 0, 160) 178 overlayColor = QColor(0, 0, 0, 160)
160 textColor = pal.color(QPalette.ColorGroup.Active, 179 textColor = pal.color(QPalette.ColorGroup.Active, QPalette.ColorRole.Text)
161 QPalette.ColorRole.Text) 180 textBackgroundColor = pal.color(
162 textBackgroundColor = pal.color(QPalette.ColorGroup.Active, 181 QPalette.ColorGroup.Active, QPalette.ColorRole.Base
163 QPalette.ColorRole.Base) 182 )
164 painter.drawPixmap(0, 0, self.__pixmap) 183 painter.drawPixmap(0, 0, self.__pixmap)
165 painter.setFont(font) 184 painter.setFont(font)
166 185
167 r = QRect(self.__selection) 186 r = QRect(self.__selection)
168 if not self.__selection.isNull(): 187 if not self.__selection.isNull():
169 grey = QRegion(self.rect()) 188 grey = QRegion(self.rect())
170 if self.__mode == SnapshotRegionGrabber.Ellipse: 189 if self.__mode == SnapshotRegionGrabber.Ellipse:
171 reg = QRegion(r, QRegion.RegionType.Ellipse) 190 reg = QRegion(r, QRegion.RegionType.Ellipse)
176 painter.setPen(Qt.PenStyle.NoPen) 195 painter.setPen(Qt.PenStyle.NoPen)
177 painter.setBrush(overlayColor) 196 painter.setBrush(overlayColor)
178 painter.drawRect(self.rect()) 197 painter.drawRect(self.rect())
179 painter.setClipRect(self.rect()) 198 painter.setClipRect(self.rect())
180 drawRect(painter, r, handleColor) 199 drawRect(painter, r, handleColor)
181 200
182 if self.__showHelp: 201 if self.__showHelp:
183 painter.setPen(textColor) 202 painter.setPen(textColor)
184 painter.setBrush(textBackgroundColor) 203 painter.setBrush(textBackgroundColor)
185 self.__helpTextRect = painter.boundingRect( 204 self.__helpTextRect = painter.boundingRect(
186 self.rect().adjusted(2, 2, -2, -2), 205 self.rect().adjusted(2, 2, -2, -2),
187 Qt.TextFlag.TextWordWrap, self.__helpText).translated(0, 0) 206 Qt.TextFlag.TextWordWrap,
207 self.__helpText,
208 ).translated(0, 0)
188 self.__helpTextRect.adjust(-2, -2, 4, 2) 209 self.__helpTextRect.adjust(-2, -2, 4, 2)
189 drawRect(painter, self.__helpTextRect, textColor, 210 drawRect(painter, self.__helpTextRect, textColor, textBackgroundColor)
190 textBackgroundColor)
191 painter.drawText( 211 painter.drawText(
192 self.__helpTextRect.adjusted(3, 3, -3, -3), 212 self.__helpTextRect.adjusted(3, 3, -3, -3),
193 Qt.TextFlag.TextWordWrap, self.__helpText) 213 Qt.TextFlag.TextWordWrap,
194 214 self.__helpText,
215 )
216
195 if self.__selection.isNull(): 217 if self.__selection.isNull():
196 return 218 return
197 219
198 # The grabbed region is everything which is covered by the drawn 220 # The grabbed region is everything which is covered by the drawn
199 # rectangles (border included). This means that there is no 0px 221 # rectangles (border included). This means that there is no 0px
200 # selection, since a 0px wide rectangle will always be drawn as a line. 222 # selection, since a 0px wide rectangle will always be drawn as a line.
201 txt = "{0}, {1} ({2} x {3})".format( 223 txt = "{0}, {1} ({2} x {3})".format(
202 self.__locale.toString(self.__selection.x()), 224 self.__locale.toString(self.__selection.x()),
203 self.__locale.toString(self.__selection.y()), 225 self.__locale.toString(self.__selection.y()),
204 self.__locale.toString(self.__selection.width()), 226 self.__locale.toString(self.__selection.width()),
205 self.__locale.toString(self.__selection.height()) 227 self.__locale.toString(self.__selection.height()),
206 ) 228 )
207 textRect = painter.boundingRect(self.rect(), 229 textRect = painter.boundingRect(self.rect(), Qt.AlignmentFlag.AlignLeft, txt)
208 Qt.AlignmentFlag.AlignLeft, txt)
209 boundingRect = textRect.adjusted(-4, 0, 0, 0) 230 boundingRect = textRect.adjusted(-4, 0, 0, 0)
210 231
211 if ( 232 if (
212 textRect.width() < r.width() - 2 * self.__handleSize and 233 textRect.width() < r.width() - 2 * self.__handleSize
213 textRect.height() < r.height() - 2 * self.__handleSize and 234 and textRect.height() < r.height() - 2 * self.__handleSize
214 r.width() > 100 and 235 and r.width() > 100
215 r.height() > 100 236 and r.height() > 100
216 ): 237 ):
217 # center, unsuitable for small selections 238 # center, unsuitable for small selections
218 boundingRect.moveCenter(r.center()) 239 boundingRect.moveCenter(r.center())
219 textRect.moveCenter(r.center()) 240 textRect.moveCenter(r.center())
220 elif ( 241 elif (
221 r.y() - 3 > textRect.height() and 242 r.y() - 3 > textRect.height()
222 r.x() + textRect.width() < self.rect().width() 243 and r.x() + textRect.width() < self.rect().width()
223 ): 244 ):
224 # on top, left aligned 245 # on top, left aligned
225 boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3)) 246 boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3))
226 textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3)) 247 textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3))
227 elif r.x() - 3 > textRect.width(): 248 elif r.x() - 3 > textRect.width():
228 # left, top aligned 249 # left, top aligned
229 boundingRect.moveTopRight(QPoint(r.x() - 3, r.y())) 250 boundingRect.moveTopRight(QPoint(r.x() - 3, r.y()))
230 textRect.moveTopRight(QPoint(r.x() - 5, r.y())) 251 textRect.moveTopRight(QPoint(r.x() - 5, r.y()))
231 elif ( 252 elif (
232 r.bottom() + 3 + textRect.height() < self.rect().bottom() and 253 r.bottom() + 3 + textRect.height() < self.rect().bottom()
233 r.right() > textRect.width() 254 and r.right() > textRect.width()
234 ): 255 ):
235 # at bottom, right aligned 256 # at bottom, right aligned
236 boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3)) 257 boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3))
237 textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3)) 258 textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3))
238 elif r.right() + textRect.width() + 3 < self.rect().width(): 259 elif r.right() + textRect.width() + 3 < self.rect().width():
239 # right, bottom aligned 260 # right, bottom aligned
240 boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom())) 261 boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom()))
241 textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom())) 262 textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom()))
242 263
243 # If the above didn't catch it, you are running on a very 264 # If the above didn't catch it, you are running on a very
244 # tiny screen... 265 # tiny screen...
245 drawRect(painter, boundingRect, textColor, textBackgroundColor) 266 drawRect(painter, boundingRect, textColor, textBackgroundColor)
246 painter.drawText(textRect, Qt.AlignmentFlag.AlignHCenter, txt) 267 painter.drawText(textRect, Qt.AlignmentFlag.AlignHCenter, txt)
247 268
248 if ( 269 if (
249 (r.height() > self.__handleSize * 2 and 270 r.height() > self.__handleSize * 2 and r.width() > self.__handleSize * 2
250 r.width() > self.__handleSize * 2) or 271 ) or not self.__mouseDown:
251 not self.__mouseDown
252 ):
253 self.__updateHandles() 272 self.__updateHandles()
254 painter.setPen(Qt.PenStyle.NoPen) 273 painter.setPen(Qt.PenStyle.NoPen)
255 painter.setBrush(handleColor) 274 painter.setBrush(handleColor)
256 painter.setClipRegion( 275 painter.setClipRegion(self.__handleMask(SnapshotRegionGrabber.StrokeMask))
257 self.__handleMask(SnapshotRegionGrabber.StrokeMask))
258 painter.drawRect(self.rect()) 276 painter.drawRect(self.rect())
259 handleColor.setAlpha(60) 277 handleColor.setAlpha(60)
260 painter.setBrush(handleColor) 278 painter.setBrush(handleColor)
261 painter.setClipRegion( 279 painter.setClipRegion(self.__handleMask(SnapshotRegionGrabber.FillMask))
262 self.__handleMask(SnapshotRegionGrabber.FillMask))
263 painter.drawRect(self.rect()) 280 painter.drawRect(self.rect())
264 281
265 def resizeEvent(self, evt): 282 def resizeEvent(self, evt):
266 """ 283 """
267 Protected method to handle resize events. 284 Protected method to handle resize events.
268 285
269 @param evt resize event (QResizeEvent) 286 @param evt resize event (QResizeEvent)
270 """ 287 """
271 if self.__selection.isNull(): 288 if self.__selection.isNull():
272 return 289 return
273 290
274 r = QRect(self.__selection) 291 r = QRect(self.__selection)
275 r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect())) 292 r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect()))
276 r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect())) 293 r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect()))
277 if r.width() <= 1 or r.height() <= 1: 294 if r.width() <= 1 or r.height() <= 1:
278 # This just results in ugly drawing... 295 # This just results in ugly drawing...
279 self.__selection = QRect() 296 self.__selection = QRect()
280 else: 297 else:
281 self.__selection = self.__normalizeSelection(r) 298 self.__selection = self.__normalizeSelection(r)
282 299
283 def mousePressEvent(self, evt): 300 def mousePressEvent(self, evt):
284 """ 301 """
285 Protected method to handle mouse button presses. 302 Protected method to handle mouse button presses.
286 303
287 @param evt mouse press event (QMouseEvent) 304 @param evt mouse press event (QMouseEvent)
288 """ 305 """
289 self.__showHelp = not self.__helpTextRect.contains( 306 self.__showHelp = not self.__helpTextRect.contains(evt.position().toPoint())
290 evt.position().toPoint())
291 if evt.button() == Qt.MouseButton.LeftButton: 307 if evt.button() == Qt.MouseButton.LeftButton:
292 self.__mouseDown = True 308 self.__mouseDown = True
293 self.__dragStartPoint = evt.position().toPoint() 309 self.__dragStartPoint = evt.position().toPoint()
294 self.__selectionBeforeDrag = QRect(self.__selection) 310 self.__selectionBeforeDrag = QRect(self.__selection)
295 if not self.__selection.contains(evt.position().toPoint()): 311 if not self.__selection.contains(evt.position().toPoint()):
300 elif evt.button() == Qt.MouseButton.RightButton: 316 elif evt.button() == Qt.MouseButton.RightButton:
301 self.__newSelection = False 317 self.__newSelection = False
302 self.__selection = QRect() 318 self.__selection = QRect()
303 self.setCursor(Qt.CursorShape.CrossCursor) 319 self.setCursor(Qt.CursorShape.CrossCursor)
304 self.update() 320 self.update()
305 321
306 def mouseMoveEvent(self, evt): 322 def mouseMoveEvent(self, evt):
307 """ 323 """
308 Protected method to handle mouse movements. 324 Protected method to handle mouse movements.
309 325
310 @param evt mouse move event (QMouseEvent) 326 @param evt mouse move event (QMouseEvent)
311 """ 327 """
312 shouldShowHelp = not self.__helpTextRect.contains( 328 shouldShowHelp = not self.__helpTextRect.contains(evt.position().toPoint())
313 evt.position().toPoint())
314 if shouldShowHelp != self.__showHelp: 329 if shouldShowHelp != self.__showHelp:
315 self.__showHelp = shouldShowHelp 330 self.__showHelp = shouldShowHelp
316 self.update() 331 self.update()
317 332
318 if self.__mouseDown: 333 if self.__mouseDown:
319 if self.__newSelection: 334 if self.__newSelection:
320 p = evt.position().toPoint() 335 p = evt.position().toPoint()
321 r = self.rect() 336 r = self.rect()
322 self.__selection = self.__normalizeSelection( 337 self.__selection = self.__normalizeSelection(
323 QRect(self.__dragStartPoint, 338 QRect(self.__dragStartPoint, self.__limitPointToRect(p, r))
324 self.__limitPointToRect(p, r))) 339 )
325 elif self.__mouseOverHandle is None: 340 elif self.__mouseOverHandle is None:
326 # moving the whole selection 341 # moving the whole selection
327 r = self.rect().normalized() 342 r = self.rect().normalized()
328 s = self.__selectionBeforeDrag.normalized() 343 s = self.__selectionBeforeDrag.normalized()
329 p = ( 344 p = s.topLeft() + evt.position().toPoint() - self.__dragStartPoint
330 s.topLeft() + evt.position().toPoint() - 345 r.setBottomRight(
331 self.__dragStartPoint 346 r.bottomRight() - QPoint(s.width(), s.height()) + QPoint(1, 1)
332 ) 347 )
333 r.setBottomRight(
334 r.bottomRight() - QPoint(s.width(), s.height()) +
335 QPoint(1, 1))
336 if not r.isNull() and r.isValid(): 348 if not r.isNull() and r.isValid():
337 self.__selection.moveTo(self.__limitPointToRect(p, r)) 349 self.__selection.moveTo(self.__limitPointToRect(p, r))
338 else: 350 else:
339 # dragging a handle 351 # dragging a handle
340 r = QRect(self.__selectionBeforeDrag) 352 r = QRect(self.__selectionBeforeDrag)
341 offset = evt.position().toPoint() - self.__dragStartPoint 353 offset = evt.position().toPoint() - self.__dragStartPoint
342 354
343 if self.__mouseOverHandle in [ 355 if self.__mouseOverHandle in [
344 self.__TLHandle, self.__THandle, self.__TRHandle]: 356 self.__TLHandle,
357 self.__THandle,
358 self.__TRHandle,
359 ]:
345 r.setTop(r.top() + offset.y()) 360 r.setTop(r.top() + offset.y())
346 361
347 if self.__mouseOverHandle in [ 362 if self.__mouseOverHandle in [
348 self.__TLHandle, self.__LHandle, self.__BLHandle]: 363 self.__TLHandle,
364 self.__LHandle,
365 self.__BLHandle,
366 ]:
349 r.setLeft(r.left() + offset.x()) 367 r.setLeft(r.left() + offset.x())
350 368
351 if self.__mouseOverHandle in [ 369 if self.__mouseOverHandle in [
352 self.__BLHandle, self.__BHandle, self.__BRHandle]: 370 self.__BLHandle,
371 self.__BHandle,
372 self.__BRHandle,
373 ]:
353 r.setBottom(r.bottom() + offset.y()) 374 r.setBottom(r.bottom() + offset.y())
354 375
355 if self.__mouseOverHandle in [ 376 if self.__mouseOverHandle in [
356 self.__TRHandle, self.__RHandle, self.__BRHandle]: 377 self.__TRHandle,
378 self.__RHandle,
379 self.__BRHandle,
380 ]:
357 r.setRight(r.right() + offset.x()) 381 r.setRight(r.right() + offset.x())
358 382
359 r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect())) 383 r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect()))
360 r.setBottomRight( 384 r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect()))
361 self.__limitPointToRect(r.bottomRight(), self.rect()))
362 self.__selection = self.__normalizeSelection(r) 385 self.__selection = self.__normalizeSelection(r)
363 386
364 self.update() 387 self.update()
365 else: 388 else:
366 if self.__selection.isNull(): 389 if self.__selection.isNull():
367 return 390 return
368 391
369 found = False 392 found = False
370 for r in self.__handles: 393 for r in self.__handles:
371 if r.contains(evt.position().toPoint()): 394 if r.contains(evt.position().toPoint()):
372 self.__mouseOverHandle = r 395 self.__mouseOverHandle = r
373 found = True 396 found = True
374 break 397 break
375 398
376 if not found: 399 if not found:
377 self.__mouseOverHandle = None 400 self.__mouseOverHandle = None
378 if self.__selection.contains(evt.position().toPoint()): 401 if self.__selection.contains(evt.position().toPoint()):
379 self.setCursor(Qt.CursorShape.OpenHandCursor) 402 self.setCursor(Qt.CursorShape.OpenHandCursor)
380 else: 403 else:
381 self.setCursor(Qt.CursorShape.CrossCursor) 404 self.setCursor(Qt.CursorShape.CrossCursor)
382 else: 405 else:
383 if self.__mouseOverHandle in [self.__TLHandle, 406 if self.__mouseOverHandle in [self.__TLHandle, self.__BRHandle]:
384 self.__BRHandle]:
385 self.setCursor(Qt.CursorShape.SizeFDiagCursor) 407 self.setCursor(Qt.CursorShape.SizeFDiagCursor)
386 elif self.__mouseOverHandle in [self.__TRHandle, 408 elif self.__mouseOverHandle in [self.__TRHandle, self.__BLHandle]:
387 self.__BLHandle]:
388 self.setCursor(Qt.CursorShape.SizeBDiagCursor) 409 self.setCursor(Qt.CursorShape.SizeBDiagCursor)
389 elif self.__mouseOverHandle in [self.__LHandle, 410 elif self.__mouseOverHandle in [self.__LHandle, self.__RHandle]:
390 self.__RHandle]:
391 self.setCursor(Qt.CursorShape.SizeHorCursor) 411 self.setCursor(Qt.CursorShape.SizeHorCursor)
392 elif self.__mouseOverHandle in [self.__THandle, 412 elif self.__mouseOverHandle in [self.__THandle, self.__BHandle]:
393 self.__BHandle]:
394 self.setCursor(Qt.CursorShape.SizeVerCursor) 413 self.setCursor(Qt.CursorShape.SizeVerCursor)
395 414
396 def mouseReleaseEvent(self, evt): 415 def mouseReleaseEvent(self, evt):
397 """ 416 """
398 Protected method to handle mouse button releases. 417 Protected method to handle mouse button releases.
399 418
400 @param evt mouse release event (QMouseEvent) 419 @param evt mouse release event (QMouseEvent)
401 """ 420 """
402 self.__mouseDown = False 421 self.__mouseDown = False
403 self.__newSelection = False 422 self.__newSelection = False
404 if ( 423 if self.__mouseOverHandle is None and self.__selection.contains(
405 self.__mouseOverHandle is None and 424 evt.position().toPoint()
406 self.__selection.contains(evt.position().toPoint())
407 ): 425 ):
408 self.setCursor(Qt.CursorShape.OpenHandCursor) 426 self.setCursor(Qt.CursorShape.OpenHandCursor)
409 self.update() 427 self.update()
410 428
411 def mouseDoubleClickEvent(self, evt): 429 def mouseDoubleClickEvent(self, evt):
412 """ 430 """
413 Protected method to handle mouse double clicks. 431 Protected method to handle mouse double clicks.
414 432
415 @param evt mouse double click event (QMouseEvent) 433 @param evt mouse double click event (QMouseEvent)
416 """ 434 """
417 self.__grabRect() 435 self.__grabRect()
418 436
419 def keyPressEvent(self, evt): 437 def keyPressEvent(self, evt):
420 """ 438 """
421 Protected method to handle key presses. 439 Protected method to handle key presses.
422 440
423 @param evt key press event (QKeyEvent) 441 @param evt key press event (QKeyEvent)
424 """ 442 """
425 if evt.key() == Qt.Key.Key_Escape: 443 if evt.key() == Qt.Key.Key_Escape:
426 self.grabbed.emit(QPixmap()) 444 self.grabbed.emit(QPixmap())
427 elif evt.key() in [Qt.Key.Key_Enter, Qt.Key.Key_Return]: 445 elif evt.key() in [Qt.Key.Key_Enter, Qt.Key.Key_Return]:
428 self.__grabRect() 446 self.__grabRect()
429 else: 447 else:
430 evt.ignore() 448 evt.ignore()
431 449
432 def __updateHandles(self): 450 def __updateHandles(self):
433 """ 451 """
434 Private method to update the handles. 452 Private method to update the handles.
435 """ 453 """
436 r = QRect(self.__selection) 454 r = QRect(self.__selection)
437 s2 = self.__handleSize // 2 455 s2 = self.__handleSize // 2
438 456
439 self.__TLHandle.moveTopLeft(r.topLeft()) 457 self.__TLHandle.moveTopLeft(r.topLeft())
440 self.__TRHandle.moveTopRight(r.topRight()) 458 self.__TRHandle.moveTopRight(r.topRight())
441 self.__BLHandle.moveBottomLeft(r.bottomLeft()) 459 self.__BLHandle.moveBottomLeft(r.bottomLeft())
442 self.__BRHandle.moveBottomRight(r.bottomRight()) 460 self.__BRHandle.moveBottomRight(r.bottomRight())
443 461
444 self.__LHandle.moveTopLeft( 462 self.__LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() // 2 - s2))
445 QPoint(r.x(), r.y() + r.height() // 2 - s2)) 463 self.__THandle.moveTopLeft(QPoint(r.x() + r.width() // 2 - s2, r.y()))
446 self.__THandle.moveTopLeft( 464 self.__RHandle.moveTopRight(QPoint(r.right(), r.y() + r.height() // 2 - s2))
447 QPoint(r.x() + r.width() // 2 - s2, r.y())) 465 self.__BHandle.moveBottomLeft(QPoint(r.x() + r.width() // 2 - s2, r.bottom()))
448 self.__RHandle.moveTopRight( 466
449 QPoint(r.right(), r.y() + r.height() // 2 - s2))
450 self.__BHandle.moveBottomLeft(
451 QPoint(r.x() + r.width() // 2 - s2, r.bottom()))
452
453 def __handleMask(self, maskType): 467 def __handleMask(self, maskType):
454 """ 468 """
455 Private method to calculate the handle mask. 469 Private method to calculate the handle mask.
456 470
457 @param maskType type of the mask to be used 471 @param maskType type of the mask to be used
458 (SnapshotRegionGrabber.FillMask or 472 (SnapshotRegionGrabber.FillMask or
459 SnapshotRegionGrabber.StrokeMask) 473 SnapshotRegionGrabber.StrokeMask)
460 @return calculated mask (QRegion) 474 @return calculated mask (QRegion)
461 """ 475 """
465 r = QRegion(rect) 479 r = QRegion(rect)
466 mask += r.subtracted(QRegion(rect.adjusted(1, 1, -1, -1))) 480 mask += r.subtracted(QRegion(rect.adjusted(1, 1, -1, -1)))
467 else: 481 else:
468 mask += QRegion(rect.adjusted(1, 1, -1, -1)) 482 mask += QRegion(rect.adjusted(1, 1, -1, -1))
469 return mask 483 return mask
470 484
471 def __limitPointToRect(self, point, rect): 485 def __limitPointToRect(self, point, rect):
472 """ 486 """
473 Private method to limit the given point to the given rectangle. 487 Private method to limit the given point to the given rectangle.
474 488
475 @param point point to be limited (QPoint) 489 @param point point to be limited (QPoint)
476 @param rect rectangle the point shall be limited to (QRect) 490 @param rect rectangle the point shall be limited to (QRect)
477 @return limited point (QPoint) 491 @return limited point (QPoint)
478 """ 492 """
479 q = QPoint() 493 q = QPoint()
488 elif point.y() < rect.bottom(): 502 elif point.y() < rect.bottom():
489 q.setY(point.y()) 503 q.setY(point.y())
490 else: 504 else:
491 q.setY(rect.bottom()) 505 q.setY(rect.bottom())
492 return q 506 return q
493 507
494 def __normalizeSelection(self, sel): 508 def __normalizeSelection(self, sel):
495 """ 509 """
496 Private method to normalize the given selection. 510 Private method to normalize the given selection.
497 511
498 @param sel selection to be normalized (QRect) 512 @param sel selection to be normalized (QRect)
499 @return normalized selection (QRect) 513 @return normalized selection (QRect)
500 """ 514 """
501 rect = QRect(sel) 515 rect = QRect(sel)
502 if rect.width() <= 0: 516 if rect.width() <= 0:
508 top = rect.top() 522 top = rect.top()
509 height = rect.height() 523 height = rect.height()
510 rect.setTop(top + height - 1) 524 rect.setTop(top + height - 1)
511 rect.setBottom(top) 525 rect.setBottom(top)
512 return rect 526 return rect
513 527
514 def __grabRect(self): 528 def __grabRect(self):
515 """ 529 """
516 Private method to grab the selected rectangle (i.e. do the snapshot). 530 Private method to grab the selected rectangle (i.e. do the snapshot).
517 """ 531 """
518 if self.__mode == SnapshotRegionGrabber.Ellipse: 532 if self.__mode == SnapshotRegionGrabber.Ellipse:
519 ell = QRegion(self.__selection, QRegion.RegionType.Ellipse) 533 ell = QRegion(self.__selection, QRegion.RegionType.Ellipse)
520 if not ell.isEmpty(): 534 if not ell.isEmpty():
521 self.__grabbing = True 535 self.__grabbing = True
522 536
523 xOffset = self.__pixmap.rect().x() - ell.boundingRect().x() 537 xOffset = self.__pixmap.rect().x() - ell.boundingRect().x()
524 yOffset = self.__pixmap.rect().y() - ell.boundingRect().y() 538 yOffset = self.__pixmap.rect().y() - ell.boundingRect().y()
525 translatedEll = ell.translated(xOffset, yOffset) 539 translatedEll = ell.translated(xOffset, yOffset)
526 540
527 pixmap2 = QPixmap(ell.boundingRect().size()) 541 pixmap2 = QPixmap(ell.boundingRect().size())
528 pixmap2.fill(Qt.GlobalColor.transparent) 542 pixmap2.fill(Qt.GlobalColor.transparent)
529 543
530 pt = QPainter() 544 pt = QPainter()
531 pt.begin(pixmap2) 545 pt.begin(pixmap2)
532 if pt.paintEngine().hasFeature( 546 if pt.paintEngine().hasFeature(
533 QPaintEngine.PaintEngineFeature.PorterDuff 547 QPaintEngine.PaintEngineFeature.PorterDuff
534 ): 548 ):
535 pt.setRenderHints( 549 pt.setRenderHints(
536 QPainter.RenderHint.Antialiasing | 550 QPainter.RenderHint.Antialiasing
537 QPainter.RenderHint.SmoothPixmapTransform, 551 | QPainter.RenderHint.SmoothPixmapTransform,
538 True) 552 True,
553 )
539 pt.setBrush(Qt.GlobalColor.black) 554 pt.setBrush(Qt.GlobalColor.black)
540 pt.setPen(QPen(QBrush(Qt.GlobalColor.black), 0.5)) 555 pt.setPen(QPen(QBrush(Qt.GlobalColor.black), 0.5))
541 pt.drawEllipse(translatedEll.boundingRect()) 556 pt.drawEllipse(translatedEll.boundingRect())
542 pt.setCompositionMode( 557 pt.setCompositionMode(
543 QPainter.CompositionMode.CompositionMode_SourceIn) 558 QPainter.CompositionMode.CompositionMode_SourceIn
559 )
544 else: 560 else:
545 pt.setClipRegion(translatedEll) 561 pt.setClipRegion(translatedEll)
546 pt.setCompositionMode( 562 pt.setCompositionMode(
547 QPainter.CompositionMode.CompositionMode_Source) 563 QPainter.CompositionMode.CompositionMode_Source
548 564 )
549 pt.drawPixmap(pixmap2.rect(), self.__pixmap, 565
550 ell.boundingRect()) 566 pt.drawPixmap(pixmap2.rect(), self.__pixmap, ell.boundingRect())
551 pt.end() 567 pt.end()
552 568
553 self.grabbed.emit(pixmap2) 569 self.grabbed.emit(pixmap2)
554 else: 570 else:
555 r = QRect(self.__selection) 571 r = QRect(self.__selection)
556 if not r.isNull() and r.isValid(): 572 if not r.isNull() and r.isValid():
557 self.__grabbing = True 573 self.__grabbing = True

eric ide

mercurial