Fri, 20 Jan 2023 16:14:05 +0100
Undid part of change 8956a005f478 because it was not the real issue.
9704 | 1 | # -*- coding: utf-8 -*- |
2 | ||
3 | # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> | |
4 | # | |
5 | ||
6 | """ | |
7 | Module implementing a specialized PDF view class. | |
8 | """ | |
9 | ||
9707 | 10 | import collections |
11 | import enum | |
12 | ||
13 | from dataclasses import dataclass | |
14 | ||
15 | from PyQt6.QtCore import ( | |
16 | QSize, Qt, pyqtSlot, QEvent, QSizeF, QRect, QPoint, QPointF, QRectF, | |
17 | pyqtSignal | |
18 | ) | |
19 | from PyQt6.QtGui import QGuiApplication, QPainter, QColor, QPen | |
20 | from PyQt6.QtPdf import QPdfDocument, QPdfLink | |
9704 | 21 | from PyQt6.QtPdfWidgets import QPdfView |
9707 | 22 | from PyQt6.QtWidgets import QRubberBand |
9704 | 23 | |
24 | from .PdfZoomSelector import PdfZoomSelector | |
25 | ||
26 | ||
9707 | 27 | class PdfMarkerType(enum.Enum): |
28 | """ | |
29 | Class defining the various marker types. | |
30 | """ | |
31 | ||
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
32 | SEARCHRESULT = 0 |
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
33 | SELECTION = 1 |
9707 | 34 | |
35 | ||
36 | @dataclass | |
37 | class PdfMarker: | |
38 | """ | |
39 | Class defining the data structure for markers. | |
40 | """ | |
41 | ||
42 | rectangle: QRectF | |
43 | markerType: PdfMarkerType | |
44 | ||
45 | ||
46 | @dataclass | |
47 | class PdfMarkerGeometry: | |
48 | """ | |
49 | Class defining the data structure for marker geometries. | |
50 | """ | |
51 | ||
52 | rectangle: QRect | |
53 | markerType: PdfMarkerType | |
54 | ||
55 | ||
9704 | 56 | class PdfView(QPdfView): |
57 | """ | |
58 | Class implementing a specialized PDF view. | |
59 | """ | |
60 | ||
9707 | 61 | MarkerColors = { |
62 | # merker type: (pen color, brush color) | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
63 | PdfMarkerType.SEARCHRESULT: (QColor(255, 200, 0, 255), QColor(255, 200, 0, 64)), |
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
64 | PdfMarkerType.SELECTION: (QColor(0, 0, 255, 255), QColor(0, 0, 255, 64)), |
9707 | 65 | } |
66 | ||
67 | selectionAvailable = pyqtSignal(bool) | |
68 | ||
9704 | 69 | def __init__(self, parent): |
70 | """ | |
71 | Constructor | |
72 | ||
73 | @param parent reference to the parent widget | |
74 | @type QWidget | |
75 | """ | |
76 | super().__init__(parent) | |
77 | ||
78 | self.__screenResolution = ( | |
79 | QGuiApplication.primaryScreen().logicalDotsPerInch() / 72.0 | |
80 | ) | |
81 | ||
9707 | 82 | self.__documentViewport = QRect() |
83 | self.__documentSize = QSize() | |
84 | self.__pageGeometries = {} | |
85 | self.__markers = collections.defaultdict(list) | |
86 | self.__markerGeometries = collections.defaultdict(list) | |
87 | self.__rubberBand = None | |
88 | ||
89 | self.pageModeChanged.connect(self.__calculateDocumentLayout) | |
90 | self.zoomModeChanged.connect(self.__calculateDocumentLayout) | |
91 | self.zoomFactorChanged.connect(self.__calculateDocumentLayout) | |
92 | self.pageSpacingChanged.connect(self.__calculateDocumentLayout) | |
93 | self.documentMarginsChanged.connect(self.__calculateDocumentLayout) | |
94 | ||
95 | self.pageNavigator().currentPageChanged.connect(self.__currentPageChanged) | |
96 | ||
9704 | 97 | self.grabGesture(Qt.GestureType.PinchGesture) |
98 | ||
9707 | 99 | def setDocument(self, document): |
100 | """ | |
101 | Public method to set the PDF document. | |
102 | ||
103 | @param document reference to the PDF document object | |
104 | @type QPdfDocument | |
105 | """ | |
106 | super().setDocument(document) | |
107 | ||
108 | document.statusChanged.connect(self.__calculateDocumentLayout) | |
109 | ||
9704 | 110 | def __zoomInOut(self, zoomIn): |
111 | """ | |
112 | Private method to zoom into or out of the view. | |
113 | ||
114 | @param zoomIn flag indicating to zoom into the view | |
115 | @type bool | |
116 | """ | |
117 | zoomFactor = self.__zoomFactorForMode(self.zoomMode()) | |
118 | ||
119 | factors = list(PdfZoomSelector.ZoomValues) | |
120 | factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView)) | |
121 | factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth)) | |
122 | if zoomIn: | |
123 | factors.sort() | |
124 | if zoomFactor >= factors[-1]: | |
125 | return | |
126 | newIndex = next(x for x, val in enumerate(factors) if val > zoomFactor) | |
127 | else: | |
128 | factors.sort(reverse=True) | |
129 | if zoomFactor <= factors[-1]: | |
130 | return | |
131 | newIndex = next(x for x, val in enumerate(factors) if val < zoomFactor) | |
132 | newFactor = factors[newIndex] | |
133 | if newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView): | |
134 | self.setZoomMode(QPdfView.ZoomMode.FitInView) | |
135 | self.zoomModeChanged.emit(QPdfView.ZoomMode.FitInView) | |
136 | elif newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth): | |
137 | self.setZoomMode(QPdfView.ZoomMode.FitToWidth) | |
138 | self.zoomModeChanged.emit(QPdfView.ZoomMode.FitToWidth) | |
139 | else: | |
140 | self.setZoomFactor(newFactor) | |
141 | self.zoomFactorChanged.emit(newFactor) | |
142 | self.setZoomMode(QPdfView.ZoomMode.Custom) | |
143 | self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) | |
144 | ||
145 | def __zoomFactorForMode(self, zoomMode): | |
146 | """ | |
147 | Private method to calculate the zoom factor iaw. the current zoom mode. | |
148 | ||
149 | @param zoomMode zoom mode to get the zoom factor for | |
150 | @type QPdfView.ZoomMode | |
151 | @return zoom factor | |
152 | @rtype float | |
153 | """ | |
9707 | 154 | self.__calculateDocumentViewport() |
155 | ||
9704 | 156 | if zoomMode == QPdfView.ZoomMode.Custom: |
157 | return self.zoomFactor() | |
158 | else: | |
159 | curPage = self.pageNavigator().currentPage() | |
160 | margins = self.documentMargins() | |
161 | if zoomMode == QPdfView.ZoomMode.FitToWidth: | |
162 | pageSize = ( | |
163 | self.document().pagePointSize(curPage) * self.__screenResolution | |
164 | ).toSize() | |
165 | factor = ( | |
9707 | 166 | self.__documentViewport.width() - margins.left() - margins.right() |
9704 | 167 | ) / pageSize.width() |
168 | pageSize *= factor | |
169 | else: | |
170 | # QPdfView.ZoomMode.FitInView | |
9707 | 171 | viewportSize = self.__documentViewport.size() + QSize( |
9704 | 172 | -margins.left() - margins.right(), -self.pageSpacing() |
173 | ) | |
174 | pageSize = ( | |
175 | self.document().pagePointSize(curPage) * self.__screenResolution | |
176 | ).toSize() | |
177 | pageSize = pageSize.scaled( | |
178 | viewportSize, Qt.AspectRatioMode.KeepAspectRatio | |
179 | ) | |
180 | zoomFactor = pageSize.width() / ( | |
181 | self.document().pagePointSize(curPage) * self.__screenResolution | |
182 | ).width() | |
183 | return zoomFactor | |
184 | ||
185 | @pyqtSlot() | |
186 | def zoomIn(self): | |
187 | """ | |
188 | Public slot to zoom into the view. | |
189 | """ | |
190 | self.__zoomInOut(True) | |
191 | ||
192 | @pyqtSlot() | |
193 | def zoomOut(self): | |
194 | """ | |
195 | Public slot to zoom out of the view. | |
196 | """ | |
197 | self.__zoomInOut(False) | |
198 | ||
199 | @pyqtSlot() | |
200 | def zoomReset(self): | |
201 | """ | |
202 | Public slot to reset the zoom factor of the view. | |
203 | """ | |
204 | if self.zoomMode() != QPdfView.ZoomMode.Custom or self.zoomFactor() != 1.0: | |
205 | self.setZoomFactor(1.0) | |
206 | self.zoomFactorChanged.emit(1.0) | |
207 | self.setZoomMode(QPdfView.ZoomMode.Custom) | |
208 | self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) | |
209 | ||
210 | def wheelEvent(self, evt): | |
211 | """ | |
212 | Protected method to handle wheel events. | |
213 | ||
214 | @param evt reference to the wheel event | |
215 | @type QWheelEvent | |
216 | """ | |
217 | delta = evt.angleDelta().y() | |
218 | if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: | |
219 | if delta < 0: | |
220 | self.zoomOut() | |
221 | elif delta > 0: | |
222 | self.zoomIn() | |
223 | evt.accept() | |
224 | return | |
225 | ||
226 | elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: | |
227 | if delta < 0: | |
228 | self.pageNavigator().back() | |
229 | elif delta > 0: | |
230 | self.pageNavigator().forward() | |
231 | evt.accept() | |
232 | return | |
233 | ||
234 | super().wheelEvent(evt) | |
235 | ||
9707 | 236 | def keyPressEvent(self, evt): |
237 | """ | |
238 | Protected method handling key press events. | |
239 | ||
240 | @param evt reference to the key event | |
241 | @type QKeyEvent | |
242 | """ | |
243 | if evt.key() == Qt.Key.Key_Escape: | |
244 | self.clearSelection() | |
245 | ||
246 | def mousePressEvent(self, evt): | |
247 | """ | |
248 | Protected method to handle mouse press events. | |
249 | ||
250 | @param evt reference to the mouse event | |
251 | @type QMouseEvent | |
252 | """ | |
253 | if evt.button() == Qt.MouseButton.LeftButton: | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
254 | self.clearMarkers(PdfMarkerType.SELECTION) |
9707 | 255 | self.selectionAvailable.emit(False) |
256 | ||
257 | self.__rubberBandOrigin = evt.pos() | |
258 | if self.__rubberBand is None: | |
259 | self.__rubberBand = QRubberBand( | |
260 | QRubberBand.Shape.Rectangle, self.viewport() | |
261 | ) | |
262 | self.__rubberBand.setGeometry(QRect(self.__rubberBandOrigin, QSize())) | |
263 | self.__rubberBand.show() | |
264 | ||
265 | super().mousePressEvent(evt) | |
266 | ||
267 | def mouseMoveEvent(self, evt): | |
268 | """ | |
269 | Protected method to handle mouse move events. | |
270 | ||
271 | @param evt reference to the mouse event | |
272 | @type QMouseEvent | |
273 | """ | |
274 | if evt.buttons() & Qt.MouseButton.LeftButton: | |
275 | self.__rubberBand.setGeometry( | |
276 | QRect(self.__rubberBandOrigin, evt.pos()).normalized() | |
277 | ) | |
278 | ||
279 | super().mousePressEvent(evt) | |
280 | ||
281 | def mouseReleaseEvent(self, evt): | |
282 | """ | |
283 | Protected method to handle mouse release events. | |
284 | ||
285 | @param evt reference to the mouse event | |
286 | @type QMouseEvent | |
287 | """ | |
288 | if evt.button() == Qt.MouseButton.LeftButton: | |
289 | self.__rubberBand.hide() | |
290 | translatedRubber = self.__rubberBand.geometry().translated( | |
291 | self.__documentViewport.topLeft() | |
292 | ) | |
293 | for page in self.__pageGeometries: | |
294 | if self.__pageGeometries[page].intersects(translatedRubber): | |
295 | translatedRubber = translatedRubber.translated( | |
296 | -self.__pageGeometries[page].topLeft() | |
297 | ) | |
298 | factor = self.__zoomFactorForMode(self.zoomMode()) | |
299 | selectionSize = ( | |
300 | QSizeF(translatedRubber.size()) / factor | |
301 | / self.__screenResolution | |
302 | ) | |
303 | selectionTopLeft = ( | |
304 | QPointF(translatedRubber.topLeft()) / factor | |
305 | / self.__screenResolution | |
306 | ) | |
307 | selectionRect = QRectF(selectionTopLeft, selectionSize) | |
308 | selection = self.document().getSelection( | |
309 | page, selectionRect.topLeft(), selectionRect.bottomRight() | |
310 | ) | |
311 | if selection.isValid(): | |
312 | for bound in selection.bounds(): | |
313 | self.addMarker( | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
314 | page, bound.boundingRect(), PdfMarkerType.SELECTION |
9707 | 315 | ) |
316 | self.selectionAvailable.emit(True) | |
317 | ||
318 | super().mousePressEvent(evt) | |
319 | ||
9704 | 320 | def event(self, evt): |
321 | """ | |
322 | Public method handling events. | |
323 | ||
324 | @param evt reference to the event | |
325 | @type QEvent | |
326 | @return flag indicating, if the event was handled | |
327 | @rtype bool | |
328 | """ | |
329 | if evt.type() == QEvent.Type.Gesture: | |
330 | self.gestureEvent(evt) | |
331 | return True | |
332 | ||
333 | return super().event(evt) | |
334 | ||
335 | def gestureEvent(self, evt): | |
336 | """ | |
337 | Protected method handling gesture events. | |
338 | ||
339 | @param evt reference to the gesture event | |
340 | @type QGestureEvent | |
341 | """ | |
342 | pinch = evt.gesture(Qt.GestureType.PinchGesture) | |
343 | if pinch: | |
344 | if pinch.state() == Qt.GestureState.GestureStarted: | |
345 | pinch.setTotalScaleFactor(self.__zoomFactorForMode(self.zoomMode())) | |
346 | elif pinch.state() == Qt.GestureState.GestureUpdated: | |
347 | if self.zoomMode() != QPdfView.ZoomMode.Custom: | |
348 | self.setZoomMode(QPdfView.ZoomMode.Custom) | |
349 | self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) | |
350 | zoomFactor = pinch.totalScaleFactor() | |
351 | self.setZoomFactor(zoomFactor) | |
352 | self.zoomFactorChanged.emit(zoomFactor) | |
353 | evt.accept() | |
9707 | 354 | |
355 | def resizeEvent(self, evt): | |
356 | """ | |
357 | Protected method to handle a widget resize. | |
358 | ||
359 | @param evt reference to the resize event | |
360 | @type QResizeEvent | |
361 | """ | |
362 | super().resizeEvent(evt) | |
363 | ||
364 | self.__calculateDocumentViewport() | |
365 | ||
366 | def paintEvent(self, evt): | |
367 | """ | |
368 | Protected method to paint the view. | |
369 | ||
370 | This event handler calls the original paint event handler of the super class | |
371 | and paints the markers on top of the result. | |
372 | ||
373 | @param evt reference to the paint event | |
374 | @type QPaintEvent | |
375 | """ | |
376 | super().paintEvent(evt) | |
377 | ||
378 | painter = QPainter(self.viewport()) | |
379 | painter.translate(-self.__documentViewport.x(), -self.__documentViewport.y()) | |
380 | for page in self.__markerGeometries: | |
381 | for markerGeom in self.__markerGeometries[page]: | |
382 | if markerGeom.rectangle.intersects(self.__documentViewport): | |
383 | painter.setPen(QPen( | |
384 | PdfView.MarkerColors[markerGeom.markerType][0], 2 | |
385 | )) | |
386 | painter.setBrush(PdfView.MarkerColors[markerGeom.markerType][1]) | |
387 | painter.drawRect(markerGeom.rectangle) | |
388 | painter.end() | |
389 | ||
390 | def __calculateDocumentViewport(self): | |
391 | """ | |
392 | Private method to calculate the document viewport. | |
393 | ||
394 | This is a PyQt implementation of the code found in the QPdfView class | |
395 | because it is calculated in a private part and not accessible. | |
396 | """ | |
397 | x = self.horizontalScrollBar().value() | |
398 | y = self.verticalScrollBar().value() | |
399 | width = self.viewport().width() | |
400 | height = self.viewport().height() | |
401 | ||
402 | docViewport = QRect(x, y, width, height) | |
403 | if self.__documentViewport == docViewport: | |
404 | return | |
405 | ||
406 | oldSize = self.__documentViewport.size() | |
407 | ||
408 | self.__documentViewport = docViewport | |
409 | ||
410 | if oldSize != self.__documentViewport.size(): | |
411 | self.__calculateDocumentLayout() | |
412 | ||
413 | @pyqtSlot() | |
414 | def __calculateDocumentLayout(self): | |
415 | """ | |
416 | Private slot to calculate the document layout data. | |
417 | ||
418 | This is a PyQt implementation of the code found in the QPdfView class | |
419 | because it is calculated in a private part and not accessible. | |
420 | """ | |
421 | self.__documentSize = QSize() | |
422 | self.__pageGeometries.clear() | |
423 | self.__markerGeometries.clear() | |
424 | ||
425 | document = self.document() | |
426 | margins = self.documentMargins() | |
427 | ||
428 | if document is None or document.status() != QPdfDocument.Status.Ready: | |
429 | return | |
430 | ||
431 | pageCount = document.pageCount() | |
432 | ||
433 | totalWidth = 0 | |
434 | ||
435 | startPage = ( | |
436 | self.pageNavigator().currentPage() | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
437 | if self.pageMode() == QPdfView.PageMode.SinglePage |
9707 | 438 | else 0 |
439 | ) | |
440 | endPage = ( | |
441 | self.pageNavigator().currentPage() + 1 | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
442 | if self.pageMode() == QPdfView.PageMode.SinglePage |
9707 | 443 | else pageCount |
444 | ) | |
445 | ||
446 | # calculate pageSizes | |
447 | for page in range(startPage, endPage): | |
448 | if self.zoomMode() == QPdfView.ZoomMode.Custom: | |
449 | pageSize = QSizeF( | |
450 | document.pagePointSize(page) * self.__screenResolution | |
451 | * self.zoomFactor() | |
452 | ).toSize() | |
453 | elif self.zoomMode() == QPdfView.ZoomMode.FitToWidth: | |
454 | pageSize = QSizeF( | |
455 | document.pagePointSize(page) * self.__screenResolution | |
456 | ).toSize() | |
457 | factor = ( | |
458 | self.__documentViewport.width() - margins.left() - margins.right() | |
459 | ) / pageSize.width() | |
460 | pageSize *= factor | |
461 | elif self.zoomMode() == QPdfView.ZoomMode.FitInView: | |
462 | viewportSize = ( | |
463 | self.__documentViewport.size() | |
464 | + QSize(-margins.left() - margins.right(), -self.pageSpacing()) | |
465 | ) | |
466 | pageSize = QSizeF( | |
467 | document.pagePointSize(page) * self.__screenResolution | |
468 | ).toSize() | |
469 | pageSize = pageSize.scaled( | |
470 | viewportSize, Qt.AspectRatioMode.KeepAspectRatio | |
471 | ) | |
472 | ||
473 | totalWidth = max(totalWidth, pageSize.width()) | |
474 | ||
475 | self.__pageGeometries[page] = QRect(QPoint(0, 0), pageSize) | |
476 | ||
477 | totalWidth += margins.left() + margins.right() | |
478 | ||
479 | pageY = margins.top() | |
480 | ||
481 | # calculate page positions | |
482 | for page in range(startPage, endPage): | |
483 | pageSize = self.__pageGeometries[page].size() | |
484 | ||
485 | # center horizontally inside the viewport | |
486 | pageX = ( | |
487 | max(totalWidth, self.__documentViewport.width()) - pageSize.width() | |
488 | ) // 2 | |
489 | self.__pageGeometries[page].moveTopLeft(QPoint(pageX, pageY)) | |
490 | ||
491 | self.__calculateMarkerGeometries(page, QPoint(pageX, pageY)) | |
492 | ||
493 | pageY += pageSize.height() + self.pageSpacing() | |
494 | ||
495 | pageY += margins.bottom() | |
496 | ||
497 | self.__documentSize = QSize(totalWidth, pageY) | |
498 | ||
499 | @pyqtSlot() | |
500 | def __currentPageChanged(self): | |
501 | """ | |
502 | Private slot to handle a change of the current page. | |
503 | """ | |
504 | if self.pageMode() == QPdfView.PageMode.SinglePage: | |
505 | self.__calculateDocumentLayout() | |
9708
8956a005f478
Corrected an issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9707
diff
changeset
|
506 | self.update() |
9707 | 507 | |
508 | def __calculateMarkerGeometries(self, page, offset): | |
509 | """ | |
510 | Private method to calculate the marker geometries. | |
511 | ||
512 | @param page page number | |
513 | @type int | |
514 | @param offset page offset | |
515 | @type QPoint or QPointF | |
516 | """ | |
517 | # calculate search marker sizes | |
518 | if page in self.__markers: | |
519 | factor = self.__zoomFactorForMode(self.zoomMode()) | |
520 | for marker in self.__markers[page]: | |
521 | markerSize = ( | |
522 | QSizeF(marker.rectangle.size()) * factor * self.__screenResolution | |
523 | ).toSize() | |
524 | markerTopLeft = ( | |
525 | QPointF(marker.rectangle.topLeft()) * factor | |
526 | * self.__screenResolution | |
527 | ).toPoint() | |
528 | ||
529 | markerGeometry = QRect(markerTopLeft, markerSize) | |
530 | self.__markerGeometries[page].append( | |
531 | PdfMarkerGeometry( | |
532 | rectangle=markerGeometry.translated(offset), | |
533 | markerType=marker.markerType | |
534 | ) | |
535 | ) | |
536 | ||
537 | def scrollContentsBy(self, dx, dy): | |
538 | """ | |
539 | Public method called when the scrollbars are moved. | |
540 | ||
541 | @param dx change of the horizontal scroll bar | |
542 | @type int | |
543 | @param dy change of the vertical scroll bar | |
544 | @type int | |
545 | """ | |
546 | super().scrollContentsBy(dx, dy) | |
547 | ||
548 | self.__calculateDocumentViewport() | |
549 | ||
550 | def __updateView(self): | |
551 | """ | |
552 | Private method to update the view. | |
553 | """ | |
554 | self.__calculateDocumentLayout() | |
555 | self.update() | |
556 | ||
557 | @pyqtSlot(int, QRectF, PdfMarkerType) | |
558 | @pyqtSlot(int, QRect, PdfMarkerType) | |
559 | def addMarker(self, page, rect, markerType): | |
560 | """ | |
561 | Public slot to add a marker. | |
562 | ||
563 | @param page page number for the marker | |
564 | @type int | |
565 | @param rect marker rectangle | |
566 | @type QRect or QRectF | |
567 | @param markerType type of the marker | |
568 | @type PdfMarkerType | |
569 | """ | |
570 | marker = PdfMarker(rectangle=QRectF(rect), markerType=markerType) | |
571 | if marker not in self.__markers[page]: | |
572 | self.__markers[page].append(marker) | |
573 | self.__updateView() | |
574 | ||
575 | @pyqtSlot(PdfMarkerType) | |
576 | def clearMarkers(self, markerType): | |
577 | """ | |
578 | Public slot to clear the markers of a specific type. | |
579 | ||
580 | @param markerType type of the marker | |
581 | @type PdfMarkerType | |
582 | """ | |
583 | markers = collections.defaultdict(list) | |
584 | for page in self.__markers: | |
585 | markersList = [ | |
586 | m for m in self.__markers[page] if m.markerType != markerType | |
587 | ] | |
588 | if markersList: | |
589 | markers[page] = markersList | |
590 | ||
591 | self.__markers = markers | |
592 | self.__updateView() | |
593 | ||
594 | @pyqtSlot() | |
595 | def clearAllMarkers(self): | |
596 | """ | |
597 | Public slot to clear all markers. | |
598 | """ | |
599 | self.__markers.clear() | |
600 | self.__updateView() | |
601 | ||
602 | @pyqtSlot(QPdfLink) | |
603 | def addSearchMarker(self, link): | |
604 | """ | |
605 | Public slot to add a search marker given a PDF link. | |
606 | ||
607 | @param link reference to the PDF link object | |
608 | @type QPdfLink | |
609 | """ | |
610 | for rect in link.rectangles(): | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
611 | self.addMarker(link.page(), rect, PdfMarkerType.SEARCHRESULT) |
9707 | 612 | |
613 | @pyqtSlot() | |
614 | def clearSearchMarkers(self): | |
615 | """ | |
616 | Public slot to clear the search markers. | |
617 | """ | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
618 | self.clearMarkers(PdfMarkerType.SEARCHRESULT) |
9707 | 619 | |
620 | def hasSelection(self): | |
621 | """ | |
622 | Public method to check the presence of a selection. | |
623 | ||
624 | @return flag indicating the presence of a selection | |
625 | @rtype bool | |
626 | """ | |
627 | return any( | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
628 | m.markerType == PdfMarkerType.SELECTION |
9707 | 629 | for p in self.__markers |
630 | for m in self.__markers[p] | |
631 | ) | |
632 | ||
633 | def getSelection(self): | |
634 | """ | |
635 | Public method to get a PDF selection object. | |
636 | ||
637 | @return reference to the PDF selection object | |
638 | @rtype QPdfSelection | |
639 | """ | |
640 | for page in self.__markers: | |
641 | markersList = [ | |
642 | m for m in self.__markers[page] | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
643 | if m.markerType == PdfMarkerType.SELECTION |
9707 | 644 | ] |
645 | if markersList: | |
646 | selection = self.document().getSelection( | |
647 | page, | |
648 | markersList[0].rectangle.topLeft(), | |
649 | markersList[-1].rectangle.bottomRight(), | |
650 | ) | |
651 | if selection.isValid(): | |
652 | return selection | |
653 | ||
654 | return None | |
655 | ||
656 | @pyqtSlot() | |
657 | def clearSelection(self): | |
658 | """ | |
659 | Public slot to clear the current selection. | |
660 | """ | |
9710
e011859649ea
Corrected another issue showing markers in Single Page mode.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9708
diff
changeset
|
661 | self.clearMarkers(PdfMarkerType.SELECTION) |