[PyQt] C++ destructors and PyQt?

Bryan A. Jones bjones at ece.msstate.edu
Mon Nov 3 22:07:47 GMT 2014


All,

Pardon the double post. I saw a few obvious bugs after hitting send. My
apologies. Here is the improved version.

Bryan

# Test program to ask about destructors and PyQt4.

import sys, time
from PyQt4.QtGui import QApplication
from PyQt4.QtCore import QThread, QTimer, QObject, pyqtSignal

class Worker(QObject):
    def onStartWork(self):
        time.sleep(1)

class ThreadController(QObject):
    startWork = pyqtSignal()
    def __init__(self, parent):
        QObject.__init__(self, parent)
        # No parent object provided; if so, we get the error
        # "QObject::moveToThread: Cannot move objects with a parent".
        # Likewise, calling setParent after moving the object produces
        # "QObject::setParent: Cannot set parent, new parent is in a different
        # thread". Who will deallocate this object, since it has no parent? I
        # assume the answer is "Python, maybe". Is that good enough?
        self.worker = Worker()
        self.workerThread = QThread(self)
        self.worker.moveToThread(self.workerThread)

        # Use method #2 below. Commnet out for an ominous warning which will
        # crash in a more complex program: "QThread: Destroyed while thread is
        # still running".
        #parent.aboutToQuit.connect(self.del_)
        self.startWork.connect(self.worker.onStartWork)
        self.workerThread.start()

    # I'd like to run when when ~ThreadController is invoked. That's quite
    # differentfrom when __del__ is invoked. How?
    #
    # 1. Give up. Just invoke it manually. However, this is error-prone (i.e.
    #    I'll forget to do it at some point).
    # 2. Connect app.aboutToQuit to this. This seems fairly reasonable, but
    #    it would feel cleaner to invoke when the destructor is invoked,
    #    rather than earlier. Also, this means that the QApplication would
    #    need to be passed to every thread, which might involve awkwardness
    #    in passing it through several intervening subclasses.
    # 3. Connect destroyed(), which doesn't work -- this isn't emitted until
    #    the object is mostly dead, far after ~ThreadController was invoked,
    #    and won't be executed.
    # 4. Have __del__ invoke this, which doesn't work -- it only happens after
    #    ~ThreadController has finihed.
    def del_(self):
        print('del_')
        self.workerThread.quit()
        self.workerThread.wait()

    def __del__(self):
        print('__del__')
        # Crash when uncommented.
        #self.del_()

if __name__ == '__main__':
    app = QApplication(sys.argv)

    # Exit the program shortly after the event loop starts up, but before the
    # thread finishes.
    QTimer.singleShot(200, app.exit)

    # Start a thread
    tm = ThreadController(app)

    # Run the main event loop.
    ret = app.exec_()
    print('done')
    sys.exit(ret)

​

On Mon, Nov 3, 2014 at 3:59 PM, Bryan A. Jones <bjones at ece.msstate.edu>
wrote:

> All,
>
> I’m confused when it comes to the proper way to mesh Qt’s model of
> creating a tree of parented objects which destroy themselves when no longer
> used with Python’s “delete it when I feel like it” garbage collector. In
> particular, I’d like to shut down threads when the underlying Qt objects
> are destroyed, but I can’t figure out how to do that. Any advice and wisdom
> would be appreciated. I've attached same code with my thoughts.
>
> Bryan
>
> # Test program to ask about destructors and PyQt4.
>
> import sys, time
> from PyQt4.QtGui import QApplication
> from PyQt4.QtCore import QThread, QTimer, QObject
>
> class Worker(QObject):
>     def run(self):
>         time.sleep(1)
>
> class ThreadController(QObject):
>     def __init__(self, parent):
>         QObject.__init__(self, parent)
>         # No parent object provided; if so, we get the error
>         # "QObject::moveToThread: Cannot move objects with a parent".
>         # Who will deallocate this object, since it has no parent? I
>         # assume the answer is "Python, maybe". Is that good enough?
>         self.worker = Worker()
>         self.workerThread = QThread(self)
>         self.worker.moveToThread(self.workerThread)
>         # Use method #2 below. Commnet out for a crash.
>         parent.aboutToQuit.connect(self.del_)
>         self.workerThread.start()
>
>     # I'd like to run when when ~ThreadController is invoked. That's quite
>     # differentfrom when __del__ is invoked. How?
>     #
>     # 1. Give up. Just invoke it manually. However, this is error-prone (i.e.
>     #    I'll forget to do it at some point).
>     # 2. Connect app.aboutToQuit to this. This seems fairly reasonable, but
>     #    it would feel cleaner to invoke when the destructor is invoked,
>     #    rather than earlier. Also, this means that the QApplication would
>     #    need to be passed to every thread, which might involve awkwardness
>     #    in passing it through several intervening subclasses.
>     # 3. Connect destroyed(), which doesn't work -- this isn't emitted until
>     #    the object is mostly dead, far after ~ThreadController was invoked,
>     #    and won't be executed.
>     # 4. Have __del__ invoke this, which doesn't work -- it only happens after
>     #    ~ThreadController has finihed.
>     def del_(self):
>         print('del_')
>         self.workerThread.quit()
>         self.workerThread.wait()
>
>     def __del__(self):
>         print('__del__')
>         # Crash when uncommented.
>         #self.del_()
>
> if __name__ == '__main__':
>     app = QApplication(sys.argv)
>
>     # Exit the program shortly after the event loop starts up, but before the
>     # thread finishes.
>     QTimer.singleShot(200, app.exit)
>
>     # Start a thread
>     tm = ThreadController(app)
>
>     # Run the main event loop.
>     ret = app.exec_()
>     print('done')
>     sys.exit(ret)
>
>> --
> Bryan A. Jones, Ph.D.
> Associate Professor
> Department of Electrical and Computer Engineering
> 231 Simrall / PO Box 9571
> Mississippi State University
> Mississippi state, MS 39762
> http://www.ece.msstate.edu/~bjones
> bjones AT ece DOT msstate DOT edu
> voice 662-325-3149
> fax 662-325-2298
>
> Our Master, Jesus Christ, is on his way. He'll show up right on
> time, his arrival guaranteed by the Blessed and Undisputed Ruler,
> High King, High God.
> - 1 Tim. 6:14b-15 (The Message)
>



-- 
Bryan A. Jones, Ph.D.
Associate Professor
Department of Electrical and Computer Engineering
231 Simrall / PO Box 9571
Mississippi State University
Mississippi state, MS 39762
http://www.ece.msstate.edu/~bjones
bjones AT ece DOT msstate DOT edu
voice 662-325-3149
fax 662-325-2298

Our Master, Jesus Christ, is on his way. He'll show up right on
time, his arrival guaranteed by the Blessed and Undisputed Ruler,
High King, High God.
- 1 Tim. 6:14b-15 (The Message)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20141103/088e4d85/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: dest.py
Type: application/octet-stream
Size: 2727 bytes
Desc: not available
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20141103/088e4d85/attachment-0001.obj>


More information about the PyQt mailing list