Release a Qpdf without loading a new one
-
Hello, I'm trying to unload a pdf file while keeping the object and without loading a file. My aim is to edit the pdf and reload it. I've already spent 5 hours on this problem, read tons of things and I can't do it.
I already check on https://forum.qt.io/topic/158080/how-can-i-release-a-pdf-document-for-use-again-after-it-has-been-used-with-qpdfview-in-pyqt/2. The only method that closes access is
document.load
but you need an other document (i dont want)class EditorWidget(QFrame): def __init__(self, pdf_path: str, parent=None): super().__init__(parent=parent) self.setObjectName("EditorWidget") self.pdf_path = pdf_path self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.commandBar = EditorCommandBar(self) self.layout.addWidget(self.commandBar) self.container = QWidget(self) self.container.setLayout(QVBoxLayout()) self.container.layout().setContentsMargins(0, 0, 0, 0) # Initialisation du PDF viewer self.pdfView = CustomPdfView(self.container) self.pdfDocument = QPdfDocument(self) self.pdfDocument.load(self.pdf_path) self.pdfView.setDocument(self.pdfDocument) self.container.layout().addWidget(self.pdfView) self.layout.addWidget(self.container) # Créer le ScrollDelegate après avoir ajouté la vue self.scrollDelegate = SmoothScrollDelegate(self.pdfView) self.scrollDelegate.useAni = True self.scrollDelegate.eventFilter = lambda obj, event: False # Connecter les actions de zoom self.commandBar.zoomInAction.triggered.connect(self.pdfView.zoom_in) self.commandBar.zoomOutAction.triggered.connect(self.pdfView.zoom_out) self.commandBar.resetZoomAction.triggered.connect(self.pdfView.reset_zoom)
I would like to implement an unload_pdf method
Please don't tell me to make temporary files too -
I've finally found the solution, it was indeed the eventloop. The process will only be destroyed after the next loop.
match_manager.py
# Mettre à jour les variables dans le fichier Excel ExcelToPdfWorker.update_variables(excel_path, variables) # Connecter le signal avant de décharger le PDF match_widget.editor.pdf_unloaded.connect( lambda: self._convert_after_unload(match_widget, excel_path) ) match_widget.editor.unload_pdf() def _convert_after_unload(self, match_widget, excel_path): """Appelé quand le PDF est vraiment déchargé.""" self.excel_worker.excel_path = excel_path self.excel_worker.convert() # Une fois la conversion terminée, on peut recharger le PDF match_widget.editor.load_new_pdf(match_widget.pdf_path)
def unload_pdf(self): """Ferme le PDF actuel et nettoie les références.""" # Détacher la vue du document actuel self.pdfView.setDocument(None) # Fermer et supprimer l'ancien document if self.pdfDocument: old_document = self.pdfDocument # Créer un nouveau document vide avant de détruire l'ancien self.pdfDocument = QPdfDocument(self) # Connecter le signal destroyed à l'émission de notre signal old_document.destroyed.connect(self.pdf_unloaded.emit) old_document.close() old_document.deleteLater() def load_new_pdf(self, pdf_path): """Charge un nouveau fichier PDF.""" # Charger le nouveau PDF self.pdfDocument.load(pdf_path) self.pdfView.setDocument(self.pdfDocument) print(f"Nouveau PDF chargé: {pdf_path}") return True
I'm well aware that my code sucks.
Here's the solution. As
deleteLater()
really destroy the PdfDocument and unload it, you have to wait until the end of a complete cycle and return toapp.exec()
.
Then I put in a signal that will convert the file once the reference has been destroyed, since we know that this frees accessYou should know that I think this is the only method without making temporary files
-
Did you try something like
self.pdfDocument.close()
followed byself.pdfDocument.load()
again?
If not you might find that theself.pdfView.setDocument(self.pdfDocument)
interferes. Try removing that to see if theQPdfDocument
would successfullyclose()
and thenload()
on its own. If so unset the document from the view before closing it and reattach the view afterwards. -
Just to explain how it works.
First i initialise the document and display it. Then I make some changes (in fact it's an Excel file that I convert to PDF).
So at first I have to display it. Then I have to unload it using anunload_pdf
method, convert excel to pdf and display it again using aload_pdf
method.In my
unload_pdf
method I've already tried quite a few things,.close()
,setDocument(None)
etc... but despite this the pdf remains open. It's only by using the.load()
method that the QPdfView gives up access.And if I want to unload it using the
load()
method, you have to pass a file. I've already tried calling load without specifying a file (.load("")
), it doesn't work and I don't want to pass a temporary file or anything.@JonB If I remove
self.pdfView.setDocument(self.pdfDocument)
it will no longer be attached to my pdfView and will not be displayed. -
@Armanta said in Release a Qpdf without loading a new one:
I've already tried calling load without specifying a file (.load("")),
I would have tried
.load(None)
?If I remove self.pdfView.setDocument(self.pdfDocument)
Yes, but you could go
self.pdfView.setDocument(self.pdfDocument) # first time # then whatever, till you want to reset the document self.pdfView.setDocument(None) # change what is is `self.pdfDocument` by reloading it or whatever self.pdfView.setDocument(self.pdfDocument)
You could try above? I don't really know what is exactly that you want to happen when.
-
Same problem, it crashed when going to self.pdfDocument.load(None)
# Third-party imports from PySide6.QtCore import Qt from PySide6.QtWidgets import QFrame, QVBoxLayout, QWidget, QHBoxLayout from PySide6.QtPdfWidgets import QPdfView from PySide6.QtPdf import QPdfDocument from qfluentwidgets import ( CommandBar, Action, FluentIcon as FIF, SmoothScrollDelegate ) class EditorWidget(QFrame): def __init__(self, pdf_path: str, parent=None): super().__init__(parent=parent) self.setObjectName("EditorWidget") self.pdf_path = pdf_path self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.commandBar = EditorCommandBar(self) self.layout.addWidget(self.commandBar) self.container = QWidget(self) self.container.setLayout(QVBoxLayout()) self.container.layout().setContentsMargins(0, 0, 0, 0) # Initialisation du PDF viewer self.pdfView = CustomPdfView(self.container) self.pdfDocument = QPdfDocument(self) self.pdfDocument.load(self.pdf_path) self.pdfView.setDocument(self.pdfDocument) self.container.layout().addWidget(self.pdfView) self.layout.addWidget(self.container) # Créer le ScrollDelegate après avoir ajouté la vue self.scrollDelegate = SmoothScrollDelegate(self.pdfView) self.scrollDelegate.useAni = True self.scrollDelegate.eventFilter = lambda obj, event: False # Connecter les actions de zoom self.commandBar.zoomInAction.triggered.connect(self.pdfView.zoom_in) self.commandBar.zoomOutAction.triggered.connect(self.pdfView.zoom_out) self.commandBar.resetZoomAction.triggered.connect(self.pdfView.reset_zoom) def unload_pdf(self): """Ferme le PDF actuel et nettoie les références.""" self.pdfView.close() self.pdfDocument.close() self.pdfView.setDocument(None) self.pdfDocument.load(None) def load_new_pdf(self, pdf_path): """Charge un nouveau fichier PDF.""" # Charger le nouveau PDF self.pdfDocument.load(pdf_path) self.pdfView.setDocument(self.pdfDocument) print(f"Nouveau PDF chargé: {pdf_path}") return True
-
@Armanta said in Release a Qpdf without loading a new one:
self.pdfDocument.load(None)
TBH I don't see why you need this at all. You have detached the view from the document and closed the document. What is wrong at this stage, what is the problem, what more do you want and why? If you can explain in one sentence with words of not more than three syllables that would be great :)
-
I've finally found the solution, it was indeed the eventloop. The process will only be destroyed after the next loop.
match_manager.py
# Mettre à jour les variables dans le fichier Excel ExcelToPdfWorker.update_variables(excel_path, variables) # Connecter le signal avant de décharger le PDF match_widget.editor.pdf_unloaded.connect( lambda: self._convert_after_unload(match_widget, excel_path) ) match_widget.editor.unload_pdf() def _convert_after_unload(self, match_widget, excel_path): """Appelé quand le PDF est vraiment déchargé.""" self.excel_worker.excel_path = excel_path self.excel_worker.convert() # Une fois la conversion terminée, on peut recharger le PDF match_widget.editor.load_new_pdf(match_widget.pdf_path)
def unload_pdf(self): """Ferme le PDF actuel et nettoie les références.""" # Détacher la vue du document actuel self.pdfView.setDocument(None) # Fermer et supprimer l'ancien document if self.pdfDocument: old_document = self.pdfDocument # Créer un nouveau document vide avant de détruire l'ancien self.pdfDocument = QPdfDocument(self) # Connecter le signal destroyed à l'émission de notre signal old_document.destroyed.connect(self.pdf_unloaded.emit) old_document.close() old_document.deleteLater() def load_new_pdf(self, pdf_path): """Charge un nouveau fichier PDF.""" # Charger le nouveau PDF self.pdfDocument.load(pdf_path) self.pdfView.setDocument(self.pdfDocument) print(f"Nouveau PDF chargé: {pdf_path}") return True
I'm well aware that my code sucks.
Here's the solution. As
deleteLater()
really destroy the PdfDocument and unload it, you have to wait until the end of a complete cycle and return toapp.exec()
.
Then I put in a signal that will convert the file once the reference has been destroyed, since we know that this frees accessYou should know that I think this is the only method without making temporary files
-