最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to load a PDF from bytes instead of file in Pyside6? - Stack Overflow

programmeradmin8浏览0评论

I'm trying to display a PDF I created (using fpdf2) in a Pyside6 app.

There seems to be two roads there:

  1. 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.

  2. 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:

  1. 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.

  2. 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
  • QBuffer is inherited from QIODevice and QBuffer can have its data from QByteArray and afaik, you can just pass a bytes buffer to QByteArray .. – rasjani Commented 2 days ago
  • some programs allows to load from file-like object and it allows to use io.BytesIO(your_bytes) instead of filename. Maybe QIODevice also means some kind of file-like object and you can use QBuffer(your_bytes) (as suggested in other comment) – furas Commented 2 days ago
  • You can't create instances of QIODevice, period, Python is not the problem: QIODevice is an abstract, meant to be used in subclasses, like those listed in the "inherited by" field of the documentation. A signature like that means: the argument can be any object inheriting from QIODevice. – musicamante Commented 2 days ago
Add a comment  | 

1 Answer 1

Reset to default 0

You 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()
发布评论

评论列表(0)

  1. 暂无评论