Пример #1
0
 def renderRect(self) -> QRect:
     rect = QRect(self.rect())
     if self.orientation() == Qt.Vertical:
         rect.adjust(self.frame_width, self.frame_width * 2, 0, 0)
     else:
         rect.adjust(self.frame_width * 2, self.frame_width, 0, 0)
     return rect
Пример #2
0
 def capturedArea(self):
     captured = QRect(self.mCaptureStart, self.tilePosition()).normalized()
     if (captured.width() == 0):
         captured.adjust(-1, 0, 1, 0)
     if (captured.height() == 0):
         captured.adjust(0, -1, 0, 1)
     return captured
Пример #3
0
 def capturedArea(self):
     captured = QRect(self.mCaptureStart, self.tilePosition()).normalized()
     if (captured.width() == 0):
         captured.adjust(-1, 0, 1, 0)
     if (captured.height() == 0):
         captured.adjust(0, -1, 0, 1)
     return captured
Пример #4
0
    def drawPrimitive(self,
                      QStyle_PrimitiveElement,
                      QStyleOption,
                      QPainter,
                      widget=None):
        if QStyle_PrimitiveElement == QStyle.PE_PanelItemViewRow:
            # print(widget)
            # print(QStyleOption)

            # 通过point定位
            item = widget.itemAt(QStyleOption.rect.center())

            if isinstance(item, YqFloderItem):
                if QStyleOption.state & QStyle.State_Selected:
                    rect = QRect(QStyleOption.rect)
                    rect.setWidth(QPainter.window().width())
                    margin = (QStyleOption.rect.height() - 20) / 2
                    rect.adjust(0, margin, 0, -margin)

                    is_focuse = QStyleOption.state & QStyle.State_HasFocus
                    color = QColor()

                    if is_focuse:
                        color.setNamedColor('#5990EF')
                    else:
                        color.setNamedColor('#cecece')

                    QPainter.fillRect(rect, color)

        super().drawPrimitive(QStyle_PrimitiveElement, QStyleOption, QPainter,
                              widget)
Пример #5
0
 def paintEvent(self, event):
     painter = QPainter(self)
     if self.pixmap:
         painter.drawPixmap(0, 0, self.pixmap)
     rect = QRect(0, 0, self.width(), self.height())
     margin = 25
     pad = 10
     if self.distance:
         txt = QLocale().toString(self.distance / 1000, 'f', 1) + ' km'
         fm = QFontMetrics(painter.font())
         rect = fm.boundingRect(txt)
         rect = fm.boundingRect(rect, 0, txt)
         rect.adjust(-pad, 0, pad, 0)
         rect.moveBottomRight(
             QPoint(self.width() - margin,
                    self.height() - margin))
         if self.intersectsMarkers(rect):
             rect.moveTopRight(QPoint(self.width() - margin, margin))
         painter.fillRect(rect, QColor(255, 255, 255, 192))
         painter.drawText(rect, Qt.AlignCenter, txt)
     if self.duration:
         minutes = math.floor(self.duration / 60)
         seconds = self.duration - minutes * 60
         txt = '%u:%02u min.' % (minutes, seconds)
         fm = QFontMetrics(painter.font())
         rect = fm.boundingRect(txt)
         rect = fm.boundingRect(rect, 0, txt)
         rect.adjust(-pad, 0, pad, 0)
         rect.moveBottomLeft(QPoint(margin, self.height() - margin))
         if self.intersectsMarkers(rect):
             rect.moveTopLeft(QPoint(margin, margin))
         painter.fillRect(rect, QColor(255, 255, 255, 192))
         painter.drawText(rect, Qt.AlignCenter, txt)
Пример #6
0
    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)
Пример #7
0
    def draw_untransformed(self, painter):

        if not self._enablePointerDraw:
            return

        canvas = self._canvas

        x = canvas.mouse_state.global_pos.x() + 1
        y = canvas.mouse_state.global_pos.y() + 1

        size = canvas.pixel_size * canvas.zoom

        if size <= 0.0:
            return

        painter.setPen(Qt.white)
        painter.setOpacity(0.6)
        painter.setCompositionMode(QPainter.CompositionMode_Difference)

        if size == 1.0:
            painter.fillRect(x, y, 1, 1, Qt.white)
            painter.setPen(Qt.white)

            painter.drawLine(x, y - 4, x, y - 8)
            painter.drawLine(x, y + 4, x, y + 8)

            painter.drawLine(x - 4, y, x - 8, y)
            painter.drawLine(x + 4, y, x + 8, y)

        elif size == 2.0:

            painter.fillRect(x - 1, y - 1, 2, 2, Qt.white)
            painter.setPen(Qt.white)

            painter.drawLine(x - 2, y - 8, x + 1, y - 8)
            painter.drawLine(x - 2, y + 7, x + 1, y + 7)

            painter.drawLine(x - 8, y - 2, x - 8, y + 1)
            painter.drawLine(x + 7, y - 2, x + 7, y + 1)

        elif size == 4.0:

            painter.setPen(Qt.white)

            painter.drawRect(x - 2, y - 2, 4, 4)

            painter.drawLine(x - 2, y - 8, x + 2, y - 8)
            painter.drawLine(x - 2, y + 8, x + 2, y + 8)

            painter.drawLine(x - 8, y - 2, x - 8, y + 2)
            painter.drawLine(x + 8, y - 2, x + 8, y + 2)

        else:

            rect = QRect(x - size / 2, y - size / 2, size, size)
            painter.drawRect(rect)

            rect.adjust(2, 2, -2, -2)
            painter.drawRect(rect)
Пример #8
0
    def paintTab(self, painter: QPainter, index: int):
        if not self.isValidIndex(index):
            return
        painter.save()

        tab = self._tabs[index]
        rect = self._tabRect(index)
        selected = index == self._currentIndex
        enabled = self._enabled and tab.enabled

        if selected:
            painter.fillRect(rect, FancyToolButtonSelectedColor)

        tabText = tab.text
        tabTextRect = QRect(rect)
        drawIcon = rect.height() > 36
        tabIconRect = QRect(rect)

        tabTextRect.translate(0, -2 if drawIcon else 1)
        boldFont = QFont(painter.font())
        boldFont.setPointSizeF(SIDEBAR_FONT_SIZE)
        boldFont.setBold(True)
        painter.setFont(boldFont)
        #painter.setPen(QColor(255, 255, 255, 160) if selected else QColor(0, 0, 0, 110))
        textFlags = Qt.AlignCenter | (Qt.AlignBottom if drawIcon else
                                      Qt.AlignVCenter) | Qt.TextWordWrap

        fader = tab.fader
        if fader > 0 and not selected and enabled:
            painter.save()
            painter.setOpacity(fader)
            painter.fillRect(rect, FancyToolButtonHoverColor)
            painter.restore()

        if not enabled:
            painter.setOpacity(0.7)

        if drawIcon:
            textHeight = (painter.fontMetrics().boundingRect(
                QRect(0, 0, self.width(), self.height()), Qt.TextWordWrap,
                tabText).height())
            tabIconRect.adjust(0, 4, 0, -textHeight - 4)
            iconMode = (QIcon.Active if selected else
                        QIcon.Normal) if enabled else QIcon.Disabled
            iconRect = QRect(0, 0, MODEBAR_ICON_SIZE, MODEBAR_ICON_SIZE)
            iconRect.moveCenter(tabIconRect.center())
            iconRect = iconRect.intersected(tabIconRect)
            drawIconWithShadow(tab.icon, iconRect, painter, iconMode)

        if enabled:
            penColor = FancyTabWidgetEnabledSelectedTextColor if selected else FancyTabWidgetEnabledUnselectedTextColor
        else:
            penColor = FancyTabWidgetDisabledSelectedTextColor if selected else FancyTabWidgetDisabledUnselectedTextColor
        painter.setPen(penColor)
        painter.translate(0, -1)
        painter.drawText(tabTextRect, textFlags, tabText)

        painter.restore()
Пример #9
0
    def restoreWindowGeom(self, offset=0):
        """Restore window geometry from history options.

        Arguments:
            offset -- number of pixels to offset window, down and to right
        """
        rect = QRect(globalref.histOptions['WindowXPos'],
                     globalref.histOptions['WindowYPos'],
                     globalref.histOptions['WindowXSize'],
                     globalref.histOptions['WindowYSize'])
        if rect.x() == -1000 and rect.y() == -1000:
            # let OS position window the first time
            self.resize(rect.size())
        else:
            if offset:
                rect.adjust(offset, offset, offset, offset)
            availRect = QApplication.primaryScreen().availableVirtualGeometry()
            topMargin = globalref.histOptions['WindowTopMargin']
            otherMargin = globalref.histOptions['WindowOtherMargin']
            # remove frame space from available rect
            availRect.adjust(otherMargin, topMargin, -otherMargin,
                             -otherMargin)
            finalRect = rect.intersected(availRect)
            if finalRect.isEmpty():
                rect.moveTo(0, 0)
                finalRect = rect.intersected(availRect)
            if finalRect.isValid():
                self.setGeometry(finalRect)
        crumbWidth = int(self.breadcrumbSplitter.width() / 100 *
                         globalref.histOptions['CrumbSplitPercent'])
        self.breadcrumbSplitter.setSizes(
            [crumbWidth,
             self.breadcrumbSplitter.width() - crumbWidth])
        treeWidth = int(self.treeSplitter.width() / 100 *
                        globalref.histOptions['TreeSplitPercent'])
        self.treeSplitter.setSizes(
            [treeWidth, self.treeSplitter.width() - treeWidth])
        outHeight = int(self.outputSplitter.height() / 100.0 *
                        globalref.histOptions['OutputSplitPercent'])
        self.outputSplitter.setSizes(
            [outHeight, self.outputSplitter.height() - outHeight])
        editHeight = int(self.editorSplitter.height() / 100.0 *
                         globalref.histOptions['EditorSplitPercent'])
        self.editorSplitter.setSizes(
            [editHeight, self.editorSplitter.height() - editHeight])
        titleHeight = int(self.titleSplitter.height() / 100.0 *
                          globalref.histOptions['TitleSplitPercent'])
        self.titleSplitter.setSizes(
            [titleHeight,
             self.titleSplitter.height() - titleHeight])
        self.rightTabs.setCurrentIndex(
            globalref.histOptions['ActiveRightView'])
