My goal is to create a PDF viewer that allows to draw a rectangle in front of the text. Right now the viewer part is done as well as the drawing rectangles part. The problem is, they don't work together. As soon as I load a PDF file, I can no longer draw my shapes over it.I highly suspect that the problem is wrong QPixmap or QPainter argument somewhere. Unfortunately, I'm very much nooby with PyQt6, so I don't really understand how those classes work (I've read the docs).I'll be really grateful if some noble soul would help me with this problem.Screenshot - Left: I can draw rectangles. Right: I open PDF and rectangles disappear and I can't draw new ones.
import sysfrom pathlib import Pathfrom PyQt6 import QtWidgetsfrom PyQt6.QtCore import QPointF, QRect, QPoint, Qtfrom PyQt6.QtGui import QAction, QPainter, QPixmapfrom PyQt6.QtPdf import QPdfDocumentfrom PyQt6.QtPdfWidgets import QPdfViewfrom PyQt6.QtWidgets import QFileDialog, QApplicationclass MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() # Painting settings self.pix = QPixmap(self.rect().size()) self.pix.fill(Qt.GlobalColor.transparent) self.point1, self.point2 = QPoint(), QPoint() # PDF settings self.pdf_document = QPdfDocument(self) self.pdf_view = QPdfView(self) self.m_fileDialog = None self.pdf_view.setDocument(self.pdf_document) def init_ui(self): self.setGeometry(100, 100, 800, 600) self.setWindowTitle('RectanglePDF') # Buttons open_file = QAction('Open PDF file...', self) open_file.triggered.connect(self.open_pdf) close_program = QAction('Exit', self) close_program.triggered.connect(self.exit_program) p_page = QAction('Previous Page', self) p_page.triggered.connect(self.previous_page) n_page = QAction('Next Page', self) n_page.triggered.connect(self.next_page) # Menubar menubar = self.menuBar() file_menu = menubar.addMenu('File') file_menu.addAction(open_file) file_menu.addAction(close_program) menubar.addAction(p_page) menubar.addAction(n_page) self.show() def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(QPoint(), self.pix) if not self.point1.isNull() and not self.point2.isNull(): rect = QRect(self.point1, self.point2) painter.drawRect(rect.normalized()) def mousePressEvent(self, event): if event.buttons() & Qt.MouseButton.LeftButton: self.point1 = event.pos() self.point2 = self.point1 self.update() def mouseMoveEvent(self, event): if event.buttons() & Qt.MouseButton.LeftButton: self.point2 = event.pos() self.update() def mouseReleaseEvent(self, event): if event.button() & Qt.MouseButton.LeftButton: rect = QRect(self.point1, self.point2) painter = QPainter(self.pix) painter.drawRect(rect.normalized()) self.point1, self.point2 = QPoint(), QPoint() self.update() def next_page(self): nav = self.pdf_view.pageNavigator() if nav.currentPage() < self.pdf_document.pageCount() - 1: nav.jump(nav.currentPage() + 1, QPointF(), nav.currentZoom()) def previous_page(self): nav = self.pdf_view.pageNavigator() if nav.currentPage() > 0: nav.jump(nav.currentPage() - 1, QPointF(), nav.currentZoom()) def exit_program(self): sys.exit(app.exec()) def open_pdf(self): home_dir = str(Path.home()) doc_location = QFileDialog.getOpenFileName( self,'Open File', home_dir,"PDF files (*.pdf)" ) self.pdf_document.load(doc_location[0]) self.pdf_view.setDocument(self.pdf_document) self.pdf_view.setPageMode(QPdfView.PageMode.SinglePage) self.pdf_view.setZoomMode(QPdfView.ZoomMode.FitInView) self.setCentralWidget(self.pdf_view) self.update() self.pdf_view.show() # oldif __name__ == '__main__': app = QApplication(sys.argv) myApp = MainWindow() myApp.show() try: sys.exit(app.exec()) except SystemExit: print('Closing Window...')
Update. I tried to use Stacked Layout, but result is still unsatisfactory.
import sysfrom pathlib import Pathfrom PyQt6 import QtCore, QtWidgetsfrom PyQt6.QtCore import QPointF, QRect, QPoint, Qtfrom PyQt6.QtGui import QAction, QPainter, QPixmap, QIconfrom PyQt6.QtPdf import QPdfDocumentfrom PyQt6.QtPdfWidgets import QPdfViewfrom PyQt6.QtWidgets import (QFileDialog, QApplication, QStackedLayout, QVBoxLayout, QWidget, )class RectWindow(QWidget): def __init__(self): super().__init__() layout = QVBoxLayout() self.setLayout(layout) self.pix = QPixmap(self.rect().size()) # TODO get screen resolution self.pix.fill(Qt.GlobalColor.transparent) self.begin, self.destination = QPoint(), QPoint() # TODO Transparent background to reveal PDF underneath # self.setWindowFlags(Qt.WindowType.FramelessWindowHint) # self.setWindowFlags(Qt.WindowType.WindowTransparentForInput) # self.setStyleSheet('QWidget {background: transparent') # self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True) # self.setWindowOpacity(0.5) def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(QPoint(), self.pix) if not self.begin.isNull() and not self.destination.isNull(): rect = QRect(self.begin, self.destination) painter.drawRect(rect.normalized()) def mousePressEvent(self, event): if event.buttons() & Qt.MouseButton.LeftButton: self.begin = event.pos() self.destination = self.begin self.update() def mouseMoveEvent(self, event): if event.buttons() & Qt.MouseButton.LeftButton: self.destination = event.pos() self.update() def mouseReleaseEvent(self, event): if event.button() & Qt.MouseButton.LeftButton: rect = QRect(self.begin, self.destination) painter = QPainter(self.pix) painter.drawRect(rect.normalized()) self.begin, self.destination = QPoint(), QPoint() self.update()class PdfWindow(QWidget): def __init__(self): super().__init__() self.pdf_document = QPdfDocument(self) self.pdf_view = QPdfView(self) self.m_fileDialog = None self.pdf_view.setDocument(self.pdf_document) def open_pdf(self): home_dir = str(Path.home()) doc_location = QFileDialog.getOpenFileName( self,'Open File', home_dir,"PDF files (*.pdf)" ) self.pdf_document.load(doc_location[0]) self.pdf_view.setDocument(self.pdf_document) self.pdf_view.setPageMode(QPdfView.PageMode.SinglePage) self.pdf_view.setZoomMode(QPdfView.ZoomMode.FitInView) self.update() self.pdf_view.showMaximized() def next_page(self): nav = self.pdf_view.pageNavigator() if nav.currentPage() < self.pdf_document.pageCount() - 1: nav.jump(nav.currentPage() + 1, QPointF(), nav.currentZoom()) def previous_page(self): nav = self.pdf_view.pageNavigator() if nav.currentPage() > 0: nav.jump(nav.currentPage() - 1, QPointF(), nav.currentZoom())class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('RectanglePDF') # Widgets base_widget = QWidget() rect_widget = RectWindow() pdf_widget = PdfWindow() # Layout settings main_layout = QVBoxLayout() self.stacklayout = QStackedLayout() self.setLayout(main_layout) base_widget.setLayout(self.stacklayout) # Layout ordering self.stacklayout.addWidget(pdf_widget) self.stacklayout.addWidget(rect_widget) self.setCentralWidget(base_widget) # Buttons open_file = QAction('Open PDF file...', self) open_file.triggered.connect(pdf_widget.open_pdf) close_program = QAction('Exit', self) close_program.triggered.connect(self.exit_program) p_page = QAction('Previous Page', self) p_page.triggered.connect(pdf_widget.previous_page) n_page = QAction('Next Page', self) n_page.triggered.connect(pdf_widget.next_page) # Menubar menubar = self.menuBar() file_menu = menubar.addMenu('File') file_menu.addAction(open_file) file_menu.addAction(close_program) menubar.addAction(p_page) menubar.addAction(n_page) # DEBUG r_pdf = QAction('Raise PDF', self) r_pdf.triggered.connect(self.raise_pdf) menubar.addAction(r_pdf) r_rect = QAction('Raise Rect', self) r_rect.triggered.connect(self.raise_rect) menubar.addAction(r_rect) def raise_pdf(self): # for DEBUG self.stacklayout.setCurrentIndex(0) def raise_rect(self): # for DEBUG self.stacklayout.setCurrentIndex(1) def exit_program(self): sys.exit(app.exec())if __name__ == '__main__': app = QApplication(sys.argv) mainApp = MainWindow() mainApp.showMaximized() try: sys.exit(app.exec()) except SystemExit: print('Closing Window...')