PyMuPDFとPyQt5でPDFビューワを作ってみた

2022/02/02 categories:Python| tags:Python|PyMuPDF|PDF|

PyMuPDFとPyQt5でPDFビューワを作ってみました。

実装した機能

PDFビューワとは言っても、PDFを画像化して表示しているので、ただの画像ビューワと変わりません。とりあえず以下のような機能を実装しました。

実行結果

Pythonコード

import fitz
import sys
from PyQt5 import QtWidgets, QtCore, QtGui

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(600, 800)
        self.view = GraphicsView()
        self.view.setScene( QtWidgets.QGraphicsScene() )

        self.page_number = QtWidgets.QLineEdit()
        self.resolution = QtWidgets.QDoubleSpinBox()
        self.resolution.setMinimum(0.1)
        self.resolution.setValue(1.0)
        menu = QtWidgets.QWidget()
        menu.setLayout( QtWidgets.QHBoxLayout() )
        menu.layout().addWidget( QtWidgets.QPushButton('Before page') )
        menu.layout().addWidget( QtWidgets.QPushButton('Next page') )
        menu.layout().addWidget( QtWidgets.QLabel('Page number') )
        menu.layout().addWidget( self.page_number )
        menu.layout().addWidget( QtWidgets.QLabel('Resolution') )
        menu.layout().addWidget( self.resolution )

        self.setCentralWidget( QtWidgets.QWidget() )
        self.centralWidget().setLayout( QtWidgets.QVBoxLayout() )
        self.centralWidget().layout().addWidget(menu)
        self.centralWidget().layout().addWidget(self.view)

        self.doc = fitz.open('a4_page2_2.pdf')
        self.page_number.setText('1')
        self.load_page()

        menu.layout().itemAt(0).widget().clicked.connect(self.before_page)
        menu.layout().itemAt(1).widget().clicked.connect(self.next_page)
        self.resolution.valueChanged.connect(self.load_page)

    def next_page(self):
        page_number = int( self.page_number.text() )
        if page_number < self.doc.page_count:
            page_number = page_number + 1
            self.page_number.setText( str(page_number) )
            self.load_page()

    def before_page(self):
        page_number = int( self.page_number.text() )
        if page_number > 1:
            page_number = page_number - 1
            self.page_number.setText( str(page_number) )
            self.load_page()
        
    def load_page(self):
        self.view.scene().clear()
        page_number = int( self.page_number.text() ) - 1
        page = self.doc.load_page(page_number)
        zoom = self.resolution.value()
        pix = page.get_pixmap( matrix=fitz.Matrix(zoom, zoom) )
        byte_array = QtCore.QByteArray( pix.tobytes() )
        qpixmap = QtGui.QPixmap()
        qpixmap.loadFromData(byte_array)
        self.view.scene().addPixmap(qpixmap)
        self.view.scene().setSceneRect(0, 0, pix.width, pix.height)

class GraphicsView(QtWidgets.QGraphicsView):
    dragged = QtCore.pyqtSignal(QtCore.QPointF, QtCore.QPointF)
    def __init__(self, *argv, **keywords):
        super(GraphicsView, self).__init__(*argv, **keywords)
        self._numScheduledScalings = 0
        self.points = []
        self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(200,200,200)))

    def animFinished(self):
        if self._numScheduledScalings > 0:
            self._numScheduledScalings -= 1
        else:
            self._numScheduledScalings += 1
        
    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.MidButton:
            self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)

            event = QtGui.QMouseEvent(
                QtCore.QEvent.GraphicsSceneDragMove, 
                event.pos(), 
                QtCore.Qt.MouseButton.LeftButton, 
                QtCore.Qt.MouseButton.LeftButton, 
                QtCore.Qt.KeyboardModifier.NoModifier
            )

        elif event.button() == QtCore.Qt.LeftButton:
            self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
            p = self.mapToScene(event.pos())
            self.points = [p]

        QtWidgets.QGraphicsView.mousePressEvent(self, event)
   
    def mouseReleaseEvent(self, event):
        QtWidgets.QGraphicsView.mouseReleaseEvent(self, event)
        self.setDragMode(QtWidgets.QGraphicsView.NoDrag)

        if event.button() == QtCore.Qt.LeftButton:
            p = self.mapToScene( event.pos() )
            self.points.append(p)
            self.dragged.emit(self.points[0], self.points[1])
        
    def scalingTime(self, x):
        factor = 1.0 + float(self._numScheduledScalings) / 300.0
        self.scale(factor, factor)

    def setRubberBandDragMode(self):
        self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)

    def setScrollHandDragMode(self):
        self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)

    def wheelEvent(self, event):
        numDegrees = event.angleDelta().y() / 8
        numSteps = numDegrees / 15
        self._numScheduledScalings += numSteps
        if self._numScheduledScalings * numSteps < 0:
            self._numScheduledScalings = numSteps
        anim = QtCore.QTimeLine(350, self)
        anim.setUpdateInterval(20)
        anim.valueChanged.connect(self.scalingTime)
        anim.finished.connect(self.animFinished)
        anim.start()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec()

Share post

Related Posts

Comments

comments powered by Disqus