Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Release a Qpdf without loading a new one
QtWS25 Last Chance

Release a Qpdf without loading a new one

Scheduled Pinned Locked Moved Solved Qt for Python
qt for pythonpythonpyside
11 Posts 3 Posters 503 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Armanta

    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

    jsulmJ Offline
    jsulmJ Offline
    jsulm
    Lifetime Qt Champion
    wrote on last edited by
    #2

    @Armanta What about emptyPDF() method?

    https://forum.qt.io/topic/113070/qt-code-of-conduct

    A 1 Reply Last reply
    0
    • jsulmJ jsulm

      @Armanta What about emptyPDF() method?

      A Offline
      A Offline
      Armanta
      wrote on last edited by
      #3

      @jsulm It sounds like emptyPDF() method doesn't exist.

      https://doc.qt.io/qtforpython-6/search.html?q=emptyPDF&check_keywords=yes&area=default#

      1 Reply Last reply
      0
      • JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by
        #4

        Did you try something like self.pdfDocument.close() followed by self.pdfDocument.load() again?
        If not you might find that the self.pdfView.setDocument(self.pdfDocument) interferes. Try removing that to see if the QPdfDocument would successfully close() and then load() on its own. If so unset the document from the view before closing it and reattach the view afterwards.

        1 Reply Last reply
        0
        • A Offline
          A Offline
          Armanta
          wrote on last edited by Armanta
          #5

          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 an unload_pdf method, convert excel to pdf and display it again using a load_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.

          alt text

          JonBJ 1 Reply Last reply
          0
          • A Armanta

            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 an unload_pdf method, convert excel to pdf and display it again using a load_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.

            alt text

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #6

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

            1 Reply Last reply
            0
            • A Offline
              A Offline
              Armanta
              wrote on last edited by
              #7

              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
              

              923a6ca0-b38f-42dd-8db2-224b559c8155-image.png

              JonBJ 1 Reply Last reply
              0
              • A Armanta

                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
                

                923a6ca0-b38f-42dd-8db2-224b559c8155-image.png

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #8

                @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 :)

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Armanta
                  wrote on last edited by
                  #9

                  The problem is that even after using the unload_pdf function, the document can be modified in read-write mode but cannot be deleted because it is still being used by the process. This then causes the conversion problem in Excel

                  image.png

                  image.png

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    Armanta
                    wrote on last edited by
                    #10

                    After a long debugging session I've just had a small piece of information: the problem is that Qt doesn't immediately release resources until the event loop has completed a full cycle

                    1 Reply Last reply
                    1
                    • A Offline
                      A Offline
                      Armanta
                      wrote on last edited by
                      #11

                      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)
                      

                      editor.py

                      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 to app.exec().
                      Then I put in a signal that will convert the file once the reference has been destroyed, since we know that this frees access

                      You should know that I think this is the only method without making temporary files

                      1 Reply Last reply
                      0
                      • A Armanta has marked this topic as solved on

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved