[PyQt] Conflict between setuptools & requirements in official PyQt5 docs
Jones, Bryan
bjones at ece.msstate.edu
Wed Feb 10 16:49:48 GMT 2016
Phil, Kovid, and all,
Thanks for your thoughts on this. The difficulty of correctly reconciling
Qt's memory management scheme (you must insure all objects are destroyed in
the correct order) with Python's (the interpreter chooses an order which
you have little control over) has puzzled me for a while. The following
code crashes several ways, even with Kovid's suggestion. I'm running under
Qt 5.5.1, SIP 4.17, PtQt 5.5.1, Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24
2015, 22:44:40) [MSC v.1600 64 bit (AMD64)], OS nt, platform Windows 7.
Bryan
import sys
import os
import platform
import gc
import sip
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QT_VERSION_STR
from PyQt5.Qt import PYQT_VERSION_STR
app = None
def printDestroyed(qObject):
def destroyed():
print('destroyed {}'.format(qObject))
qObject.destroyed.connect(destroyed)
def quickCrash():
print('quickCrash')
QApplication(sys.argv)
QWidget()
def slowCrash():
global app
print('slowCrash')
app = QApplication(sys.argv)
printDestroyed(app)
w = QWidget()
printDestroyed(w)
w.show()
sip.delete(app)
sip.delete(w)
def qtMain():
global app
app = QApplication(sys.argv)
printDestroyed(app)
w = QWidget()
printDestroyed(w)
w.show()
def gcCrash():
print('gcCrash')
qtMain()
gc.collect()
def main():
print('Qt {}, SIP {}, PtQt {}, Python {}, OS {}, platform {} {}'.format(
QT_VERSION_STR, sip.SIP_VERSION_STR, PYQT_VERSION_STR, sys.version,
os.name, platform.system(), platform.release()))
crashDict = {'q' : quickCrash, 's' : slowCrash, 'g' : gcCrash}
try:
crashDict[sys.argv[1]]()
except (IndexError, KeyError):
quickCrash()
if __name__ == '__main__':
main()
On Wed, Feb 10, 2016 at 9:42 AM, Phil Thompson <phil at riverbankcomputing.com>
wrote:
> On 9 Feb 2016, at 4:37 am, Kovid Goyal <kovid at kovidgoyal.net> wrote:
> >
> > Surely, the advice should be to keep a module level reference to the
> > application global rather than to run code at module level. Like this:
> >
> > app = None
> >
> > def main():
> > global app
> > app = QApplication([])
> > app.exec_()
> >
> > That way you get both behaviors. Although, in my experience, you cannot
> > avoid segfaults on exit by relying on sip.setdestroyonexit().
>
> It might be worth taking a step back on this...
>
> Crashes are caused by C++ dtors being invoked (via the Python garbage
> collector) in an order that Qt is not happy with. The order is, in effect,
> random. Typically this happens at the end of a "scope", the most
> significant of which is when the interpreter exits.
>
> Probably (but it is a guess) it would be best if the QApplication instance
> was destructed last of all.
>
> PyQt5 disables the invocation of dtors when the interpreter is exiting.
> Therefore if the following pattern is used...
>
> if __name__ = '__main__':
> app = QApplication([])
> gui = QWidget()
> gui.show()
> app.exec()
>
> ...there shouldn't be a problem with crashes on exit. If anybody has an
> example where they think this is not the case then I'd like to know.
>
> However it is not always possible to follow that pattern - setuptools
> requires a function entry point. When that function (ie. "scope") returns
> then the local objects can be garbage collected in a random order. Because
> the interpreter is still running, the dtors are still invoked.
>
> PyQt5 tries to mitigate this to a certain extent. When a QApplication is
> garbage collected it first makes sure that any top-level widgets that still
> exist are owned by C++. This effectively disables the dtors of those
> widgets. However it does mean that widgets may still outlive the C++
> QApplication instance - and maybe Qt doesn't like that.
>
> The suggestion above (ie. the global reference to the QApplication object)
> has the effect of guaranteeing that the QApplication instance will outlive
> any objects that are garbage collected when main() returns. In fact the
> QApplication dtor will never be invoked as the object will only be garbage
> collected when the interpreter exits.
>
> This pattern, therefore, should also avoid any crashes on exit. Again, if
> anybody has a counter example then I'd like to know.
>
> I am considering changing the behaviour when the QApplication object gets
> garbage collected. Instead of transferring ownership of any top-level
> widgets it would instead explicitly invoke their dtors. This would
> guarantee the QApplication outlives any widgets (as the global reference
> trick does) but it would not be able to make the same guarantee regarding
> any other objects that might be referenced at the global level. However I
> think it would be an improvement over the current behaviour.
>
> PyQt4 has a mechanism for tracking objects of certain classes (actually it
> is only done for QSystemTrayIcon) so that they get handled in a similar way
> to top-level widgets. I'd consider adding the same mechanism to PyQt5 if it
> turned out that crashes always involved certain classes. I'd also consider
> exposing that mechanism to applications to that they could add specific
> objects to a cleanup handler that is invoked just before the QApplication
> dtor.
>
> As ever, feedback and comments welcome.
>
> Phil
> _______________________________________________
> PyQt mailing list PyQt at riverbankcomputing.com
> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>
--
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: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20160210/109fe367/attachment-0001.html>
More information about the PyQt
mailing list