Пример #10
0
    def paintCell(self, painter: QtGui.QPainter, rect: QtCore.QRect,
                  date: typing.Union[QtCore.QDate, datetime.date]) -> None:
        painter.save()
        with open(get_resource("config.json")) as file:
            if date.toPyDate().strftime("%Y-%m-%d") in json.loads(
                    file.read())["favorites"]:
                painter.fillRect(rect, QtGui.QColor.fromRgb(255, 255, 0))
        if (date.month() != self.monthShown()):
            painter.setPen(QtGui.QColor("#888888"))
        elif date.dayOfWeek() == 6 or date.dayOfWeek() == 7:
            painter.setPen(QtGui.QColor("red"))

        tags = self.get_tags_from_date_file(date.toPyDate())
        rect.adjust(0, 0, -1, -1)
        pen = painter.pen()
        pen.setColor(
            QtGui.QColor.fromHsl(pen.color().hue(),
                                 pen.color().saturation(),
                                 pen.color().lightness(), 150))
        painter.setPen(pen)
        painter.drawRect(rect)
        pen.setColor(
            QtGui.QColor.fromHsl(pen.color().hue(),
                                 pen.color().saturation(),
                                 pen.color().lightness(), 255))
        painter.setPen(pen)

        painter.drawText(rect, QtCore.Qt.AlignTop, str(date.day()))
        text = ""
        try:
            for tag in tags[:5]:
                if len(tag) > 12:
                    tag = str(tag[:12]) + "..."
                text += f" {tag} \n"
        except TypeError:
            text = ""
        font = QtGui.QFont()
        font.setPixelSize(10)
        painter.setFont(font)
        brush = painter.background()
        random.seed(date)
        brush.setColor(QtGui.QColor().fromHsl(randint(0, 255), randint(0, 255),
                                              randint(200, 255)))
        painter.setPen(QtGui.QColor("black"))
        painter.setBackground(brush)
        painter.setBackgroundMode(QtCore.Qt.OpaqueMode)
        painter.drawText(rect, QtCore.Qt.AlignBottom | QtCore.Qt.AlignHCenter,
                         text)
        painter.restore()
Пример #11
0
    def restoreWindowGeom(self, offset=0):
        """Restore window geometry from history options.

        Arguments:
            offset -- number of pixels to offset window, down and to right
        """
        rect = QRect(globalref.histOptions['WindowXPos'],
                     globalref.histOptions['WindowYPos'],
                     globalref.histOptions['WindowXSize'],
                     globalref.histOptions['WindowYSize'])
        if rect.x() == -1000 and rect.y() == -1000:
            # let OS position window the first time
            self.resize(rect.size())
        else:
            if offset:
                rect.adjust(offset, offset, offset, offset)
            desktop = QApplication.desktop()
            if desktop.isVirtualDesktop():
                # availRect = desktop.screen().rect() # buggy in windows?
                availRect = (
                    QGuiApplication.screens()[0].availableVirtualGeometry())
            else:
                availRect = desktop.availableGeometry(desktop.primaryScreen())
            rect = rect.intersected(availRect)
            self.setGeometry(rect)
        crumbWidth = int(self.breadcrumbSplitter.width() / 100 *
                         globalref.histOptions['CrumbSplitPercent'])
        self.breadcrumbSplitter.setSizes(
            [crumbWidth,
             self.breadcrumbSplitter.width() - crumbWidth])
        treeWidth = int(self.treeSplitter.width() / 100 *
                        globalref.histOptions['TreeSplitPercent'])
        self.treeSplitter.setSizes(
            [treeWidth, self.treeSplitter.width() - treeWidth])
        outHeight = int(self.outputSplitter.height() / 100.0 *
                        globalref.histOptions['OutputSplitPercent'])
        self.outputSplitter.setSizes(
            [outHeight, self.outputSplitter.height() - outHeight])
        editHeight = int(self.editorSplitter.height() / 100.0 *
                         globalref.histOptions['EditorSplitPercent'])
        self.editorSplitter.setSizes(
            [editHeight, self.editorSplitter.height() - editHeight])
        titleHeight = int(self.titleSplitter.height() / 100.0 *
                          globalref.histOptions['TitleSplitPercent'])
        self.titleSplitter.setSizes(
            [titleHeight,
             self.titleSplitter.height() - titleHeight])
        self.rightTabs.setCurrentIndex(
            globalref.histOptions['ActiveRightView'])
Пример #12
0
    def paintEvent(self, e):

        p = QPainter(self)

        # Paint border

        bar_rect = self.rect().adjusted(0, 15, 0, -1)

        if self._background is not None:
            p.drawTiledPixmap(bar_rect, self._background)

        pen = QPen()
        pen.setColor(Qt.black)
        pen.setWidth(1)

        p.setPen(pen)

        bar_rect.adjust(0, 0, -1, 0)

        p.drawRect(bar_rect)

        p.setPen(Qt.white)

        # Paint Spectrum

        bar_rect.adjust(1, 1, 0, 0)

        p.fillRect(bar_rect, QBrush(self._gradient))

        # Paint Picker

        p.drawPixmap(self._pickRect, self._pickerPixmap)

        # Draw Label

        label_rect = QRect(200, 1, 20, 15)

        p.drawText(label_rect, Qt.AlignRight, str(self._value))

        if len(self._label) > 0:
            label_rect.adjust(-200, 0, 0, 0)
            p.drawText(label_rect, Qt.AlignLeft, self._label)
Пример #13
0
    def _tab_layout(self, opt):
        """Compute the text/icon rect from the opt rect.

        This is based on Qt's QCommonStylePrivate::tabLayout
        (qtbase/src/widgets/styles/qcommonstyle.cpp) as we can't use the
        private implementation.

        Args:
            opt: QStyleOptionTab

        Return:
            A (text_rect, icon_rect) tuple (both QRects).
        """
        padding = self.pixelMetric(PM_TabBarPadding, opt)
        icon_rect = QRect()
        text_rect = QRect(opt.rect)
        qtutils.ensure_valid(text_rect)
        indicator_width = config.get('tabs', 'indicator-width')
        text_rect.adjust(padding, 0, 0, 0)
        if indicator_width != 0:
            text_rect.adjust(indicator_width +
                             config.get('tabs', 'indicator-space'), 0, 0, 0)
        if not opt.icon.isNull():
            icon_rect = self._get_icon_rect(opt, text_rect)
            text_rect.adjust(icon_rect.width() + padding, 0, 0, 0)
        text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
        return (text_rect, icon_rect)
Пример #14
0
    def _tab_layout(self, opt):
        """Compute the text/icon rect from the opt rect.

        This is based on Qt's QCommonStylePrivate::tabLayout
        (qtbase/src/widgets/styles/qcommonstyle.cpp) as we can't use the
        private implementation.

        Args:
            opt: QStyleOptionTab

        Return:
            A Layout object with two QRects.
        """
        padding = config.cache['tabs.padding']
        indicator_padding = config.cache['tabs.indicator.padding']

        text_rect = QRect(opt.rect)
        if not text_rect.isValid():
            # This happens sometimes according to crash reports, but no idea
            # why...
            return None

        text_rect.adjust(padding.left, padding.top, -padding.right,
                         -padding.bottom)

        indicator_width = config.cache['tabs.indicator.width']
        if indicator_width == 0:
            indicator_rect = QRect()
        else:
            indicator_rect = QRect(opt.rect)
            qtutils.ensure_valid(indicator_rect)
            indicator_rect.adjust(padding.left + indicator_padding.left,
                                  padding.top + indicator_padding.top, 0,
                                  -(padding.bottom + indicator_padding.bottom))
            indicator_rect.setWidth(indicator_width)

            text_rect.adjust(
                indicator_width + indicator_padding.left +
                indicator_padding.right, 0, 0, 0)

        icon_rect = self._get_icon_rect(opt, text_rect)
        if icon_rect.isValid():
            text_rect.adjust(icon_rect.width() + TabBarStyle.ICON_PADDING, 0,
                             0, 0)

        text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
        return Layouts(text=text_rect,
                       icon=icon_rect,
                       indicator=indicator_rect)
Пример #15
0
    def _tab_layout(self, opt):
        """Compute the text/icon rect from the opt rect.

        This is based on Qt's QCommonStylePrivate::tabLayout
        (qtbase/src/widgets/styles/qcommonstyle.cpp) as we can't use the
        private implementation.

        Args:
            opt: QStyleOptionTab

        Return:
            A Layout namedtuple with two QRects.
        """
        padding = config.get('tabs', 'padding')
        indicator_padding = config.get('tabs', 'indicator-padding')

        text_rect = QRect(opt.rect)

        qtutils.ensure_valid(text_rect)
        text_rect.adjust(padding.left, padding.top, -padding.right,
                         -padding.bottom)

        indicator_width = config.get('tabs', 'indicator-width')
        if indicator_width == 0:
            indicator_rect = QRect()
        else:
            indicator_rect = QRect(opt.rect)
            qtutils.ensure_valid(indicator_rect)
            indicator_rect.adjust(padding.left + indicator_padding.left,
                                  padding.top + indicator_padding.top, 0,
                                  -(padding.bottom + indicator_padding.bottom))
            indicator_rect.setWidth(indicator_width)

            text_rect.adjust(
                indicator_width + indicator_padding.left +
                indicator_padding.right, 0, 0, 0)

        if opt.icon.isNull():
            icon_rect = QRect()
        else:
            icon_padding = self.pixelMetric(PixelMetrics.icon_padding, opt)
            icon_rect = self._get_icon_rect(opt, text_rect)
            if icon_rect.isValid():
                text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)

        text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
        return Layouts(text=text_rect,
                       icon=icon_rect,
                       indicator=indicator_rect)
