def __updateGeometry(self): """ Update the shadow geometry to fit the widget's changed geometry. """ widget = self.__widget parent = self.__widgetParent radius = self.radius_ pos = widget.pos() if parent != widget.parentWidget(): pos = widget.parentWidget().mapTo(parent, pos) geom = QRect(pos, widget.size()) geom.adjust(-radius, -radius, radius, radius) if geom != self.geometry(): self.setGeometry(geom) # Set the widget mask (punch a hole through to the `widget` instance. rect = self.rect() mask = QRegion(rect) transparent = QRegion(rect.adjusted(radius, radius, -radius, -radius)) mask = mask.subtracted(transparent) self.setMask(mask)
class Surface(QWidget): rightClicked = pyqtSignal(QPoint) linkClicked = pyqtSignal(QEvent, page.Page, popplerqt4.Poppler.Link) linkHovered = pyqtSignal(page.Page, popplerqt4.Poppler.Link) linkLeft = pyqtSignal() linkHelpRequested = pyqtSignal(QPoint, page.Page, popplerqt4.Poppler.Link) selectionChanged = pyqtSignal(QRect) def __init__(self, view): super(Surface, self).__init__(view) self.setBackgroundRole(QPalette.Dark) self._view = weakref.ref(view) self._currentLinkId = None self._selecting = False self._magnifying = False self._magnifier = None self.setMagnifier(magnifier.Magnifier()) self.setMagnifierModifiers(Qt.CTRL) self._selection = QRect() self._rubberBand = CustomRubberBand(self) self._scrolling = False self._scrollTimer = QTimer(interval=100, timeout=self._scrollTimeout) self._pageLayout = None self._highlights = weakref.WeakKeyDictionary() self.setPageLayout(layout.Layout()) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setLinksEnabled(True) self.setSelectionEnabled(True) self.setShowUrlTips(True) self.view().cursorNeedUpdate.connect(self.updateCursor) def pageLayout(self): return self._pageLayout def setPageLayout(self, layout): old, self._pageLayout = self._pageLayout, layout if old: old.redraw.disconnect(self.redraw) old.changed.disconnect(self.updateLayout) layout.redraw.connect(self.redraw) layout.changed.connect(self.updateLayout) def view(self): """Returns our associated View.""" return self._view() def viewportRect(self): """Returns the rectangle of us that is visible in the View.""" return self.view().viewport().rect().translated(-self.pos()) def setSelectionEnabled(self, enabled): """Enables or disables selecting rectangular regions.""" self._selectionEnabled = enabled if not enabled: self.clearSelection() self._rubberBand.hide() self._selecting = False def selectionEnabled(self): """Returns True if selecting rectangular regions is enabled.""" return self._selectionEnabled def setLinksEnabled(self, enabled): """Enables or disables the handling of Poppler.Links in the pages.""" self._linksEnabled = enabled def linksEnabled(self): """Returns True if the handling of Poppler.Links in the pages is enabled.""" return self._linksEnabled def setShowUrlTips(self, enabled): """Enables or disables showing the URL in a tooltip when hovering a link. (Of course also setLinksEnabled(True) if you want this.) """ self._showUrlTips = enabled def showUrlTips(self): """Returns True if URLs are shown in a tooltip when hovering a link.""" return self._showUrlTips def setMagnifier(self, magnifier): """Sets the Magnifier to use (or None to disable the magnifier). The Surface takes ownership of the Magnifier. """ if self._magnifier: self._magnifier.setParent(None) magnifier.setParent(self) self._magnifier = magnifier def magnifier(self): """Returns the currently set magnifier.""" return self._magnifier def setMagnifierModifiers(self, modifiers): """Sets the modifiers (e.g. Qt.CTRL) to show the magnifier. Use None to show the magnifier always (instead of dragging). """ self._magnifierModifiers = modifiers def magnifierModifiers(self): """Returns the currently set keyboard modifiers (e.g. Qt.CTRL) to show the magnifier.""" return self._magnifierModifiers def hasSelection(self): """Returns True if there is a selection.""" return bool(self._selection) def setSelection(self, rect): """Sets the selection rectangle.""" rect = rect.normalized() old, self._selection = self._selection, rect self._rubberBand.setGeometry(rect) self._rubberBand.setVisible(bool(rect)) if rect != old: self.selectionChanged.emit(rect) def selection(self): """Returns the selection rectangle (normalized) or an invalid QRect().""" return QRect(self._selection) def clearSelection(self): """Hides the selection rectangle.""" self.setSelection(QRect()) def redraw(self, rect): """Called when the Layout wants to redraw a rectangle.""" self.update(rect) def updateLayout(self): """Conforms ourselves to our layout (that must already be updated.)""" self.clearSelection() self.resize(self._pageLayout.size()) self.update() def highlight(self, highlighter, areas, msec=0): """Highlights the list of areas using the given highlighter. Every area is a two-tuple (page, rect), where rect is a rectangle inside (0, 0, 1, 1) like the linkArea attribute of a Poppler.Link. """ d = weakref.WeakKeyDictionary() for page, areas in itertools.groupby(sorted(areas), lambda a: a[0]): d[page] = list(area[1] for area in areas) if msec: def clear(selfref=weakref.ref(self)): self = selfref() if self: self.clearHighlight(highlighter) t = QTimer(singleShot = True, timeout = clear) t.start(msec) else: t = None self.clearHighlight(highlighter) self._highlights[highlighter] = (d, t) self.update(sum((page.rect() for page in d), QRegion())) def clearHighlight(self, highlighter): """Removes the highlighted areas of the given highlighter.""" try: (d, t) = self._highlights[highlighter] except KeyError: return del self._highlights[highlighter] self.update(sum((page.rect() for page in d), QRegion())) def paintEvent(self, ev): """Handle PaintEvent on the surface to highlight the selection.""" painter = QPainter(self) pages = list(self.pageLayout().pagesAt(ev.rect())) for page in pages: page.paint(painter, ev.rect()) for highlighter, (d, t) in self._highlights.items(): rects = [] for page in pages: try: rects.extend(map(page.linkRect, d[page])) except KeyError: continue if rects: highlighter.paintRects(painter, rects) def handleMousePressEvent(self, ev): """Handle mouse press for various operations - links to source, - magnifier, - selection highlight, If event was used, return true to indicate processing should stop. """ # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) # selecting? if self._selectionEnabled: if self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge == _OUTSIDE: self.clearSelection() else: if ev.button() != Qt.RightButton or edge != _INSIDE: self._selecting = True self._selectionEdge = edge self._selectionRect = self.selection() self._selectionPos = pos if edge == _INSIDE: self.setCursor(Qt.SizeAllCursor) return True if not self._selecting: if ev.modifiers() & Qt.ShiftModifier and ev.button() == Qt.LeftButton and self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True if ev.button() == Qt.RightButton or int(ev.modifiers()) & _SCAM: if not (int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._selecting = True self._selectionEdge = _RIGHT | _BOTTOM self._selectionRect = QRect(pos, QSize(0, 0)) self._selectionPos = pos return True # link? if self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True # magnifier? if (self._magnifier and int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._magnifier.moveCenter(pos) self._magnifier.show() self._magnifier.raise_() self._magnifying = True self.setCursor(QCursor(Qt.BlankCursor)) return True return False def handleMouseReleaseEvent(self, ev): """Handle mouse release events for various operations: - hide magnifier, - selection. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: self._magnifier.hide() self._magnifying = False self.unsetCursor() consumed = True elif self._selecting: self._selecting = False selection = self._selectionRect.normalized() if selection.width() < 8 and selection.height() < 8: self.clearSelection() else: self.setSelection(selection) if self._scrolling: self.stopScrolling() self.unsetCursor() consumed = True if ev.button() == Qt.RightButton: # As the event comes from the view, we need to map it locally. self.rightClick(self.mapFromParent(ev.pos())) consumed = True return consumed def handleMouseMoveEvent(self, ev): """Handle mouse move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: # As the event comes from the view, we need to map it locally. self._magnifier.moveCenter(self.mapFromParent(ev.pos())) consumed = True elif self._selecting: # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) self._moveSelection(pos) self._rubberBand.show() # check if we are dragging close to the edge of the view, scroll if needed view = self.viewportRect() dx = pos.x() - view.left() - 12 if dx >= 0: dx = pos.x() - view.right() + 12 if dx < 0: dx = 0 dy = pos.y() - view.top() - 12 if dy >= 0: dy = pos.y() - view.bottom() + 12 if dy < 0: dy = 0 if dx or dy: self.startScrolling(dx, dy) elif self._scrolling: self.stopScrolling() consumed = True return consumed def handleMoveEvent(self, ev): """Handle move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False pos = self.mapFromGlobal(QCursor.pos()) if self._selecting: self._moveSelection(pos) consumed = True elif self._magnifying: self._magnifier.moveCenter(pos) consumed = True return consumed def handleHelpEvent(self, ev): """Handle help event: show link if any.""" if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromParent(ev.pos())) if link: self.linkHelpEvent(ev.globalPos(), page, link) return True def updateKineticCursor(self, active): """Cursor handling when kinetic move starts/stops. - reset the cursor and hide tooltips if visible at start, - update the cursor and show the appropriate tooltips at stop. Used as a slot linked to the kineticStarted() signal. """ if active: self.unsetCursor() if QToolTip.isVisible(): QToolTip.hideText() else: self.updateCursor(QCursor.pos()) if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromGlobal(QCursor.pos())) if link: self.linkHelpEvent(QCursor.pos(), page, link) def updateCursor(self, evpos): """Set the cursor to the right glyph, depending on action""" pos = self.mapFromGlobal(evpos) cursor = None edge = _OUTSIDE if self._selectionEnabled and self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge is not _OUTSIDE: if edge in (_TOP, _BOTTOM): cursor = Qt.SizeVerCursor elif edge in (_LEFT, _RIGHT): cursor = Qt.SizeHorCursor elif edge in (_LEFT | _TOP, _RIGHT | _BOTTOM): cursor = Qt.SizeFDiagCursor elif edge in (_TOP | _RIGHT, _BOTTOM | _LEFT): cursor = Qt.SizeBDiagCursor elif edge is _INSIDE: cursor = Qt.SizeAllCursor elif self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: cursor = Qt.PointingHandCursor lid = id(link) else: lid = None if lid != self._currentLinkId: if self._currentLinkId is not None: self.linkHoverLeave() self._currentLinkId = lid if link: self.linkHoverEnter(page, link) self.setCursor(cursor) if cursor else self.unsetCursor() def linkHelpEvent(self, globalPos, page, link): """Called when a QHelpEvent occurs on a link. The default implementation shows a tooltip if showUrls() is True, and emits the linkHelpRequested() signal. """ if self._showUrlTips and isinstance(link, popplerqt4.Poppler.LinkBrowse): QToolTip.showText(globalPos, link.url(), self, page.linkRect(link.linkArea())) self.linkHelpRequested.emit(globalPos, page, link) def rightClick(self, pos): """Called when the right mouse button is released. (Use this instead of the contextMenuEvent as that one also fires when starting a right-button selection.) The default implementation emite the rightClicked(pos) signal and also sends a ContextMenu event to the View widget. """ self.rightClicked.emit(pos) QApplication.postEvent(self.view().viewport(), QContextMenuEvent(QContextMenuEvent.Mouse, pos + self.pos())) def linkClickEvent(self, ev, page, link): """Called when a link is clicked. The default implementation emits the linkClicked(event, page, link) signal. """ self.linkClicked.emit(ev, page, link) def linkHoverEnter(self, page, link): """Called when the mouse hovers over a link. The default implementation emits the linkHovered(page, link) signal. """ self.linkHovered.emit(page, link) def linkHoverLeave(self): """Called when the mouse does not hover a link anymore. The default implementation emits the linkLeft() signal. """ self.linkLeft.emit() def startScrolling(self, dx, dy): """Starts scrolling dx, dy about 10 times a second. Stops automatically when the end is reached. """ self._scrolling = QPoint(dx, dy) self._scrollTimer.isActive() or self._scrollTimer.start() def stopScrolling(self): """Stops scrolling.""" self._scrolling = False self._scrollTimer.stop() def _scrollTimeout(self): """(Internal) Called by the _scrollTimer.""" # change the scrollbars, but check how far they really moved. pos = self.pos() self.view().fastScrollBy(self._scrolling) diff = pos - self.pos() if not diff: self.stopScrolling() def _moveSelection(self, pos): """(Internal) Moves the dragged selection edge or corner to the given pos (QPoint).""" diff = pos - self._selectionPos self._selectionPos = pos edge = self._selectionEdge self._selectionRect.adjust( diff.x() if edge & _LEFT else 0, diff.y() if edge & _TOP else 0, diff.x() if edge & _RIGHT else 0, diff.y() if edge & _BOTTOM else 0) self._rubberBand.setGeometry(self._selectionRect.normalized()) if self.cursor().shape() in (Qt.SizeBDiagCursor, Qt.SizeFDiagCursor): # we're dragging a corner, use correct diagonal cursor bdiag = (edge in (3, 12)) ^ (self._selectionRect.width() * self._selectionRect.height() >= 0) self.setCursor(Qt.SizeBDiagCursor if bdiag else Qt.SizeFDiagCursor)
def lineSelectionRect(self, line): rect = QRect(line.p1(), line.p2()).normalized() if rect.isValid(): rect.adjust(-2, -2, 2, 2) return rect
def paint (self, painter, option, index): ''' QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ''' orntnDelegate = self.chooseOrientationDelegate() painter.save() if option.state & QStyle.State_Selected: borderPen = QPen(Qt.blue) borderPen.setWidth(3) nameBgColors = [Qt.white, Qt.yellow] else: borderPen = QPen(Qt.lightGray) lightBlue = QColor(0, 0, 255).lighter(180) nameBgColors = [Qt.white, lightBlue] # Set default font and color itemRect = option.rect painter.fillRect(itemRect, Qt.white) painter.setFont(orntnDelegate.getNameFont()) painter.setPen(Qt.black) m = index.model() ticker = m.data(index, PositionsModel.ROLE_TICKER) companyName = m.data(index, Qt.DisplayRole) line2 = m.data(index, PositionsModel.ROLE_CURRENT_PRICE) line3 = m.data(index, PositionsModel.ROLE_CHANGE) fontMetricsCompanyName = QFontMetrics(orntnDelegate.getNameFont()) fontMetricsTicker = QFontMetrics(orntnDelegate.getTickerFont()) fontMetricsCurrentPrice = QFontMetrics(orntnDelegate.getCurrentPriceFont()) fontMetricsChange = QFontMetrics(orntnDelegate.getChangeFont()) lineSp1 = fontMetricsCompanyName.lineSpacing() lineSp2 = fontMetricsCurrentPrice.lineSpacing() lineSp3 = fontMetricsChange.lineSpacing() # Company Name (EXCHANGE:SYMBOL) textRectShade = QRect(itemRect.left(), itemRect.top(), itemRect.width(), self.MARGIN + lineSp1 + self.MARGIN) gradient = self.createLinearGradient(textRectShade, nameBgColors[0], nameBgColors[1]) painter.fillRect(textRectShade, gradient) textRect = QRect(itemRect.left() + self.MARGIN, itemRect.top() + self.MARGIN, itemRect.width() - 2 * self.MARGIN, lineSp1 + self.MARGIN) painter.setFont(orntnDelegate.getNameFont()) # Shorten the company name such that long company names are not written on top of the ticker tickerTextW = fontMetricsTicker.width(ticker) companyNameTextW = textRect.width() - tickerTextW - 2 * self.MARGIN companyName = fontMetricsCompanyName.elidedText(companyName, Qt.ElideRight, companyNameTextW) painter.drawText(textRect, Qt.AlignVCenter | Qt.AlignLeft, companyName) painter.setFont(orntnDelegate.getTickerFont()) painter.drawText(textRect, Qt.AlignVCenter | Qt.AlignRight, "(%s)" % (ticker)) # Current price painter.setFont(orntnDelegate.getCurrentPriceFont()) textRect.adjust(0, lineSp1 + self.MARGIN, 0, lineSp2 + self.MARGIN) painter.drawText(textRect, Qt.AlignTop | Qt.AlignLeft, line2) rightColRect = QRect(textRect.left(), textRect.top(), textRect.width(), textRect.height()) # Change ccol = m.data(index, PositionsModel.ROLE_CHANGE_COLOR) if ccol == "chg": painter.setPen(QPen(Qt.darkGreen)) elif ccol == "chr": painter.setPen(QPen(Qt.red)) painter.setFont(orntnDelegate.getChangeFont()) textRect.adjust(0, lineSp2 + self.MARGIN, 0, lineSp3 + self.MARGIN) painter.drawText(textRect, Qt.AlignTop | Qt.AlignLeft, line3) ''' Right Column ''' orntnDelegate.paintRightCol(index, painter, rightColRect) painter.setPen(borderPen) painter.drawRect(itemRect) painter.restore()
def paint (self, painter, option, index): ''' @param painter QPainter @param option QStyleOptionViewItem @param index QModelIndex ''' itemRect = option.rect textW = self.columnWidths[index.column()] if index.column() is StockMatchTableModel.COL_TICKER: painter.save() m = index.model() line1 = m.data(index, Qt.DisplayRole).toString() line1 = option.fontMetrics.elidedText(line1, Qt.ElideLeft, textW) # Paint the background first and then set the pen color if option.state & QStyle.State_Selected: painter.fillRect(itemRect, Qt.white) self.setPenColor(painter, Qt.darkBlue) else: linearGradient = self.createLinearGradient(itemRect, Qt.black, Qt.blue) painter.fillRect(itemRect, linearGradient) self.setPenColor(painter, Qt.white) textRect = itemRect painter.drawText(textRect, Qt.AlignVCenter | Qt.AlignCenter, line1) painter.restore() elif index.column() is StockMatchTableModel.COL_NAME: # Name column painter.save() m = index.model() line1 = m.data(index, Qt.DisplayRole).toString() line1 = option.fontMetrics.elidedText(line1, Qt.ElideRight, textW) line2 = m.data(index, StockMatchTableModel.ROLE_SUBTEXT1).toString() # Paint the background first and then set the pen color if option.state & QStyle.State_Selected: linearGradient = self.createLinearGradient(itemRect, Qt.white, Qt.yellow) painter.fillRect(itemRect, linearGradient) self.setPenColor(painter, Qt.darkBlue) else: linearGradient = self.createLinearGradient(itemRect, Qt.blue, Qt.darkBlue) painter.fillRect(itemRect, linearGradient) self.setPenColor(painter, Qt.white) lineSp = option.fontMetrics.lineSpacing() numLines = 2 leftMargin = self.MARGIN textRect = QRect(itemRect.left() + leftMargin, itemRect.top() + self.MARGIN, itemRect.width(), numLines * lineSp) painter.drawText(textRect, Qt.AlignTop | Qt.AlignLeft, line1) textRect.adjust(0, option.fontMetrics.lineSpacing(), 0, 0) # Draw the subtext subTextColor = painter.pen().color() subTextColor.setAlphaF(0.5) painter.setPen(subTextColor) painter.drawText(textRect, Qt.AlignTop | Qt.AlignLeft, line2) painter.restore() else: QStyledItemDelegate.paint(self, painter, option, index)
class Surface(QWidget): rightClicked = pyqtSignal(QPoint) linkClicked = pyqtSignal(QEvent, page.Page, popplerqt4.Poppler.Link) linkHovered = pyqtSignal(page.Page, popplerqt4.Poppler.Link) linkLeft = pyqtSignal() linkHelpRequested = pyqtSignal(QPoint, page.Page, popplerqt4.Poppler.Link) selectionChanged = pyqtSignal(QRect) def __init__(self, view): super(Surface, self).__init__(view) self.setBackgroundRole(QPalette.Dark) self._view = weakref.ref(view) self._currentLinkId = None self._selecting = False self._magnifying = False self._magnifier = None self.setMagnifier(magnifier.Magnifier()) self.setMagnifierModifiers(Qt.CTRL) self._selection = QRect() self._rubberBand = CustomRubberBand(self) self._scrolling = False self._scrollTimer = QTimer(interval=100, timeout=self._scrollTimeout) self._pageLayout = None self._highlights = weakref.WeakKeyDictionary() self.setPageLayout(layout.Layout()) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setLinksEnabled(True) self.setSelectionEnabled(True) self.setShowUrlTips(True) self.view().cursorNeedUpdate.connect(self.updateCursor) def pageLayout(self): return self._pageLayout def setPageLayout(self, layout): old, self._pageLayout = self._pageLayout, layout if old: old.redraw.disconnect(self.redraw) old.changed.disconnect(self.updateLayout) layout.redraw.connect(self.redraw) layout.changed.connect(self.updateLayout) def view(self): """Returns our associated View.""" return self._view() def viewportRect(self): """Returns the rectangle of us that is visible in the View.""" return self.view().viewport().rect().translated(-self.pos()) def setSelectionEnabled(self, enabled): """Enables or disables selecting rectangular regions.""" self._selectionEnabled = enabled if not enabled: self.clearSelection() self._rubberBand.hide() self._selecting = False def selectionEnabled(self): """Returns True if selecting rectangular regions is enabled.""" return self._selectionEnabled def setLinksEnabled(self, enabled): """Enables or disables the handling of Poppler.Links in the pages.""" self._linksEnabled = enabled def linksEnabled(self): """Returns True if the handling of Poppler.Links in the pages is enabled.""" return self._linksEnabled def setShowUrlTips(self, enabled): """Enables or disables showing the URL in a tooltip when hovering a link. (Of course also setLinksEnabled(True) if you want this.) """ self._showUrlTips = enabled def showUrlTips(self): """Returns True if URLs are shown in a tooltip when hovering a link.""" return self._showUrlTips def setMagnifier(self, magnifier): """Sets the Magnifier to use (or None to disable the magnifier). The Surface takes ownership of the Magnifier. """ if self._magnifier: self._magnifier.setParent(None) magnifier.setParent(self) self._magnifier = magnifier def magnifier(self): """Returns the currently set magnifier.""" return self._magnifier def setMagnifierModifiers(self, modifiers): """Sets the modifiers (e.g. Qt.CTRL) to show the magnifier. Use None to show the magnifier always (instead of dragging). """ self._magnifierModifiers = modifiers def magnifierModifiers(self): """Returns the currently set keyboard modifiers (e.g. Qt.CTRL) to show the magnifier.""" return self._magnifierModifiers def hasSelection(self): """Returns True if there is a selection.""" return bool(self._selection) def setSelection(self, rect): """Sets the selection rectangle.""" rect = rect.normalized() old, self._selection = self._selection, rect self._rubberBand.setGeometry(rect) self._rubberBand.setVisible(bool(rect)) if rect != old: self.selectionChanged.emit(rect) def selection(self): """Returns the selection rectangle (normalized) or an invalid QRect().""" return QRect(self._selection) def clearSelection(self): """Hides the selection rectangle.""" self.setSelection(QRect()) def redraw(self, rect): """Called when the Layout wants to redraw a rectangle.""" self.update(rect) def updateLayout(self): """Conforms ourselves to our layout (that must already be updated.)""" self.clearSelection() self.resize(self._pageLayout.size()) self.update() def highlight(self, highlighter, areas, msec=0): """Highlights the list of areas using the given highlighter. Every area is a two-tuple (page, rect), where rect is a rectangle inside (0, 0, 1, 1) like the linkArea attribute of a Poppler.Link. """ d = weakref.WeakKeyDictionary() for page, areas in itertools.groupby(sorted(areas), lambda a: a[0]): d[page] = list(area[1] for area in areas) if msec: def clear(selfref=weakref.ref(self)): self = selfref() if self: self.clearHighlight(highlighter) t = QTimer(singleShot=True, timeout=clear) t.start(msec) else: t = None self.clearHighlight(highlighter) self._highlights[highlighter] = (d, t) self.update(sum((page.rect() for page in d), QRegion())) def clearHighlight(self, highlighter): """Removes the highlighted areas of the given highlighter.""" try: (d, t) = self._highlights[highlighter] except KeyError: return del self._highlights[highlighter] self.update(sum((page.rect() for page in d), QRegion())) def paintEvent(self, ev): """Handle PaintEvent on the surface to highlight the selection.""" painter = QPainter(self) pages = list(self.pageLayout().pagesAt(ev.rect())) for page in pages: page.paint(painter, ev.rect()) for highlighter, (d, t) in self._highlights.items(): rects = [] for page in pages: try: rects.extend(map(page.linkRect, d[page])) except KeyError: continue if rects: highlighter.paintRects(painter, rects) def handleMousePressEvent(self, ev): """Handle mouse press for various operations - links to source, - magnifier, - selection highlight, If event was used, return true to indicate processing should stop. """ # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) # selecting? if self._selectionEnabled: if self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge == _OUTSIDE: self.clearSelection() else: if ev.button() != Qt.RightButton or edge != _INSIDE: self._selecting = True self._selectionEdge = edge self._selectionRect = self.selection() self._selectionPos = pos if edge == _INSIDE: self.setCursor(Qt.SizeAllCursor) return True if not self._selecting: if ev.modifiers() & Qt.ShiftModifier and ev.button( ) == Qt.LeftButton and self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True if ev.button() == Qt.RightButton or int( ev.modifiers()) & _SCAM: if not (int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._selecting = True self._selectionEdge = _RIGHT | _BOTTOM self._selectionRect = QRect(pos, QSize(0, 0)) self._selectionPos = pos return True # link? if self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True # magnifier? if (self._magnifier and int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._magnifier.moveCenter(pos) self._magnifier.show() self._magnifier.raise_() self._magnifying = True self.setCursor(QCursor(Qt.BlankCursor)) return True return False def handleMouseReleaseEvent(self, ev): """Handle mouse release events for various operations: - hide magnifier, - selection. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: self._magnifier.hide() self._magnifying = False self.unsetCursor() consumed = True elif self._selecting: self._selecting = False selection = self._selectionRect.normalized() if selection.width() < 8 and selection.height() < 8: self.clearSelection() else: self.setSelection(selection) if self._scrolling: self.stopScrolling() self.unsetCursor() consumed = True if ev.button() == Qt.RightButton: # As the event comes from the view, we need to map it locally. self.rightClick(self.mapFromParent(ev.pos())) consumed = True return consumed def handleMouseMoveEvent(self, ev): """Handle mouse move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: # As the event comes from the view, we need to map it locally. self._magnifier.moveCenter(self.mapFromParent(ev.pos())) consumed = True elif self._selecting: # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) self._moveSelection(pos) self._rubberBand.show() # check if we are dragging close to the edge of the view, scroll if needed view = self.viewportRect() dx = pos.x() - view.left() - 12 if dx >= 0: dx = pos.x() - view.right() + 12 if dx < 0: dx = 0 dy = pos.y() - view.top() - 12 if dy >= 0: dy = pos.y() - view.bottom() + 12 if dy < 0: dy = 0 if dx or dy: self.startScrolling(dx, dy) elif self._scrolling: self.stopScrolling() consumed = True return consumed def handleMoveEvent(self, ev): """Handle move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False pos = self.mapFromGlobal(QCursor.pos()) if self._selecting: self._moveSelection(pos) consumed = True elif self._magnifying: self._magnifier.moveCenter(pos) consumed = True return consumed def handleHelpEvent(self, ev): """Handle help event: show link if any.""" if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromParent(ev.pos())) if link: self.linkHelpEvent(ev.globalPos(), page, link) return True def updateKineticCursor(self, active): """Cursor handling when kinetic move starts/stops. - reset the cursor and hide tooltips if visible at start, - update the cursor and show the appropriate tooltips at stop. Used as a slot linked to the kineticStarted() signal. """ if active: self.unsetCursor() if QToolTip.isVisible(): QToolTip.hideText() else: self.updateCursor(QCursor.pos()) if self._linksEnabled: page, link = self.pageLayout().linkAt( self.mapFromGlobal(QCursor.pos())) if link: self.linkHelpEvent(QCursor.pos(), page, link) def updateCursor(self, evpos): """Set the cursor to the right glyph, depending on action""" pos = self.mapFromGlobal(evpos) cursor = None edge = _OUTSIDE if self._selectionEnabled and self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge is not _OUTSIDE: if edge in (_TOP, _BOTTOM): cursor = Qt.SizeVerCursor elif edge in (_LEFT, _RIGHT): cursor = Qt.SizeHorCursor elif edge in (_LEFT | _TOP, _RIGHT | _BOTTOM): cursor = Qt.SizeFDiagCursor elif edge in (_TOP | _RIGHT, _BOTTOM | _LEFT): cursor = Qt.SizeBDiagCursor elif edge is _INSIDE: cursor = Qt.SizeAllCursor elif self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: cursor = Qt.PointingHandCursor lid = id(link) else: lid = None if lid != self._currentLinkId: if self._currentLinkId is not None: self.linkHoverLeave() self._currentLinkId = lid if link: self.linkHoverEnter(page, link) self.setCursor(cursor) if cursor else self.unsetCursor() def linkHelpEvent(self, globalPos, page, link): """Called when a QHelpEvent occurs on a link. The default implementation shows a tooltip if showUrls() is True, and emits the linkHelpRequested() signal. """ if self._showUrlTips and isinstance(link, popplerqt4.Poppler.LinkBrowse): QToolTip.showText(globalPos, link.url(), self, page.linkRect(link.linkArea())) self.linkHelpRequested.emit(globalPos, page, link) def rightClick(self, pos): """Called when the right mouse button is released. (Use this instead of the contextMenuEvent as that one also fires when starting a right-button selection.) The default implementation emite the rightClicked(pos) signal and also sends a ContextMenu event to the View widget. """ self.rightClicked.emit(pos) QApplication.postEvent( self.view().viewport(), QContextMenuEvent(QContextMenuEvent.Mouse, pos + self.pos())) def linkClickEvent(self, ev, page, link): """Called when a link is clicked. The default implementation emits the linkClicked(event, page, link) signal. """ self.linkClicked.emit(ev, page, link) def linkHoverEnter(self, page, link): """Called when the mouse hovers over a link. The default implementation emits the linkHovered(page, link) signal. """ self.linkHovered.emit(page, link) def linkHoverLeave(self): """Called when the mouse does not hover a link anymore. The default implementation emits the linkLeft() signal. """ self.linkLeft.emit() def startScrolling(self, dx, dy): """Starts scrolling dx, dy about 10 times a second. Stops automatically when the end is reached. """ self._scrolling = QPoint(dx, dy) self._scrollTimer.isActive() or self._scrollTimer.start() def stopScrolling(self): """Stops scrolling.""" self._scrolling = False self._scrollTimer.stop() def _scrollTimeout(self): """(Internal) Called by the _scrollTimer.""" # change the scrollbars, but check how far they really moved. pos = self.pos() self.view().fastScrollBy(self._scrolling) diff = pos - self.pos() if not diff: self.stopScrolling() def _moveSelection(self, pos): """(Internal) Moves the dragged selection edge or corner to the given pos (QPoint).""" diff = pos - self._selectionPos self._selectionPos = pos edge = self._selectionEdge self._selectionRect.adjust(diff.x() if edge & _LEFT else 0, diff.y() if edge & _TOP else 0, diff.x() if edge & _RIGHT else 0, diff.y() if edge & _BOTTOM else 0) self._rubberBand.setGeometry(self._selectionRect.normalized()) if self.cursor().shape() in (Qt.SizeBDiagCursor, Qt.SizeFDiagCursor): # we're dragging a corner, use correct diagonal cursor bdiag = (edge in (3, 12)) ^ (self._selectionRect.width() * self._selectionRect.height() >= 0) self.setCursor(Qt.SizeBDiagCursor if bdiag else Qt.SizeFDiagCursor)