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 499 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 Offline
    A Offline
    Armanta
    wrote on last edited by Armanta
    #1

    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 1 Reply Last reply
    0
    • 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

        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 Online
            JonBJ Online
            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 Online
                JonBJ Online
                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 Online
                    JonBJ Online
                    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