Пример #16
0
    def _tab_layout(self, opt):
        """Compute the text/icon rect from the opt rect.

        This is based on Qt's QCommonStylePrivate::tabLayout
        (qtbase/src/widgets/styles/qcommonstyle.cpp) as we can't use the
        private implementation.

        Args:
            opt: QStyleOptionTab

        Return:
            A Layout object with two QRects.
        """
        padding = config.val.tabs.padding
        indicator_padding = config.val.tabs.indicator_padding

        text_rect = QRect(opt.rect)
        if not text_rect.isValid():
            # This happens sometimes according to crash reports, but no idea
            # why...
            return None

        text_rect.adjust(padding.left, padding.top, -padding.right,
                         -padding.bottom)

        indicator_width = config.val.tabs.width.indicator
        if indicator_width == 0:
            indicator_rect = QRect()
        else:
            indicator_rect = QRect(opt.rect)
            qtutils.ensure_valid(indicator_rect)
            indicator_rect.adjust(padding.left + indicator_padding.left,
                                  padding.top + indicator_padding.top,
                                  0,
                                  -(padding.bottom + indicator_padding.bottom))
            indicator_rect.setWidth(indicator_width)

            text_rect.adjust(indicator_width + indicator_padding.left +
                             indicator_padding.right, 0, 0, 0)

        icon_rect = self._get_icon_rect(opt, text_rect)
        if icon_rect.isValid():
            icon_padding = self.pixelMetric(PixelMetrics.icon_padding, opt)
            text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)

        text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
        return Layouts(text=text_rect, icon=icon_rect,
                       indicator=indicator_rect)
Пример #17
0
    def _tab_layout(self, opt):
        """Compute the text/icon rect from the opt rect.

        This is based on Qt's QCommonStylePrivate::tabLayout
        (qtbase/src/widgets/styles/qcommonstyle.cpp) as we can't use the
        private implementation.

        Args:
            opt: QStyleOptionTab

        Return:
            A Layout namedtuple with two QRects.
        """
        padding = config.get('tabs', 'padding')
        indicator_padding = config.get('tabs', 'indicator-padding')

        text_rect = QRect(opt.rect)
        indicator_rect = QRect(opt.rect)

        qtutils.ensure_valid(text_rect)
        text_rect.adjust(padding.left, padding.top, -padding.right,
                         -padding.bottom)

        indicator_width = config.get('tabs', 'indicator-width')
        if indicator_width == 0:
            indicator_rect = 0
        else:
            qtutils.ensure_valid(indicator_rect)
            indicator_rect.adjust(padding.left + indicator_padding.left,
                                  padding.top + indicator_padding.top,
                                  0,
                                  -(padding.bottom + indicator_padding.bottom))
            indicator_rect.setWidth(indicator_width)

            text_rect.adjust(indicator_width + indicator_padding.left +
                             indicator_padding.right, 0, 0, 0)

        if opt.icon.isNull():
            icon_rect = QRect()
        else:
            icon_padding = self.pixelMetric(PixelMetrics.icon_padding, opt)
            icon_rect = self._get_icon_rect(opt, text_rect)
            if icon_rect.isValid():
                text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)

        text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
        return Layouts(text=text_rect, icon=icon_rect,
                       indicator=indicator_rect)
Пример #18
0
    def refreshPixmap(self):
        # Pixel map used for double buffering.
        self.pixmap = QPixmap(self.size())
        self.pixmap.fill()

        painter = QPainter(self.pixmap)

        # Clear old display.
        painter.eraseRect(self.rect())

        # Draw box around entire display.
        painter.setPen(Qt.darkGray)
        r = QRect(self.rect())
        r.adjust(0, 0, -1, -1)
        painter.drawRect(r)

        # Plot all frames.
        for i in range(
                len(self.frameList[self.numFramesIndex[self.selectedPort]])):
            self.drawAxes(painter, i)

        self.tPosition = 0
        self.update()
