On Sunday 05 October 2003 16:20, Sundance wrote:
> I heard Hans-Peter Jansen said:
>
> Erm... I happen to have another question... Exception handling
> while constructing the interface works fine, but I've got a problem
> with the event loop. While it is running, it'll drop all the
> exceptions silently after outputing the traceback to stderr. Is
> there any way to tell it NOT to brush exceptions under the carpet,
> so to speak? Or, alternatively, to install the exception handler
> *inside* the event loop rather than around it?

You could redirect stdout/stderr to an internal tee, which would
trigger a PYSIGNAL, which in turn triggers a corresponding dialog.

Again, how a about throwing things together to provide a minimum
example. I will try to help you as I can with the hairy parts ;)
[I'm somewhat time restricted atm]

> > Needless to say, you cannot protect your app from crashes
> > happened on Qt level.
>
> Yeah, I know, but the point is mostly to keep users safe from bugs
> in MY code. :)

..and I'm sure, the whole project could profit from such an 
implementation. There's no such thing like a perfect programmer, even 
if python makes it faily hard to write really stupid code (compared
to our c/c++ colleagues :).

General side note: The way, Phil interfaces Qt in form of an ultra 
thin, but quite smart layer is maybe the only user visible downside, 
since if you feed Qt with garbage, it usually core dumps. OTOH, I 
never met a GUI API for Python, which is as predicable, as PyQts 
after getting used to it.

> > Comments?
>
> Yeah, your test app works perfectly here (PyQt and sip 3.8), no
> exceptions, no crash, etc. Is it normal?

Switch comments in close/closeEvent methods to expose qApp.quit()
and watch the different shut down paths. You will see those attribute
errors, because the events:

PushButton event: 25 WindowDeactivate
PushButton event: 9 FocusOut
PushButton event: 18 Hide

are delayed until after __main__ cleanup of python:

close 1
qApp.quit() returned
exec_loop: 0
# clear __builtin__._
# clear sys.path
# clear sys.argv
# clear sys.ps1
# clear sys.ps2
# clear sys.exitfunc
# clear sys.exc_type
# clear sys.exc_value
# clear sys.exc_traceback
# clear sys.last_type
# clear sys.last_value
# clear sys.last_traceback
# restore sys.stdin
# restore sys.stdout
# restore sys.stderr
# cleanup __main__
PushButton event: 25
Traceback (most recent call last):
  File "./qt_dtor.py", line 98, in event
    print "PushButton event:", t, qte.which(t)
AttributeError: 'NoneType' object has no attribute 'which'
PushButton event: 9
Traceback (most recent call last):
  File "./qt_dtor.py", line 98, in event
    print "PushButton event:", t, qte.which(t)
AttributeError: 'NoneType' object has no attribute 'which'
PushButton event: 18
Traceback (most recent call last):
  File "./qt_dtor.py", line 98, in event
    print "PushButton event:", t, qte.which(t)
AttributeError: 'NoneType' object has no attribute 'which'

Phil: do you think, you can do something about that? Yes, I know, I
described you a similar problem back in January. Maybe it's possible 
to flush Qts event queue before leaving the exec_loop, when calling 
qApp.{quit,exit}() methods? BTW: is it expected, that they _return_ 
to the calling context? At least, I didn't.

Slighly modified script attached.

Pete
#!/usr/bin/python -v
# call python directly here, in order to provide -v flag
#!/usr/bin/env python
#
# qt_dtor.py v0.1: investigate PyQt dtor races
#
# Copyright 2002-2003 Hans-Peter Jansen <[EMAIL PROTECTED]>
#
# This program is placed under the GNU General Public License V.2

import sys
from qt import *

