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. Segment fault when opening Dialog from a different QThread
QtWS25 Last Chance

Segment fault when opening Dialog from a different QThread

Scheduled Pinned Locked Moved Unsolved Qt for Python
12 Posts 4 Posters 1.3k 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.
  • ayhonA Offline
    ayhonA Offline
    ayhon
    wrote on last edited by
    #1

    I'm not experienced in Qt, I've been using PySide6 to create a small interface which asks for data from the user.

    Currently, I'm getting a Segfault with the following code (simplified from my actual use case)

    from PySide6.QtCore import *
    from PySide6.QtWidgets import *
    
    class Worker(QThread):
        finished = Signal()
        # progressed = Signal(int)
        def run(self):
            class Dialog(QDialog):
                def __init__(self):
                    super().__init__()
                    QVBoxLayout(self).addWidget(QPushButton(self))
            print("Processing some data ...")
            print("Found incomplete data. Asking user what to do")
            Dialog().exec()
    
    class Main(QWidget):
        def __init__(self):
            super().__init__()
            print("Starting main application")
            self.worker= None
            self.vbl = QVBoxLayout(self)
            self.pb = QPushButton(self)
            self.pb.clicked.connect(self.onclick)
            self.vbl.addWidget(self.pb)
    
        def onclick(self):
            if self.worker != None:
                print("Let the previous process finish")
                return
    
            print("Start process on a separate thread to not block the UI")
            self.worker = Worker()
            self.worker.finished.connect(self.onfinish)
            self.worker.start()
    
        def onfinish(self):
            self.worker = None
    
    def main(args: list[str]):
        app = QApplication(args)
        window = Main()
        window.show()
        app.exec()
    
    if __name__ == "__main__":
        from sys import argv
        main(argv)
    

    With the following code, if I repeatedly click the button, the application segfaults, sometimes complaining about calling endPaint with an active painter. Sometimes it just segfaults directly.

     ayhon@slab 2023-09-04 $ python test.py
    Starting main application
    Requested decoration  "gnome"  not found, falling back to default
    Start process on a separate thread to not block the UI
    Processing some data ...
    Found incomplete data. Asking user what to do
    Requested decoration  "gnome"  not found, falling back to default
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QPainter::setCompositionMode: Painter not active
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    Segmentation fault (core dumped)
    

    I'm using Fedora 38 on GNOME. In my actual usecase, the application crashes as soon as I hit the Ok or Cancel button in a dialog

    JonBJ 1 Reply Last reply
    0
    • ayhonA ayhon

      I'm not experienced in Qt, I've been using PySide6 to create a small interface which asks for data from the user.

      Currently, I'm getting a Segfault with the following code (simplified from my actual use case)

      from PySide6.QtCore import *
      from PySide6.QtWidgets import *
      
      class Worker(QThread):
          finished = Signal()
          # progressed = Signal(int)
          def run(self):
              class Dialog(QDialog):
                  def __init__(self):
                      super().__init__()
                      QVBoxLayout(self).addWidget(QPushButton(self))
              print("Processing some data ...")
              print("Found incomplete data. Asking user what to do")
              Dialog().exec()
      
      class Main(QWidget):
          def __init__(self):
              super().__init__()
              print("Starting main application")
              self.worker= None
              self.vbl = QVBoxLayout(self)
              self.pb = QPushButton(self)
              self.pb.clicked.connect(self.onclick)
              self.vbl.addWidget(self.pb)
      
          def onclick(self):
              if self.worker != None:
                  print("Let the previous process finish")
                  return
      
              print("Start process on a separate thread to not block the UI")
              self.worker = Worker()
              self.worker.finished.connect(self.onfinish)
              self.worker.start()
      
          def onfinish(self):
              self.worker = None
      
      def main(args: list[str]):
          app = QApplication(args)
          window = Main()
          window.show()
          app.exec()
      
      if __name__ == "__main__":
          from sys import argv
          main(argv)
      

      With the following code, if I repeatedly click the button, the application segfaults, sometimes complaining about calling endPaint with an active painter. Sometimes it just segfaults directly.

       ayhon@slab 2023-09-04 $ python test.py
      Starting main application
      Requested decoration  "gnome"  not found, falling back to default
      Start process on a separate thread to not block the UI
      Processing some data ...
      Found incomplete data. Asking user what to do
      Requested decoration  "gnome"  not found, falling back to default
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QPainter::setCompositionMode: Painter not active
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      Segmentation fault (core dumped)
      

      I'm using Fedora 38 on GNOME. In my actual usecase, the application crashes as soon as I hit the Ok or Cancel button in a dialog

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

      @ayhon
      You cannot access UI elements from secondary threads in Qt.

      If you are a beginner it is 99.9% likely you should not use or need threads at all.

      ayhonA 1 Reply Last reply
      3
      • JonBJ JonB

        @ayhon
        You cannot access UI elements from secondary threads in Qt.

        If you are a beginner it is 99.9% likely you should not use or need threads at all.

        ayhonA Offline
        ayhonA Offline
        ayhon
        wrote on last edited by ayhon
        #3

        @JonB Thank you, I suspected something of the sort. However, I felt that my use case was reasonable. How could I re-architecture my code to accomplish something similar?

        I use a QThread since I need to do some work that blocks the UI thread, downloading data from a database. However, I need user input since that data may be incomplete, in which case I'd like to query the user on how to proceed. How would it be advised to do this without spawning a Dialog from a different thread? Do I need to communicate with the UI thread with signals to ask it to clean my data?

        Sorry if this is a bit off-topic

        JonBJ 1 Reply Last reply
        0
        • ayhonA ayhon

          @JonB Thank you, I suspected something of the sort. However, I felt that my use case was reasonable. How could I re-architecture my code to accomplish something similar?

          I use a QThread since I need to do some work that blocks the UI thread, downloading data from a database. However, I need user input since that data may be incomplete, in which case I'd like to query the user on how to proceed. How would it be advised to do this without spawning a Dialog from a different thread? Do I need to communicate with the UI thread with signals to ask it to clean my data?

          Sorry if this is a bit off-topic

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

          @ayhon said in Segment fault when opening Dialog from a different QThread:

          Do I need to communicate with the UI thread with signals to ask it to clean my data?

          Exactly that. You can and should send signals and connect slots across threads. Only main UI thread to do any UI, both output and input.

          Qt's own (SQL) database classes exchange data with a variety of SQL backends and you do not need to use it from threads, usually.

          If I were learning Qt I would start without threads and only add them when I found I really needed them. And even then learn that you'll get things wrong!

          1 Reply Last reply
          1
          • ayhonA Offline
            ayhonA Offline
            ayhon
            wrote on last edited by ayhon
            #5

            @JonB I see, thank you for your time and the confirmation!

            Then, to achieve my use case I'd have to do something like:
            ce60f790-db23-4be6-affe-6eadf68a9a4b-image.png

            This seems to me like a bit of "callback hell". Is there any way to make this a bit more developer-friendly? Perhaps an async/await syntax?

            This is purely out of curiosity, for my use case this solution works

            JonBJ 1 Reply Last reply
            0
            • ayhonA ayhon

              @JonB I see, thank you for your time and the confirmation!

              Then, to achieve my use case I'd have to do something like:
              ce60f790-db23-4be6-affe-6eadf68a9a4b-image.png

              This seems to me like a bit of "callback hell". Is there any way to make this a bit more developer-friendly? Perhaps an async/await syntax?

              This is purely out of curiosity, for my use case this solution works

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

              @ayhon
              This is the normal paradigm for Qt communication between UI and worker threads.

              You might like to read the blog/overview at https://www.qt.io/blog/asynchronous-apis-in-qt-6 for Qt asynchronous APIs, maybe QFuture/QtConcurrent would interest you.

              ayhonA 1 Reply Last reply
              1
              • JonBJ JonB

                @ayhon
                This is the normal paradigm for Qt communication between UI and worker threads.

                You might like to read the blog/overview at https://www.qt.io/blog/asynchronous-apis-in-qt-6 for Qt asynchronous APIs, maybe QFuture/QtConcurrent would interest you.

                ayhonA Offline
                ayhonA Offline
                ayhon
                wrote on last edited by
                #7

                @JonB This is exactly what I was thinking of. Thanks again!

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  SimonSchroeder
                  wrote on last edited by
                  #8

                  This is really annoyingly tricky to do it right. If QtConcurrent works for you, then you should use it.

                  Here is what I have learned so far for exactly your problem (calling GUI functions from another thread) when other approaches don't work well. (Though I only know C++ and not Python, but the ideas would be similar.)

                  In order to call any GUI function from another thread we can use QMetaObject::invokeMethod(...). In C++ the first parameter is a context object where we can plug in qApp (pointer to the only instance of QApplication). As a second parameter I would put in a lambda which has the code that should be executed inside the GUI thread.

                  However, this is not a blocking call, yet. So, we cannot wait in this way for the answer from the dialog. For this I am using a QMutex to synchronize the two threads. Here are the steps:

                  1. Create a QMutex object inside the calling thread and lock it.
                  2. Call the GUI code (a lambda in C++) with the method described above. As last statement in the GUI code unlock the mutex.
                  3. Inside the calling thread call lock on the mutex again. This will only work once it is unlocked by the GUI thread. This means we'll wait until the GUI code has run.
                  4. For clean up unlock the mutex.

                  I hope this translates well into Python. But, as mentioned before, got with QtConcurrent if this works for you.

                  jeremy_kJ 1 Reply Last reply
                  0
                  • S SimonSchroeder

                    This is really annoyingly tricky to do it right. If QtConcurrent works for you, then you should use it.

                    Here is what I have learned so far for exactly your problem (calling GUI functions from another thread) when other approaches don't work well. (Though I only know C++ and not Python, but the ideas would be similar.)

                    In order to call any GUI function from another thread we can use QMetaObject::invokeMethod(...). In C++ the first parameter is a context object where we can plug in qApp (pointer to the only instance of QApplication). As a second parameter I would put in a lambda which has the code that should be executed inside the GUI thread.

                    However, this is not a blocking call, yet. So, we cannot wait in this way for the answer from the dialog. For this I am using a QMutex to synchronize the two threads. Here are the steps:

                    1. Create a QMutex object inside the calling thread and lock it.
                    2. Call the GUI code (a lambda in C++) with the method described above. As last statement in the GUI code unlock the mutex.
                    3. Inside the calling thread call lock on the mutex again. This will only work once it is unlocked by the GUI thread. This means we'll wait until the GUI code has run.
                    4. For clean up unlock the mutex.

                    I hope this translates well into Python. But, as mentioned before, got with QtConcurrent if this works for you.

                    jeremy_kJ Offline
                    jeremy_kJ Offline
                    jeremy_k
                    wrote on last edited by
                    #9

                    @SimonSchroeder said in Segment fault when opening Dialog from a different QThread:

                    1. Create a QMutex object inside the calling thread and lock it.
                    2. Call the GUI code (a lambda in C++) with the method described above. As last statement in the GUI code unlock the mutex.
                    3. Inside the calling thread call lock on the mutex again. This will only work once it is unlocked by the GUI thread. This means we'll wait until the GUI code has run.
                    4. For clean up unlock the mutex.

                    This looks like a reinvention of Qt::BlockingQueuedConnection

                    Asking a question about code? http://eel.is/iso-c++/testcase/

                    S 1 Reply Last reply
                    2
                    • jeremy_kJ jeremy_k

                      @SimonSchroeder said in Segment fault when opening Dialog from a different QThread:

                      1. Create a QMutex object inside the calling thread and lock it.
                      2. Call the GUI code (a lambda in C++) with the method described above. As last statement in the GUI code unlock the mutex.
                      3. Inside the calling thread call lock on the mutex again. This will only work once it is unlocked by the GUI thread. This means we'll wait until the GUI code has run.
                      4. For clean up unlock the mutex.

                      This looks like a reinvention of Qt::BlockingQueuedConnection

                      S Offline
                      S Offline
                      SimonSchroeder
                      wrote on last edited by
                      #10

                      @jeremy_k said in Segment fault when opening Dialog from a different QThread:

                      This looks like a reinvention of Qt::BlockingQueuedConnection

                      Maybe it is. However, I don't like to write a signal and a slot for just one line of code. Instead I use lambdas in these places. That's why I use QMetaObject::invokeMethod instead of a signal/slot connection. As far as I know there is no equivalent of Qt::BlockingQueuedConnection for invokeMethod. Hence the workaround/reimplementation.

                      It is best to put this all into a library to make it easier to not get wrong. I have done so: https://github.com/SimonSchroeder/QtThreadHelper . This is wrapped into guiThread([](){ ... }) and guiThread(WorkerThread::SYNC, [](){ ... }). The latter will block and wait.

                      jeremy_kJ 1 Reply Last reply
                      0
                      • S SimonSchroeder

                        @jeremy_k said in Segment fault when opening Dialog from a different QThread:

                        This looks like a reinvention of Qt::BlockingQueuedConnection

                        Maybe it is. However, I don't like to write a signal and a slot for just one line of code. Instead I use lambdas in these places. That's why I use QMetaObject::invokeMethod instead of a signal/slot connection. As far as I know there is no equivalent of Qt::BlockingQueuedConnection for invokeMethod. Hence the workaround/reimplementation.

                        It is best to put this all into a library to make it easier to not get wrong. I have done so: https://github.com/SimonSchroeder/QtThreadHelper . This is wrapped into guiThread([](){ ... }) and guiThread(WorkerThread::SYNC, [](){ ... }). The latter will block and wait.

                        jeremy_kJ Offline
                        jeremy_kJ Offline
                        jeremy_k
                        wrote on last edited by jeremy_k
                        #11

                        @SimonSchroeder said in Segment fault when opening Dialog from a different QThread:

                        As far as I know there is no equivalent of Qt::BlockingQueuedConnection for invokeMethod

                        Qt::ConnectionType is the third argument of ~ half of the overloads of QMetaObject::invokeMethod, going back to at least Qt 4.5.1.

                        Asking a question about code? http://eel.is/iso-c++/testcase/

                        S 1 Reply Last reply
                        0
                        • jeremy_kJ jeremy_k

                          @SimonSchroeder said in Segment fault when opening Dialog from a different QThread:

                          As far as I know there is no equivalent of Qt::BlockingQueuedConnection for invokeMethod

                          Qt::ConnectionType is the third argument of ~ half of the overloads of QMetaObject::invokeMethod, going back to at least Qt 4.5.1.

                          S Offline
                          S Offline
                          SimonSchroeder
                          wrote on last edited by
                          #12

                          @jeremy_k said in Segment fault when opening Dialog from a different QThread:

                          Qt::ConnectionType is the third argument of ~ half of the overloads of QMetaObject::invokeMethod, going back to at least Qt 4.5.1.

                          I didn't know that! Looks like I should change the implementation then...

                          1 Reply Last reply
                          0

                          • Login

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