PyMuPDFとPyQt5でPDFビューワを作ってみた
2022/02/02 categories:Python| tags:Python|PyMuPDF|PDF|
PyMuPDFとPyQt5でPDFビューワを作ってみました。
実装した機能
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()