I'm trying to display a PDF I created (using fpdf2) in a Pyside6 app.
There seems to be two roads there:
I can use QWebEngineView with plugins enabled, in which I can inject the PDF raw bytes, which works. It is not ideal for me since there's a lot of UI involved ; I'd like something cleaner.
Or I can use QPdfView. That widgets takes an QPdfDocument object from which it reads from. Unfortunately, QPdfDocument only have a .load() function that takes a filename. Since I'm looking to avoid disk writes there, I'm not interested in temporarly saving the file to disk to show it on the GUI.
There's two signatures on the .load() function tho :
Supported signatures:
PySide6.QtPdf.QPdfDocument.load(PySide6.QtCore.QIODevice, /)
PySide6.QtPdf.QPdfDocument.load(str, /)
Here's the thing: I can't instantiate a QIODevice from Python, so I can't feed it PDF's bytes like a buffer (there's a .write() function that looks interesting). I've tried using BytesIO (from io package), but no luck there.
Is there a way to create a QPdfDocument object from a PDF file's bytes?
Thanks for your help!
I'm trying to display a PDF I created (using fpdf2) in a Pyside6 app.
There seems to be two roads there:
I can use QWebEngineView with plugins enabled, in which I can inject the PDF raw bytes, which works. It is not ideal for me since there's a lot of UI involved ; I'd like something cleaner.
Or I can use QPdfView. That widgets takes an QPdfDocument object from which it reads from. Unfortunately, QPdfDocument only have a .load() function that takes a filename. Since I'm looking to avoid disk writes there, I'm not interested in temporarly saving the file to disk to show it on the GUI.
There's two signatures on the .load() function tho :
Supported signatures:
PySide6.QtPdf.QPdfDocument.load(PySide6.QtCore.QIODevice, /)
PySide6.QtPdf.QPdfDocument.load(str, /)
Here's the thing: I can't instantiate a QIODevice from Python, so I can't feed it PDF's bytes like a buffer (there's a .write() function that looks interesting). I've tried using BytesIO (from io package), but no luck there.
Is there a way to create a QPdfDocument object from a PDF file's bytes?
Thanks for your help!
Share Improve this question asked 2 days ago lonewsklonewsk 133 bronze badges New contributor lonewsk is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 3 |1 Answer
Reset to default 0You need to use a QBuffer (which inherits QIODevice
) backed by a QByteArray containing the pdf bytes data. The buffer doesn't take ownership of the data, and the document doesn't take ownership of the buffer, so you need to ensure these objects are kept alive whilst the pdf is loading. A slot connected to the statusChanged signal can be used to clean up the objects once the document is loaded (or if an error occurs). A basic example that implements all that is given below (just provide a pdf file-path as a command-line argument to test it).
(PS: whilst testing, I found that the viewer seg-faults on exit if a document has successfully loaded. This happens even when loading from a file-path. The issue is fairly harmless, and it can be easily fixed by explicitly closing the document before closing down the application. I get the same problem using either PySide-6.8.3 or PyQt-6.8.1 with Qt-6.8.3 on Linux, so it seems to be caused by a Qt bug. It's possible that other versions/platforms aren't affected).
DEMO:
import sys
from PySide6 import QtCore, QtWidgets, QtPdf, QtPdfWidgets
# from PyQt6 import QtCore, QtWidgets, QtPdf, QtPdfWidgets
class PdfView(QtPdfWidgets.QPdfView):
def __init__(self, parent=None):
super().__init__(parent)
self.setDocument(QtPdf.QPdfDocument(self))
self.document().statusChanged.connect(self.handleStatusChanged)
def loadData(self, data):
self._data = QtCore.QByteArray(data)
self._buffer = QtCore.QBuffer(self._data)
self._buffer.open(QtCore.QIODeviceBase.OpenModeFlag.ReadOnly)
self.document().load(self._buffer)
def handleStatusChanged(self, status):
print(status)
if (status == QtPdf.QPdfDocument.Status.Ready or
status == QtPdf.QPdfDocument.Status.Error):
self._buffer.close()
del self._buffer, self._data
def closeEvent(self, event):
# prevent seg-fault on exit
self.document().close()
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
pdfview = PdfView()
pdfview.setGeometry(600, 100, 800, 600)
# FOR TESTING PURPOSES ONLY
# get sample pdf bytes data
with open(sys.argv[1], 'rb') as stream:
pdfview.loadData(stream.read())
pdfview.show()
app.exec()
file-like object
and it allows to useio.BytesIO(your_bytes)
instead of filename. MaybeQIODevice
also means some kind offile-like object
and you can useQBuffer(your_bytes)
(as suggested in other comment) – furas Commented 2 days ago