Пример #19
0
class SnapshotRegionGrabber(QWidget):
    """
    Class implementing a grabber widget for a rectangular snapshot region.
    
    @signal grabbed(QPixmap) emitted after the region was grabbed
    """
    grabbed = pyqtSignal(QPixmap)

    StrokeMask = 0
    FillMask = 1

    Rectangle = 0
    Ellipse = 1

    def __init__(self, mode=Rectangle):
        """
        Constructor
        
        @param mode region grabber mode (SnapshotRegionGrabber.Rectangle or
            SnapshotRegionGrabber.Ellipse)
        """
        super(SnapshotRegionGrabber, self).__init__(
            None, Qt.X11BypassWindowManagerHint | Qt.WindowStaysOnTopHint
            | Qt.FramelessWindowHint | Qt.Tool)

        assert mode in [
            SnapshotRegionGrabber.Rectangle, SnapshotRegionGrabber.Ellipse
        ]
        self.__mode = mode

        self.__selection = QRect()
        self.__mouseDown = False
        self.__newSelection = False
        self.__handleSize = 10
        self.__mouseOverHandle = None
        self.__showHelp = True
        self.__grabbing = False
        self.__dragStartPoint = QPoint()
        self.__selectionBeforeDrag = QRect()
        self.__locale = QLocale()

        # naming conventions for handles
        # T top, B bottom, R Right, L left
        # 2 letters: a corner
        # 1 letter: the handle on the middle of the corresponding side
        self.__TLHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__TRHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__BLHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__BRHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__LHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__THandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__RHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__BHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__handles = [
            self.__TLHandle, self.__TRHandle, self.__BLHandle, self.__BRHandle,
            self.__LHandle, self.__THandle, self.__RHandle, self.__BHandle
        ]
        self.__helpTextRect = QRect()
        self.__helpText = self.tr(
            "Select a region using the mouse. To take the snapshot, press"
            " the Enter key or double click. Press Esc to quit.")

        self.__pixmap = QPixmap()

        self.setMouseTracking(True)

        QTimer.singleShot(200, self.__initialize)

    def __initialize(self):
        """
        Private slot to initialize the rest of the widget.
        """
        self.__desktop = QApplication.desktop()
        x = self.__desktop.x()
        y = self.__desktop.y()
        if qVersion() >= "5.0.0":
            self.__pixmap = QApplication.screens()[0].grabWindow(
                self.__desktop.winId(), x, y, self.__desktop.width(),
                self.__desktop.height())
        else:
            self.__pixmap = QPixmap.grabWindow(self.__desktop.winId(), x, y,
                                               self.__desktop.width(),
                                               self.__desktop.height())
        self.resize(self.__pixmap.size())
        self.move(x, y)
        self.setCursor(Qt.CrossCursor)
        self.show()

        self.grabMouse()
        self.grabKeyboard()
        self.activateWindow()

    def paintEvent(self, evt):
        """
        Protected method handling paint events.
        
        @param evt paint event (QPaintEvent)
        """
        if self.__grabbing:  # grabWindow() should just get the background
            return

        painter = QPainter(self)
        pal = QPalette(QToolTip.palette())
        font = QToolTip.font()

        handleColor = pal.color(QPalette.Active, QPalette.Highlight)
        handleColor.setAlpha(160)
        overlayColor = QColor(0, 0, 0, 160)
        textColor = pal.color(QPalette.Active, QPalette.Text)
        textBackgroundColor = pal.color(QPalette.Active, QPalette.Base)
        painter.drawPixmap(0, 0, self.__pixmap)
        painter.setFont(font)

        r = QRect(self.__selection)
        if not self.__selection.isNull():
            grey = QRegion(self.rect())
            if self.__mode == SnapshotRegionGrabber.Ellipse:
                reg = QRegion(r, QRegion.Ellipse)
            else:
                reg = QRegion(r)
            grey = grey.subtracted(reg)
            painter.setClipRegion(grey)
            painter.setPen(Qt.NoPen)
            painter.setBrush(overlayColor)
            painter.drawRect(self.rect())
            painter.setClipRect(self.rect())
            drawRect(painter, r, handleColor)

        if self.__showHelp:
            painter.setPen(textColor)
            painter.setBrush(textBackgroundColor)
            self.__helpTextRect = painter.boundingRect(
                self.rect().adjusted(2, 2, -2, -2), Qt.TextWordWrap,
                self.__helpText).translated(-self.__desktop.x(),
                                            -self.__desktop.y())
            self.__helpTextRect.adjust(-2, -2, 4, 2)
            drawRect(painter, self.__helpTextRect, textColor,
                     textBackgroundColor)
            painter.drawText(self.__helpTextRect.adjusted(3, 3, -3, -3),
                             Qt.TextWordWrap, self.__helpText)

        if self.__selection.isNull():
            return

        # The grabbed region is everything which is covered by the drawn
        # rectangles (border included). This means that there is no 0px
        # selection, since a 0px wide rectangle will always be drawn as a line.
        txt = "{0}, {1} ({2} x {3})".format(
            self.__locale.toString(self.__selection.x()),
            self.__locale.toString(self.__selection.y()),
            self.__locale.toString(self.__selection.width()),
            self.__locale.toString(self.__selection.height()))
        textRect = painter.boundingRect(self.rect(), Qt.AlignLeft, txt)
        boundingRect = textRect.adjusted(-4, 0, 0, 0)

        if textRect.width() < r.width() - 2 * self.__handleSize and \
           textRect.height() < r.height() - 2 * self.__handleSize and \
           r.width() > 100 and \
           r.height() > 100:
            # center, unsuitable for small selections
            boundingRect.moveCenter(r.center())
            textRect.moveCenter(r.center())
        elif r.y() - 3 > textRect.height() and \
                r.x() + textRect.width() < self.rect().width():
            # on top, left aligned
            boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3))
            textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3))
        elif r.x() - 3 > textRect.width():
            # left, top aligned
            boundingRect.moveTopRight(QPoint(r.x() - 3, r.y()))
            textRect.moveTopRight(QPoint(r.x() - 5, r.y()))
        elif r.bottom() + 3 + textRect.height() < self.rect().bottom() and \
                r.right() > textRect.width():
            # at bottom, right aligned
            boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3))
            textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3))
        elif r.right() + textRect.width() + 3 < self.rect().width():
            # right, bottom aligned
            boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom()))
            textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom()))

        # If the above didn't catch it, you are running on a very
        # tiny screen...
        drawRect(painter, boundingRect, textColor, textBackgroundColor)
        painter.drawText(textRect, Qt.AlignHCenter, txt)

        if (r.height() > self.__handleSize * 2 and
            r.width() > self.__handleSize * 2) or \
           not self.__mouseDown:
            self.__updateHandles()
            painter.setPen(Qt.NoPen)
            painter.setBrush(handleColor)
            painter.setClipRegion(
                self.__handleMask(SnapshotRegionGrabber.StrokeMask))
            painter.drawRect(self.rect())
            handleColor.setAlpha(60)
            painter.setBrush(handleColor)
            painter.setClipRegion(
                self.__handleMask(SnapshotRegionGrabber.FillMask))
            painter.drawRect(self.rect())

    def resizeEvent(self, evt):
        """
        Protected method to handle resize events.
        
        @param evt resize event (QResizeEvent)
        """
        if self.__selection.isNull():
            return

        r = QRect(self.__selection)
        r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect()))
        r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect()))
        if r.width() <= 1 or r.height() <= 1:
            # This just results in ugly drawing...
            self.__selection = QRect()
        else:
            self.__selection = self.__normalizeSelection(r)

    def mousePressEvent(self, evt):
        """
        Protected method to handle mouse button presses.
        
        @param evt mouse press event (QMouseEvent)
        """
        self.__showHelp = not self.__helpTextRect.contains(evt.pos())
        if evt.button() == Qt.LeftButton:
            self.__mouseDown = True
            self.__dragStartPoint = evt.pos()
            self.__selectionBeforeDrag = QRect(self.__selection)
            if not self.__selection.contains(evt.pos()):
                self.__newSelection = True
                self.__selection = QRect()
            else:
                self.setCursor(Qt.ClosedHandCursor)
        elif evt.button() == Qt.RightButton:
            self.__newSelection = False
            self.__selection = QRect()
            self.setCursor(Qt.CrossCursor)
        self.update()

    def mouseMoveEvent(self, evt):
        """
        Protected method to handle mouse movements.
        
        @param evt mouse move event (QMouseEvent)
        """
        shouldShowHelp = not self.__helpTextRect.contains(evt.pos())
        if shouldShowHelp != self.__showHelp:
            self.__showHelp = shouldShowHelp
            self.update()

        if self.__mouseDown:
            if self.__newSelection:
                p = evt.pos()
                r = self.rect()
                self.__selection = self.__normalizeSelection(
                    QRect(self.__dragStartPoint, self.__limitPointToRect(p,
                                                                         r)))
            elif self.__mouseOverHandle is None:
                # moving the whole selection
                r = self.rect().normalized()
                s = self.__selectionBeforeDrag.normalized()
                p = s.topLeft() + evt.pos() - self.__dragStartPoint
                r.setBottomRight(r.bottomRight() -
                                 QPoint(s.width(), s.height()) + QPoint(1, 1))
                if not r.isNull() and r.isValid():
                    self.__selection.moveTo(self.__limitPointToRect(p, r))
            else:
                # dragging a handle
                r = QRect(self.__selectionBeforeDrag)
                offset = evt.pos() - self.__dragStartPoint

                if self.__mouseOverHandle in \
                   [self.__TLHandle, self.__THandle, self.__TRHandle]:
                    r.setTop(r.top() + offset.y())

                if self.__mouseOverHandle in \
                   [self.__TLHandle, self.__LHandle, self.__BLHandle]:
                    r.setLeft(r.left() + offset.x())

                if self.__mouseOverHandle in \
                   [self.__BLHandle, self.__BHandle, self.__BRHandle]:
                    r.setBottom(r.bottom() + offset.y())

                if self.__mouseOverHandle in \
                   [self.__TRHandle, self.__RHandle, self.__BRHandle]:
                    r.setRight(r.right() + offset.x())

                r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect()))
                r.setBottomRight(
                    self.__limitPointToRect(r.bottomRight(), self.rect()))
                self.__selection = self.__normalizeSelection(r)

            self.update()
        else:
            if self.__selection.isNull():
                return

            found = False
            for r in self.__handles:
                if r.contains(evt.pos()):
                    self.__mouseOverHandle = r
                    found = True
                    break

            if not found:
                self.__mouseOverHandle = None
                if self.__selection.contains(evt.pos()):
                    self.setCursor(Qt.OpenHandCursor)
                else:
                    self.setCursor(Qt.CrossCursor)
            else:
                if self.__mouseOverHandle in [
                        self.__TLHandle, self.__BRHandle
                ]:
                    self.setCursor(Qt.SizeFDiagCursor)
                elif self.__mouseOverHandle in [
                        self.__TRHandle, self.__BLHandle
                ]:
                    self.setCursor(Qt.SizeBDiagCursor)
                elif self.__mouseOverHandle in [
                        self.__LHandle, self.__RHandle
                ]:
                    self.setCursor(Qt.SizeHorCursor)
                elif self.__mouseOverHandle in [
                        self.__THandle, self.__BHandle
                ]:
                    self.setCursor(Qt.SizeVerCursor)

    def mouseReleaseEvent(self, evt):
        """
        Protected method to handle mouse button releases.
        
        @param evt mouse release event (QMouseEvent)
        """
        self.__mouseDown = False
        self.__newSelection = False
        if self.__mouseOverHandle is None and \
           self.__selection.contains(evt.pos()):
            self.setCursor(Qt.OpenHandCursor)
        self.update()

    def mouseDoubleClickEvent(self, evt):
        """
        Protected method to handle mouse double clicks.
        
        @param evt mouse double click event (QMouseEvent)
        """
        self.__grabRect()

    def keyPressEvent(self, evt):
        """
        Protected method to handle key presses.
        
        @param evt key press event (QKeyEvent)
        """
        if evt.key() == Qt.Key_Escape:
            self.grabbed.emit(QPixmap())
        elif evt.key() in [Qt.Key_Enter, Qt.Key_Return]:
            self.__grabRect()
        else:
            evt.ignore()

    def __updateHandles(self):
        """
        Private method to update the handles.
        """
        r = QRect(self.__selection)
        s2 = self.__handleSize // 2

        self.__TLHandle.moveTopLeft(r.topLeft())
        self.__TRHandle.moveTopRight(r.topRight())
        self.__BLHandle.moveBottomLeft(r.bottomLeft())
        self.__BRHandle.moveBottomRight(r.bottomRight())

        self.__LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() // 2 - s2))
        self.__THandle.moveTopLeft(QPoint(r.x() + r.width() // 2 - s2, r.y()))
        self.__RHandle.moveTopRight(
            QPoint(r.right(),
                   r.y() + r.height() // 2 - s2))
        self.__BHandle.moveBottomLeft(
            QPoint(r.x() + r.width() // 2 - s2, r.bottom()))

    def __handleMask(self, maskType):
        """
        Private method to calculate the handle mask.
        
        @param maskType type of the mask to be used
            (SnapshotRegionGrabber.FillMask or
            SnapshotRegionGrabber.StrokeMask)
        @return calculated mask (QRegion)
        """
        mask = QRegion()
        for rect in self.__handles:
            if maskType == SnapshotRegionGrabber.StrokeMask:
                r = QRegion(rect)
                mask += r.subtracted(QRegion(rect.adjusted(1, 1, -1, -1)))
            else:
                mask += QRegion(rect.adjusted(1, 1, -1, -1))
        return mask

    def __limitPointToRect(self, point, rect):
        """
        Private method to limit the given point to the given rectangle.
        
        @param point point to be limited (QPoint)
        @param rect rectangle the point shall be limited to (QRect)
        @return limited point (QPoint)
        """
        q = QPoint()
        if point.x() < rect.x():
            q.setX(rect.x())
        elif point.x() < rect.right():
            q.setX(point.x())
        else:
            q.setX(rect.right())
        if point.y() < rect.y():
            q.setY(rect.y())
        elif point.y() < rect.bottom():
            q.setY(point.y())
        else:
            q.setY(rect.bottom())
        return q

    def __normalizeSelection(self, sel):
        """
        Private method to normalize the given selection.
        
        @param sel selection to be normalized (QRect)
        @return normalized selection (QRect)
        """
        rect = QRect(sel)
        if rect.width() <= 0:
            left = rect.left()
            width = rect.width()
            rect.setLeft(left + width - 1)
            rect.setRight(left)
        if rect.height() <= 0:
            top = rect.top()
            height = rect.height()
            rect.setTop(top + height - 1)
            rect.setBottom(top)
        return rect

    def __grabRect(self):
        """
        Private method to grab the selected rectangle (i.e. do the snapshot).
        """
        if self.__mode == SnapshotRegionGrabber.Ellipse:
            ell = QRegion(self.__selection, QRegion.Ellipse)
            if not ell.isEmpty():
                self.__grabbing = True

                xOffset = self.__pixmap.rect().x() - ell.boundingRect().x()
                yOffset = self.__pixmap.rect().y() - ell.boundingRect().y()
                translatedEll = ell.translated(xOffset, yOffset)

                pixmap2 = QPixmap(ell.boundingRect().size())
                pixmap2.fill(Qt.transparent)

                pt = QPainter()
                pt.begin(pixmap2)
                if pt.paintEngine().hasFeature(QPaintEngine.PorterDuff):
                    pt.setRenderHints(
                        QPainter.Antialiasing
                        | QPainter.HighQualityAntialiasing
                        | QPainter.SmoothPixmapTransform, True)
                    pt.setBrush(Qt.black)
                    pt.setPen(QPen(QBrush(Qt.black), 0.5))
                    pt.drawEllipse(translatedEll.boundingRect())
                    pt.setCompositionMode(QPainter.CompositionMode_SourceIn)
                else:
                    pt.setClipRegion(translatedEll)
                    pt.setCompositionMode(QPainter.CompositionMode_Source)

                pt.drawPixmap(pixmap2.rect(), self.__pixmap,
                              ell.boundingRect())
                pt.end()

                self.grabbed.emit(pixmap2)
        else:
            r = QRect(self.__selection)
            if not r.isNull() and r.isValid():
                self.__grabbing = True
                self.grabbed.emit(self.__pixmap.copy(r))
Пример #20
0
class SnapshotRegionGrabber(QWidget):
    """
    Class implementing a grabber widget for a rectangular snapshot region.
    
    @signal grabbed(QPixmap) emitted after the region was grabbed
    """
    grabbed = pyqtSignal(QPixmap)
    
    StrokeMask = 0
    FillMask = 1
    
    Rectangle = 0
    Ellipse = 1
    
    def __init__(self, mode=Rectangle):
        """
        Constructor
        
        @param mode region grabber mode (SnapshotRegionGrabber.Rectangle or
            SnapshotRegionGrabber.Ellipse)
        """
        super(SnapshotRegionGrabber, self).__init__(
            None,
            Qt.X11BypassWindowManagerHint | Qt.WindowStaysOnTopHint |
            Qt.FramelessWindowHint | Qt.Tool)
        
        assert mode in [SnapshotRegionGrabber.Rectangle,
                        SnapshotRegionGrabber.Ellipse]
        self.__mode = mode
        
        self.__selection = QRect()
        self.__mouseDown = False
        self.__newSelection = False
        self.__handleSize = 10
        self.__mouseOverHandle = None
        self.__showHelp = True
        self.__grabbing = False
        self.__dragStartPoint = QPoint()
        self.__selectionBeforeDrag = QRect()
        
        # naming conventions for handles
        # T top, B bottom, R Right, L left
        # 2 letters: a corner
        # 1 letter: the handle on the middle of the corresponding side
        self.__TLHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__TRHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__BLHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__BRHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__LHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__THandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__RHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__BHandle = QRect(0, 0, self.__handleSize, self.__handleSize)
        self.__handles = [self.__TLHandle, self.__TRHandle, self.__BLHandle,
                          self.__BRHandle, self.__LHandle, self.__THandle,
                          self.__RHandle, self.__BHandle]
        self.__helpTextRect = QRect()
        self.__helpText = self.tr(
            "Select a region using the mouse. To take the snapshot, press"
            " the Enter key or double click. Press Esc to quit.")
        
        self.__pixmap = QPixmap()
        
        self.setMouseTracking(True)
        
        QTimer.singleShot(200, self.__initialize)
    
    def __initialize(self):
        """
        Private slot to initialize the rest of the widget.
        """
        self.__desktop = QApplication.desktop()
        x = self.__desktop.x()
        y = self.__desktop.y()
        if qVersion() >= "5.0.0":
            self.__pixmap = QApplication.screens()[0].grabWindow(
                self.__desktop.winId(), x, y,
                self.__desktop.width(), self.__desktop.height())
        else:
            self.__pixmap = QPixmap.grabWindow(
                self.__desktop.winId(), x, y,
                self.__desktop.width(), self.__desktop.height())
        self.resize(self.__pixmap.size())
        self.move(x, y)
        self.setCursor(Qt.CrossCursor)
        self.show()

        self.grabMouse()
        self.grabKeyboard()
    
    def paintEvent(self, evt):
        """
        Protected method handling paint events.
        
        @param evt paint event (QPaintEvent)
        """
        if self.__grabbing:     # grabWindow() should just get the background
            return
        
        painter = QPainter(self)
        pal = QPalette(QToolTip.palette())
        font = QToolTip.font()
        
        handleColor = pal.color(QPalette.Active, QPalette.Highlight)
        handleColor.setAlpha(160)
        overlayColor = QColor(0, 0, 0, 160)
        textColor = pal.color(QPalette.Active, QPalette.Text)
        textBackgroundColor = pal.color(QPalette.Active, QPalette.Base)
        painter.drawPixmap(0, 0, self.__pixmap)
        painter.setFont(font)
        
        r = QRect(self.__selection)
        if not self.__selection.isNull():
            grey = QRegion(self.rect())
            if self.__mode == SnapshotRegionGrabber.Ellipse:
                reg = QRegion(r, QRegion.Ellipse)
            else:
                reg = QRegion(r)
            grey = grey.subtracted(reg)
            painter.setClipRegion(grey)
            painter.setPen(Qt.NoPen)
            painter.setBrush(overlayColor)
            painter.drawRect(self.rect())
            painter.setClipRect(self.rect())
            drawRect(painter, r, handleColor)
        
        if self.__showHelp:
            painter.setPen(textColor)
            painter.setBrush(textBackgroundColor)
            self.__helpTextRect = painter.boundingRect(
                self.rect().adjusted(2, 2, -2, -2),
                Qt.TextWordWrap, self.__helpText).translated(
                -self.__desktop.x(), -self.__desktop.y())
            self.__helpTextRect.adjust(-2, -2, 4, 2)
            drawRect(painter, self.__helpTextRect, textColor,
                     textBackgroundColor)
            painter.drawText(
                self.__helpTextRect.adjusted(3, 3, -3, -3),
                Qt.TextWordWrap, self.__helpText)
        
        if self.__selection.isNull():
            return
        
        # The grabbed region is everything which is covered by the drawn
        # rectangles (border included). This means that there is no 0px
        # selection, since a 0px wide rectangle will always be drawn as a line.
        txt = "{0:n}, {1:n} ({2:n} x {3:n})".format(
            self.__selection.x(), self.__selection.y(),
            self.__selection.width(), self.__selection.height())
        textRect = painter.boundingRect(self.rect(), Qt.AlignLeft, txt)
        boundingRect = textRect.adjusted(-4, 0, 0, 0)
        
        if textRect.width() < r.width() - 2 * self.__handleSize and \
           textRect.height() < r.height() - 2 * self.__handleSize and \
           r.width() > 100 and \
           r.height() > 100:
            # center, unsuitable for small selections
            boundingRect.moveCenter(r.center())
            textRect.moveCenter(r.center())
        elif r.y() - 3 > textRect.height() and \
                r.x() + textRect.width() < self.rect().width():
            # on top, left aligned
            boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3))
            textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3))
        elif r.x() - 3 > textRect.width():
            # left, top aligned
            boundingRect.moveTopRight(QPoint(r.x() - 3, r.y()))
            textRect.moveTopRight(QPoint(r.x() - 5, r.y()))
        elif r.bottom() + 3 + textRect.height() < self.rect().bottom() and \
                r.right() > textRect.width():
            # at bottom, right aligned
            boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3))
            textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3))
        elif r.right() + textRect.width() + 3 < self.rect().width():
            # right, bottom aligned
            boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom()))
            textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom()))
        
        # If the above didn't catch it, you are running on a very
        # tiny screen...
        drawRect(painter, boundingRect, textColor, textBackgroundColor)
        painter.drawText(textRect, Qt.AlignHCenter, txt)
        
        if (r.height() > self.__handleSize * 2 and
            r.width() > self.__handleSize * 2) or \
           not self.__mouseDown:
            self.__updateHandles()
            painter.setPen(Qt.NoPen)
            painter.setBrush(handleColor)
            painter.setClipRegion(
                self.__handleMask(SnapshotRegionGrabber.StrokeMask))
            painter.drawRect(self.rect())
            handleColor.setAlpha(60)
            painter.setBrush(handleColor)
            painter.setClipRegion(
                self.__handleMask(SnapshotRegionGrabber.FillMask))
            painter.drawRect(self.rect())
    
    def resizeEvent(self, evt):
        """
        Protected method to handle resize events.
        
        @param evt resize event (QResizeEvent)
        """
        if self.__selection.isNull():
            return
        
        r = QRect(self.__selection)
        r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect()))
        r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect()))
        if r.width() <= 1 or r.height() <= 1:
            # This just results in ugly drawing...
            self.__selection = QRect()
        else:
            self.__selection = self.__normalizeSelection(r)
    
    def mousePressEvent(self, evt):
        """
        Protected method to handle mouse button presses.
        
        @param evt mouse press event (QMouseEvent)
        """
        self.__showHelp = not self.__helpTextRect.contains(evt.pos())
        if evt.button() == Qt.LeftButton:
            self.__mouseDown = True
            self.__dragStartPoint = evt.pos()
            self.__selectionBeforeDrag = QRect(self.__selection)
            if not self.__selection.contains(evt.pos()):
                self.__newSelection = True
                self.__selection = QRect()
            else:
                self.setCursor(Qt.ClosedHandCursor)
        elif evt.button() == Qt.RightButton:
            self.__newSelection = False
            self.__selection = QRect()
            self.setCursor(Qt.CrossCursor)
        self.update()
    
    def mouseMoveEvent(self, evt):
        """
        Protected method to handle mouse movements.
        
        @param evt mouse move event (QMouseEvent)
        """
        shouldShowHelp = not self.__helpTextRect.contains(evt.pos())
        if shouldShowHelp != self.__showHelp:
            self.__showHelp = shouldShowHelp
            self.update()
        
        if self.__mouseDown:
            if self.__newSelection:
                p = evt.pos()
                r = self.rect()
                self.__selection = self.__normalizeSelection(
                    QRect(self.__dragStartPoint,
                          self.__limitPointToRect(p, r)))
            elif self.__mouseOverHandle is None:
                # moving the whole selection
                r = self.rect().normalized()
                s = self.__selectionBeforeDrag.normalized()
                p = s.topLeft() + evt.pos() - self.__dragStartPoint
                r.setBottomRight(
                    r.bottomRight() - QPoint(s.width(), s.height()) +
                    QPoint(1, 1))
                if not r.isNull() and r.isValid():
                    self.__selection.moveTo(self.__limitPointToRect(p, r))
            else:
                # dragging a handle
                r = QRect(self.__selectionBeforeDrag)
                offset = evt.pos() - self.__dragStartPoint
                
                if self.__mouseOverHandle in \
                   [self.__TLHandle, self.__THandle, self.__TRHandle]:
                    r.setTop(r.top() + offset.y())
                
                if self.__mouseOverHandle in \
                   [self.__TLHandle, self.__LHandle, self.__BLHandle]:
                    r.setLeft(r.left() + offset.x())
                
                if self.__mouseOverHandle in \
                   [self.__BLHandle, self.__BHandle, self.__BRHandle]:
                    r.setBottom(r.bottom() + offset.y())
                
                if self.__mouseOverHandle in \
                   [self.__TRHandle, self.__RHandle, self.__BRHandle]:
                    r.setRight(r.right() + offset.x())
                
                r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect()))
                r.setBottomRight(
                    self.__limitPointToRect(r.bottomRight(), self.rect()))
                self.__selection = self.__normalizeSelection(r)
            
            self.update()
        else:
            if self.__selection.isNull():
                return
            
            found = False
            for r in self.__handles:
                if r.contains(evt.pos()):
                    self.__mouseOverHandle = r
                    found = True
                    break
            
            if not found:
                self.__mouseOverHandle = None
                if self.__selection.contains(evt.pos()):
                    self.setCursor(Qt.OpenHandCursor)
                else:
                    self.setCursor(Qt.CrossCursor)
            else:
                if self.__mouseOverHandle in [self.__TLHandle,
                                              self.__BRHandle]:
                    self.setCursor(Qt.SizeFDiagCursor)
                elif self.__mouseOverHandle in [self.__TRHandle,
                                                self.__BLHandle]:
                    self.setCursor(Qt.SizeBDiagCursor)
                elif self.__mouseOverHandle in [self.__LHandle,
                                                self.__RHandle]:
                    self.setCursor(Qt.SizeHorCursor)
                elif self.__mouseOverHandle in [self.__THandle,
                                                self.__BHandle]:
                    self.setCursor(Qt.SizeVerCursor)
    
    def mouseReleaseEvent(self, evt):
        """
        Protected method to handle mouse button releases.
        
        @param evt mouse release event (QMouseEvent)
        """
        self.__mouseDown = False
        self.__newSelection = False
        if self.__mouseOverHandle is None and \
           self.__selection.contains(evt.pos()):
            self.setCursor(Qt.OpenHandCursor)
        self.update()
    
    def mouseDoubleClickEvent(self, evt):
        """
        Protected method to handle mouse double clicks.
        
        @param evt mouse double click event (QMouseEvent)
        """
        self.__grabRect()
    
    def keyPressEvent(self, evt):
        """
        Protected method to handle key presses.
        
        @param evt key press event (QKeyEvent)
        """
        if evt.key() == Qt.Key_Escape:
            self.grabbed.emit(QPixmap())
        elif evt.key() in [Qt.Key_Enter, Qt.Key_Return]:
            self.__grabRect()
        else:
            evt.ignore()
    
    def __updateHandles(self):
        """
        Private method to update the handles.
        """
        r = QRect(self.__selection)
        s2 = self.__handleSize // 2
        
        self.__TLHandle.moveTopLeft(r.topLeft())
        self.__TRHandle.moveTopRight(r.topRight())
        self.__BLHandle.moveBottomLeft(r.bottomLeft())
        self.__BRHandle.moveBottomRight(r.bottomRight())
        
        self.__LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() // 2 - s2))
        self.__THandle.moveTopLeft(QPoint(r.x() + r.width() // 2 - s2, r.y()))
        self.__RHandle.moveTopRight(
            QPoint(r.right(), r.y() + r.height() // 2 - s2))
        self.__BHandle.moveBottomLeft(
            QPoint(r.x() + r.width() // 2 - s2, r.bottom()))
    
    def __handleMask(self, maskType):
        """
        Private method to calculate the handle mask.
        
        @param maskType type of the mask to be used
            (SnapshotRegionGrabber.FillMask or
            SnapshotRegionGrabber.StrokeMask)
        @return calculated mask (QRegion)
        """
        mask = QRegion()
        for rect in self.__handles:
            if maskType == SnapshotRegionGrabber.StrokeMask:
                r = QRegion(rect)
                mask += r.subtracted(QRegion(rect.adjusted(1, 1, -1, -1)))
            else:
                mask += QRegion(rect.adjusted(1, 1, -1, -1))
        return mask
    
    def __limitPointToRect(self, point, rect):
        """
        Private method to limit the given point to the given rectangle.
        
        @param point point to be limited (QPoint)
        @param rect rectangle the point shall be limited to (QRect)
        @return limited point (QPoint)
        """
        q = QPoint()
        if point.x() < rect.x():
            q.setX(rect.x())
        elif point.x() < rect.right():
            q.setX(point.x())
        else:
            q.setX(rect.right())
        if point.y() < rect.y():
            q.setY(rect.y())
        elif point.y() < rect.bottom():
            q.setY(point.y())
        else:
            q.setY(rect.bottom())
        return q
    
    def __normalizeSelection(self, sel):
        """
        Private method to normalize the given selection.
        
        @param sel selection to be normalized (QRect)
        @return normalized selection (QRect)
        """
        rect = QRect(sel)
        if rect.width() <= 0:
            left = rect.left()
            width = rect.width()
            rect.setLeft(left + width - 1)
            rect.setRight(left)
        if rect.height() <= 0:
            top = rect.top()
            height = rect.height()
            rect.setTop(top + height - 1)
            rect.setBottom(top)
        return rect
    
    def __grabRect(self):
        """
        Private method to grab the selected rectangle (i.e. do the snapshot).
        """
        if self.__mode == SnapshotRegionGrabber.Ellipse:
            ell = QRegion(self.__selection, QRegion.Ellipse)
            if not ell.isEmpty():
                self.__grabbing = True
                
                xOffset = self.__pixmap.rect().x() - ell.boundingRect().x()
                yOffset = self.__pixmap.rect().y() - ell.boundingRect().y()
                translatedEll = ell.translated(xOffset, yOffset)
                
                pixmap2 = QPixmap(ell.boundingRect().size())
                pixmap2.fill(Qt.transparent)
                
                pt = QPainter()
                pt.begin(pixmap2)
                if pt.paintEngine().hasFeature(QPaintEngine.PorterDuff):
                    pt.setRenderHints(
                        QPainter.Antialiasing |
                        QPainter.HighQualityAntialiasing |
                        QPainter.SmoothPixmapTransform,
                        True)
                    pt.setBrush(Qt.black)
                    pt.setPen(QPen(QBrush(Qt.black), 0.5))
                    pt.drawEllipse(translatedEll.boundingRect())
                    pt.setCompositionMode(QPainter.CompositionMode_SourceIn)
                else:
                    pt.setClipRegion(translatedEll)
                    pt.setCompositionMode(QPainter.CompositionMode_Source)
                
                pt.drawPixmap(pixmap2.rect(), self.__pixmap,
                              ell.boundingRect())
                pt.end()
                
                self.grabbed.emit(pixmap2)
        else:
            r = QRect(self.__selection)
            if not r.isNull() and r.isValid():
                self.__grabbing = True
                self.grabbed.emit(self.__pixmap.copy(r))
Пример #21
0
    def paint(self, painter, option: QStyleOptionViewItem, index: QModelIndex):
        if index.isValid():
            text_alignment = index.data(Qt.TextAlignmentRole)
            if not text_alignment:
                text_alignment = Qt.AlignLeft | Qt.AlignVCenter
            fg_color = index.data(Qt.ForegroundRole)
            if not fg_color:
                fg_color = Qt.black
            data = index.data()

            if isinstance(self.view, QTableView):
                view_has_focus = self.view.hasFocus()
                select_whole_row = self.view.selectionBehavior(
                ) == QAbstractItemView.SelectRows
            else:
                view_has_focus = False
                select_whole_row = False

            painter.save()

            painter.setPen(QPen(Qt.NoPen))
            if option.state & QStyle.State_Selected:
                if (option.state
                        & QStyle.State_HasFocus) or (view_has_focus
                                                     and select_whole_row):
                    fg_color = option.palette.color(
                        QPalette.Normal, option.palette.HighlightedText)
                    painter.fillRect(
                        option.rect,
                        QBrush(
                            option.palette.color(QPalette.Active,
                                                 option.palette.Highlight)))
                else:
                    fg_color = option.palette.color(
                        QPalette.Inactive, option.palette.HighlightedText)
                    painter.fillRect(
                        option.rect,
                        QBrush(
                            option.palette.color(QPalette.Inactive,
                                                 option.palette.Highlight)))
            else:
                painter.setBrush(QBrush(self.background_color))
                fg_color = option.palette.color(QPalette.Normal,
                                                option.palette.WindowText)

            r = option.rect
            r.translate(IconTextItemDelegate.CellHorizontalMargin,
                        IconTextItemDelegate.CellVerticalMargin)

            offs = 0
            if isinstance(data, tuple) and len(data) > 0 and isinstance(
                    data[0], QPixmap):
                offs += 1
                pix = data[0]
                if pix:
                    rp = QRect(r)
                    rp.setWidth(pix.width())
                    rp.setHeight(pix.height())

                    if text_alignment & Qt.AlignVCenter:
                        # align incon vertically if the text is aligned so
                        diff_height = r.height() - pix.height()
                        if diff_height > 2:
                            diff_offs = int(diff_height / 2)
                            if diff_offs:
                                rp.adjust(0, diff_offs, 0, diff_offs)
                    painter.drawImage(rp, pix.toImage())
                    r.translate(
                        rp.width() + IconTextItemDelegate.IconRightMargin, 0)
                    r.setWidth(r.width() - rp.width() -
                               IconTextItemDelegate.IconRightMargin)

            if isinstance(data, tuple) and len(data) > offs and isinstance(
                    data[offs], str):
                text = data[offs]
            elif isinstance(data, str):
                text = data
            else:
                text = ''

            if text:
                painter.setPen(QPen(fg_color))
                painter.setFont(option.font)
                painter.drawText(r, text_alignment, text)
            painter.restore()
Пример #22
0
class Surface(QWidget):
    
    rightClicked = pyqtSignal(QPoint)
    linkClicked = pyqtSignal(QEvent, page.Page, popplerqt5.Poppler.Link)
    linkHovered = pyqtSignal(page.Page, popplerqt5.Poppler.Link)
    linkLeft = pyqtSignal()
    linkHelpRequested = pyqtSignal(QPoint, page.Page, popplerqt5.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.view())
        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.setVisible(rect.isValid())
        self._rubberBand.setGeometry(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 selectedPages(self):
        """Return a list of the Page objects the selection encompasses."""
        return list(self.pageLayout().pagesAt(self.selection()))
    
    def selectedPage(self):
        """Return the Page thas is selected for the largest part, or None."""
        pages = self.selectedPages()
        if not pages:
            return
        def key(page):
            size = page.rect().intersected(self.selection()).size()
            return size.width() + size.height()
        return max(pages, key = key)
    
    def selectedPageRect(self, page):
        """Return the QRect on the page that falls in the selection."""
        return self.selection().normalized().intersected(page.rect()).translated(-page.pos())

    def selectedText(self):
        """Return all text falling in the selection."""
        return '\n'.join(page.text(self.selection()) for page in self.selectedPages())
    
    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 = collections.defaultdict(list)
        for page, area in areas:
            d[page].append(area)
        d = weakref.WeakKeyDictionary(d)
        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, popplerqt5.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 emits 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)
        rect = self._selectionRect.normalized()
        self._rubberBand.setVisible(rect.isValid())
        self._rubberBand.setGeometry(rect)
        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)
Пример #23
0
class SnapshotFreehandGrabber(QWidget):
    """
    Class implementing a grabber widget for a freehand snapshot region.
    
    @signal grabbed(QPixmap) emitted after the region was grabbed
    """
    grabbed = pyqtSignal(QPixmap)
    
    def __init__(self):
        """
        Constructor
        """
        super(SnapshotFreehandGrabber, self).__init__(
            None,
            Qt.X11BypassWindowManagerHint | Qt.WindowStaysOnTopHint |
            Qt.FramelessWindowHint | Qt.Tool)
        
        self.__selection = QPolygon()
        self.__mouseDown = False
        self.__newSelection = False
        self.__handleSize = 10
        self.__showHelp = True
        self.__grabbing = False
        self.__dragStartPoint = QPoint()
        self.__selectionBeforeDrag = QPolygon()
        self.__locale = QLocale()
        
        self.__helpTextRect = QRect()
        self.__helpText = self.tr(
            "Select a region using the mouse. To take the snapshot,"
            " press the Enter key or double click. Press Esc to quit.")
        
        self.__pixmap = QPixmap()
        self.__pBefore = QPoint()
        
        self.setMouseTracking(True)
        
        QTimer.singleShot(200, self.__initialize)
    
    def __initialize(self):
        """
        Private slot to initialize the rest of the widget.
        """
        self.__desktop = QApplication.desktop()
        x = self.__desktop.x()
        y = self.__desktop.y()
        if qVersion() >= "5.0.0":
            self.__pixmap = QApplication.screens()[0].grabWindow(
                self.__desktop.winId(), x, y,
                self.__desktop.width(), self.__desktop.height())
        else:
            self.__pixmap = QPixmap.grabWindow(
                self.__desktop.winId(), x, y,
                self.__desktop.width(), self.__desktop.height())
        self.resize(self.__pixmap.size())
        self.move(x, y)
        self.setCursor(Qt.CrossCursor)
        self.show()

        self.grabMouse()
        self.grabKeyboard()
        self.activateWindow()
    
    def paintEvent(self, evt):
        """
        Protected method handling paint events.
        
        @param evt paint event (QPaintEvent)
        """
        if self.__grabbing:     # grabWindow() should just get the background
            return
        
        painter = QPainter(self)
        pal = QPalette(QToolTip.palette())
        font = QToolTip.font()
        
        handleColor = pal.color(QPalette.Active, QPalette.Highlight)
        handleColor.setAlpha(160)
        overlayColor = QColor(0, 0, 0, 160)
        textColor = pal.color(QPalette.Active, QPalette.Text)
        textBackgroundColor = pal.color(QPalette.Active, QPalette.Base)
        painter.drawPixmap(0, 0, self.__pixmap)
        painter.setFont(font)
        
        pol = QPolygon(self.__selection)
        if not self.__selection.boundingRect().isNull():
            # Draw outline around selection.
            # Important: the 1px-wide outline is *also* part of the
            # captured free-region
            pen = QPen(handleColor, 1, Qt.SolidLine, Qt.SquareCap,
                       Qt.BevelJoin)
            painter.setPen(pen)
            painter.drawPolygon(pol)
            
            # Draw the grey area around the selection.
            grey = QRegion(self.rect())
            grey = grey - QRegion(pol)
            painter.setClipRegion(grey)
            painter.setPen(Qt.NoPen)
            painter.setBrush(overlayColor)
            painter.drawRect(self.rect())
            painter.setClipRect(self.rect())
            drawPolygon(painter, pol, handleColor)
        
        if self.__showHelp:
            painter.setPen(textColor)
            painter.setBrush(textBackgroundColor)
            self.__helpTextRect = painter.boundingRect(
                self.rect().adjusted(2, 2, -2, -2),
                Qt.TextWordWrap, self.__helpText).translated(
                -self.__desktop.x(), -self.__desktop.y())
            self.__helpTextRect.adjust(-2, -2, 4, 2)
            drawPolygon(painter, self.__helpTextRect, textColor,
                        textBackgroundColor)
            painter.drawText(
                self.__helpTextRect.adjusted(3, 3, -3, -3),
                Qt.TextWordWrap, self.__helpText)
        
        if self.__selection.isEmpty():
            return
        
        # The grabbed region is everything which is covered by the drawn
        # rectangles (border included). This means that there is no 0px
        # selection, since a 0px wide rectangle will always be drawn as a line.
        boundingRect = self.__selection.boundingRect()
        txt = "{0}, {1} ({2} x {3})".format(
            self.__locale.toString(boundingRect.x()),
            self.__locale.toString(boundingRect.y()),
            self.__locale.toString(boundingRect.width()),
            self.__locale.toString(boundingRect.height())
        )
        textRect = painter.boundingRect(self.rect(), Qt.AlignLeft, txt)
        boundingRect = textRect.adjusted(-4, 0, 0, 0)
        
        polBoundingRect = pol.boundingRect()
        if (textRect.width() <
            polBoundingRect.width() - 2 * self.__handleSize) and \
           (textRect.height() <
            polBoundingRect.height() - 2 * self.__handleSize) and \
           polBoundingRect.width() > 100 and \
           polBoundingRect.height() > 100:
            # center, unsuitable for small selections
            boundingRect.moveCenter(polBoundingRect.center())
            textRect.moveCenter(polBoundingRect.center())
        elif polBoundingRect.y() - 3 > textRect.height() and \
                polBoundingRect.x() + textRect.width() < self.rect().width():
            # on top, left aligned
            boundingRect.moveBottomLeft(
                QPoint(polBoundingRect.x(), polBoundingRect.y() - 3))
            textRect.moveBottomLeft(
                QPoint(polBoundingRect.x() + 2, polBoundingRect.y() - 3))
        elif polBoundingRect.x() - 3 > textRect.width():
            # left, top aligned
            boundingRect.moveTopRight(
                QPoint(polBoundingRect.x() - 3, polBoundingRect.y()))
            textRect.moveTopRight(
                QPoint(polBoundingRect.x() - 5, polBoundingRect.y()))
        elif (polBoundingRect.bottom() + 3 + textRect.height() <
              self.rect().bottom()) and \
                polBoundingRect.right() > textRect.width():
            # at bottom, right aligned
            boundingRect.moveTopRight(
                QPoint(polBoundingRect.right(), polBoundingRect.bottom() + 3))
            textRect.moveTopRight(
                QPoint(polBoundingRect.right() - 2,
                       polBoundingRect.bottom() + 3))
        elif polBoundingRect.right() + textRect.width() + 3 < \
                self.rect().width():
            # right, bottom aligned
            boundingRect.moveBottomLeft(
                QPoint(polBoundingRect.right() + 3, polBoundingRect.bottom()))
            textRect.moveBottomLeft(
                QPoint(polBoundingRect.right() + 5, polBoundingRect.bottom()))
        
        # If the above didn't catch it, you are running on a very
        # tiny screen...
        drawPolygon(painter, boundingRect, textColor, textBackgroundColor)
        painter.drawText(textRect, Qt.AlignHCenter, txt)
        
        if (polBoundingRect.height() > self.__handleSize * 2 and
            polBoundingRect.width() > self.__handleSize * 2) or \
           not self.__mouseDown:
            painter.setBrush(Qt.transparent)
            painter.setClipRegion(QRegion(pol))
            painter.drawPolygon(QPolygon(self.rect()))
    
    def mousePressEvent(self, evt):
        """
        Protected method to handle mouse button presses.
        
        @param evt mouse press event (QMouseEvent)
        """
        self.__pBefore = evt.pos()
        
        self.__showHelp = not self.__helpTextRect.contains(evt.pos())
        if evt.button() == Qt.LeftButton:
            self.__mouseDown = True
            self.__dragStartPoint = evt.pos()
            self.__selectionBeforeDrag = QPolygon(self.__selection)
            if not self.__selection.containsPoint(evt.pos(), Qt.WindingFill):
                self.__newSelection = True
                self.__selection = QPolygon()
            else:
                self.setCursor(Qt.ClosedHandCursor)
        elif evt.button() == Qt.RightButton:
            self.__newSelection = False
            self.__selection = QPolygon()
            self.setCursor(Qt.CrossCursor)
        self.update()
    
    def mouseMoveEvent(self, evt):
        """
        Protected method to handle mouse movements.
        
        @param evt mouse move event (QMouseEvent)
        """
        shouldShowHelp = not self.__helpTextRect.contains(evt.pos())
        if shouldShowHelp != self.__showHelp:
            self.__showHelp = shouldShowHelp
            self.update()
        
        if self.__mouseDown:
            if self.__newSelection:
                p = evt.pos()
                self.__selection.append(p)
            else:
                # moving the whole selection
                p = evt.pos() - self.__pBefore  # Offset
                self.__pBefore = evt.pos()  # save position for next iteration
                self.__selection.translate(p)
            
            self.update()
        else:
            if self.__selection.boundingRect().isEmpty():
                return
            
            if self.__selection.containsPoint(evt.pos(), Qt.WindingFill):
                self.setCursor(Qt.OpenHandCursor)
            else:
                self.setCursor(Qt.CrossCursor)
    
    def mouseReleaseEvent(self, evt):
        """
        Protected method to handle mouse button releases.
        
        @param evt mouse release event (QMouseEvent)
        """
        self.__mouseDown = False
        self.__newSelection = False
        if self.__selection.containsPoint(evt.pos(), Qt.WindingFill):
            self.setCursor(Qt.OpenHandCursor)
        self.update()
    
    def mouseDoubleClickEvent(self, evt):
        """
        Protected method to handle mouse double clicks.
        
        @param evt mouse double click event (QMouseEvent)
        """
        self.__grabRegion()
    
    def keyPressEvent(self, evt):
        """
        Protected method to handle key presses.
        
        @param evt key press event (QKeyEvent)
        """
        if evt.key() == Qt.Key_Escape:
            self.grabbed.emit(QPixmap())
        elif evt.key() in [Qt.Key_Enter, Qt.Key_Return]:
            self.__grabRegion()
        else:
            evt.ignore()
    
    def __grabRegion(self):
        """
        Private method to grab the selected region (i.e. do the snapshot).
        """
        pol = QPolygon(self.__selection)
        if not pol.isEmpty():
            self.__grabbing = True
            
            xOffset = self.__pixmap.rect().x() - pol.boundingRect().x()
            yOffset = self.__pixmap.rect().y() - pol.boundingRect().y()
            translatedPol = pol.translated(xOffset, yOffset)
            
            pixmap2 = QPixmap(pol.boundingRect().size())
            pixmap2.fill(Qt.transparent)
            
            pt = QPainter()
            pt.begin(pixmap2)
            if pt.paintEngine().hasFeature(QPaintEngine.PorterDuff):
                pt.setRenderHints(
                    QPainter.Antialiasing |
                    QPainter.HighQualityAntialiasing |
                    QPainter.SmoothPixmapTransform,
                    True)
                pt.setBrush(Qt.black)
                pt.setPen(QPen(QBrush(Qt.black), 0.5))
                pt.drawPolygon(translatedPol)
                pt.setCompositionMode(QPainter.CompositionMode_SourceIn)
            else:
                pt.setClipRegion(QRegion(translatedPol))
                pt.setCompositionMode(QPainter.CompositionMode_Source)
            
            pt.drawPixmap(pixmap2.rect(), self.__pixmap, pol.boundingRect())
            pt.end()
            
            self.grabbed.emit(pixmap2)
Пример #24
0
    def paintEvent(self, event):

        if not self.customStyle:
            return QGroupBox.paintEvent(self, event)

        p = QStylePainter(self)
        opt = QStyleOptionGroupBox()
        self.initStyleOption(opt)

        style = qApp.style()
        groupBox = opt

        # // Draw frame
        textRect = style.subControlRect(style.CC_GroupBox, opt, style.SC_GroupBoxLabel)
        checkBoxRect = style.subControlRect(style.CC_GroupBox, opt, style.SC_GroupBoxCheckBox)

        p.save()
        titleRect = style.subControlRect(style.CC_GroupBox, opt, style.SC_GroupBoxFrame)
        # r.setBottom(style.subControlRect(style.CC_GroupBox, opt, style.SC_GroupBoxContents).top())
        titleRect.setHeight(textRect.height())
        titleRect.moveTop(textRect.top())

        p.setBrush(QBrush(QColor(Qt.blue).lighter(190)))
        p.setPen(Qt.NoPen)
        p.drawRoundedRect(titleRect, 10, 10)
        p.restore()

        if groupBox.subControls & QStyle.SC_GroupBoxFrame:
            frame = QStyleOptionFrame()
            # frame.operator=(groupBox)
            frame.state = groupBox.state
            frame.features = groupBox.features
            frame.lineWidth = groupBox.lineWidth
            frame.midLineWidth = groupBox.midLineWidth
            frame.rect = style.subControlRect(style.CC_GroupBox, opt, style.SC_GroupBoxFrame)
            p.save()
            region = QRegion(groupBox.rect)
            if groupBox.text:
                ltr = groupBox.direction == Qt.LeftToRight
                finalRect = QRect()
                if groupBox.subControls & QStyle.SC_GroupBoxCheckBox:
                    finalRect = checkBoxRect.united(textRect)
                    finalRect.adjust(-4 if ltr else 0, 0, 0 if ltr else 4, 0)
                else:
                    finalRect = textRect

                region -= QRegion(finalRect)

            p.setClipRegion(region)
            style.drawPrimitive(style.PE_FrameGroupBox, frame, p)
            p.restore()

        # // Draw title
        if groupBox.subControls & QStyle.SC_GroupBoxLabel and groupBox.text:
            # textColor = QColor(groupBox.textColor)
            # if textColor.isValid():
            # p.setPen(textColor)
            # alignment = int(groupBox.textAlignment)
            # if not style.styleHint(QStyle.SH_UnderlineShortcut, opt):
            # alignment |= Qt.TextHideMnemonic

            # style.drawItemText(p, textRect,  Qt.TextShowMnemonic | Qt.AlignHCenter | alignment,
            # groupBox.palette, groupBox.state & style.State_Enabled, groupBox.text,
            # QPalette.NoRole if textColor.isValid() else QPalette.WindowText)

            p.save()
            topt = QTextOption(Qt.AlignHCenter | Qt.AlignVCenter)
            f = QFont()
            f.setBold(True)
            p.setFont(f)
            p.setPen(Qt.darkBlue)
            p.drawText(QRectF(titleRect), groupBox.text.replace("&", ""), topt)
            p.restore()

            if groupBox.state & style.State_HasFocus:
                fropt = QStyleOptionFocusRect()
                # fropt.operator=(groupBox)
                fropt.state = groupBox.state
                fropt.rect = textRect
                style.drawPrimitive(style.PE_FrameFocusRect, fropt, p)
Пример #25
0
class Surface(QWidget):

    rightClicked = pyqtSignal(QPoint)
    linkClicked = pyqtSignal(QEvent, page.Page, popplerqt5.Poppler.Link)
    linkHovered = pyqtSignal(page.Page, popplerqt5.Poppler.Link)
    linkLeft = pyqtSignal()
    linkHelpRequested = pyqtSignal(QPoint, page.Page, popplerqt5.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.view())
        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.setVisible(rect.isValid())
        self._rubberBand.setGeometry(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 selectedPages(self):
        """Return a list of the Page objects the selection encompasses."""
        return list(self.pageLayout().pagesAt(self.selection()))

    def selectedPage(self):
        """Return the Page thas is selected for the largest part, or None."""
        pages = self.selectedPages()
        if not pages:
            return

        def key(page):
            size = page.rect().intersected(self.selection()).size()
            return size.width() + size.height()

        return max(pages, key=key)

    def selectedPageRect(self, page):
        """Return the QRect on the page that falls in the selection."""
        return self.selection().normalized().intersected(
            page.rect()).translated(-page.pos())

    def selectedText(self):
        """Return all text falling in the selection."""
        return '\n'.join(
            page.text(self.selection()) for page in self.selectedPages())

    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 = collections.defaultdict(list)
        for page, area in areas:
            d[page].append(area)
        d = weakref.WeakKeyDictionary(d)
        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,
                                            popplerqt5.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 emits 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)
        rect = self._selectionRect.normalized()
        self._rubberBand.setVisible(rect.isValid())
        self._rubberBand.setGeometry(rect)
        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)
Пример #26
0
 def renderRect(self) -> QRect:
     rect = QRect(self.rect())
     rect.adjust(0, self.frame_width, 0, 0)
     return rect