class _qtEvent:
    _qtEventDict = {
        0: "None",
        1: "Timer",
        2: "MouseButtonPress",
        3: "MouseButtonRelease",
        4: "MouseButtonDblClick",
        5: "MouseMove",
        6: "KeyPress",
        7: "KeyRelease",
        8: "FocusIn",
        9: "FocusOut",
        10: "Enter",
        11: "Leave",
        12: "Paint",
        13: "Move",
        14: "Resize",
        15: "Create",
        16: "Destroy",
        17: "Show",
        18: "Hide",
        19: "Close",
        20: "Quit",
        21: "Reparent",
        22: "ShowMinimized",
        23: "ShowNormal",
        24: "WindowActivate",
        25: "WindowDeactivate",
        26: "ShowToParent",
        27: "HideToParent",
        28: "ShowMaximized",
        29: "ShowFullScreen",
        30: "Accel",
        31: "Wheel",
        32: "AccelAvailable",
        33: "CaptionChange",
        34: "IconChange",
        35: "ParentFontChange",
        36: "ApplicationFontChange",
        37: "ParentPaletteChange",
        38: "ApplicationPaletteChange",
        39: "PaletteChange",
        40: "Clipboard",
        42: "Speech",
        50: "SockAct",
        51: "AccelOverride",
        52: "DeferredDelete",
        60: "DragEnter",
        61: "DragMove",
        62: "DragLeave",
        63: "Drop",
        64: "DragResponse",
        70: "ChildInserted",
        71: "ChildRemoved",
        72: "LayoutHint",
        73: "ShowWindowRequest",
        80: "ActivateControl",
        81: "DeactivateControl",
        82: "ContextMenu",
        83: "IMStart",
        84: "IMCompose",
        85: "IMEnd",
        86: "Accessibility",
        87: "Tablet"
    }

    def types(self):
        return self._qtEventDict.keys()

    def which(self, t):
        try:
            if t in self._qtEventDict.keys():
                return self._qtEventDict[t]
        except:
            return "Unknown";

qte = _qtEvent()

class PushButton(QPushButton):
    def __init__(self, text = None, parent = None, name = None):
        QPushButton.__init__(self, text, parent, name)

    def event(self, e):
        t = e.type()
        print "PushButton event:", t, qte.which(t)
        return QPushButton.event(self, e)

class qtTest1(QWidget):
    def __init__(self, parent = None, name = None, fl = 0):
        QWidget.__init__(self, parent, name, fl)
        self.setCaption("PyQt dtor testing v1")
        vbox = QVBoxLayout(self, 4, -1, "vbox")
        pb = PushButton("Quit", self, "pb")
        vbox.addWidget(pb)
        self.connect(pb, SIGNAL("clicked()"), self.close)

    def close(self, alsoDelete = 1):
        print "close", alsoDelete
        # calling qApp.quit() here seems to delay Qts event
        # distribution until after python cleaned up __main__
        # which triggers AttributeErrors from accessing qte
        # methods in the overloaded PushButton event handler
        qApp.quit()
        print "qApp.quit() returned"
        # calling sys.exit(0) here works, but prevents 
        # a.exec_loop from returning and Qt from terminating
        # properly
        #sys.exit(0)
        # The savest method to savely terminate this module
        # without former mentioned problems
        #return QWidget.close(self, alsoDelete)

class qtTest2(QWidget):
    def __init__(self, parent = None, name = None, fl = 0):
        QWidget.__init__(self, parent, name, fl)
        self.setCaption("PyQt dtor testing v2")
        vbox = QVBoxLayout(self, 4, -1, "vbox")
        pb = PushButton("Quit", self, "pb")
        vbox.addWidget(pb)
        self.connect(pb, SIGNAL("clicked()"), self.closeEvent)

    def closeEvent(self, e = None):
        print "closeEvent", e
        # calling qApp.quit() here seems to delay Qts event
        # distribution until after python cleaned up __main__
        # which triggers AttributeErrors from accessing qte
        # methods in the overloaded PushButton event handler
        qApp.quit()
        print "qApp.quit() returned"
        return
        # calling sys.exit(0) here works, but prevents 
        # a.exec_loop from returning and Qt from terminating
        # properly
        #sys.exit(0)
        # The savest method to savely terminate this module
        # without former mentioned problems in an overloaded
        # closeEvent (which is an Qt signal handler also)
        if e:
            e.accept()
        else:
            self.close()

if __name__ == "__main__":
    a = QApplication(sys.argv)
    if len(sys.argv) > 1 and sys.argv[1] == "2":
        w = qtTest2()
    else:
        w = qtTest1()
    w.resize(280, 20)
    a.setMainWidget(w)
    w.show()
    a.connect(qApp, SIGNAL("lastWindowClosed()"), SLOT("quit()"))
    ret = a.exec_loop()
    print "exec_loop:", ret

Reply via email to