Search code examples
pythonpdfpyqt6

How to Subclass QPdfview to add ability to draw rectangles on a pdf?


I would like to be able to draw a rectangle on a pdf inside QPdfView

I attempted to achieve this by creating a subclass based on this:

https://www.pythonguis.com/faq/use-mouse-drag-to-change-the-width-of-a-rectangle/

FREE_STATE = 1
BUILDING_SQUARE = 2
BEGIN_SIDE_EDIT = 3
END_SIDE_EDIT = 4
    


class customQPdfView(QPdfView):
    def __init__(self,parent):
        super().__init__(parent)
        
        self.setGeometry(30, 30, 600, 400)
        self.begin = QPoint()
        self.end = QPoint()

        self.state = FREE_STATE
    
    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self.viewport())
        painter.drawRect(QRect(self.begin, self.end))

    def mousePressEvent(self, event):
        print('press')
        if not self.begin.isNull() and not self.end.isNull():
            p = event.pos()
            y1, y2 = sorted([self.begin.y(), self.end.y()])
            if y1 <= p.y() <= y2:
                # 3 resolution, more easy to pick than 1px
                if abs(self.begin.x() - p.x()) <= 3:
                    self.state = BEGIN_SIDE_EDIT
                    return
                elif abs(self.end.x() - p.x()) <= 3:
                    self.state = END_SIDE_EDIT
                    return
        self.state = BUILDING_SQUARE

        self.begin = event.pos()
        self.end = event.pos()
        self.update()

    def applye_event(self, event):

        if self.state == BUILDING_SQUARE:
            self.end = event.pos()
        elif self.state == BEGIN_SIDE_EDIT:
            self.begin.setX(event.x())
        elif self.state == END_SIDE_EDIT:
            self.end.setX(event.x())

    def mouseMoveEvent(self, event):
        print('move')
        self.applye_event(event)
        self.update()

    def mouseReleaseEvent(self, event):
        print('release')
        self.applye_event(event)
        self.state = FREE_STATE

What happens is I load my pdf and the only way I can see the rectangles is to refresh the pdf by moving to another page...Is the problem I am writing the rectangles to the widget and not the pdf?


Solution

  • #Replaced self.update() with self.viewport().repaint()
    
    FREE_STATE = 1
    BUILDING_SQUARE = 2
    BEGIN_SIDE_EDIT = 3
    END_SIDE_EDIT = 4
    
    class customQPdfView(QPdfView):
        def __init__(self,parent):
            super().__init__(parent)
            
            self.setGeometry(30, 30, 600, 400)
            self.begin = QPoint()
            self.end = QPoint()
    
            self.state = FREE_STATE
        
        def paintEvent(self, event):
            super().paintEvent(event)
            painter = QPainter(self.viewport())
            painter.drawRect(QRect(self.begin, self.end))
    
        def mousePressEvent(self, event):
            print('press')
            if not self.begin.isNull() and not self.end.isNull():
                p = event.pos()
                y1, y2 = sorted([self.begin.y(), self.end.y()])
                if y1 <= p.y() <= y2:
                    # 3 resolution, more easy to pick than 1px
                    if abs(self.begin.x() - p.x()) <= 3:
                        self.state = BEGIN_SIDE_EDIT
                        return
                    elif abs(self.end.x() - p.x()) <= 3:
                        self.state = END_SIDE_EDIT
                        return
            self.state = BUILDING_SQUARE
            self.begin = event.pos()
            self.end = event.pos()
    
        def applye_event(self, event):
    
            if self.state == BUILDING_SQUARE:
                self.end = event.pos()
            elif self.state == BEGIN_SIDE_EDIT:
                self.begin.setX(event.x())
            elif self.state == END_SIDE_EDIT:
                self.end.setX(event.x())
          
        def mouseMoveEvent(self, event):
            print('move')
            self.applye_event(event)
            self.viewport().repaint()
    
        def mouseReleaseEvent(self, event):
            print('release')
            self.applye_event(event)
            self.state = FREE_STATE