PyQt event handlers snarf exceptions

后端 未结 2 818
予麋鹿
予麋鹿 2020-12-06 18:58

Here\'s the code that illustrates the problem:

from PyQt4 import QtGui
app = QtGui.QApplication([])
dialog = QtGui.QDialog()
button = QtGui.QPushButton(\'I c         


        
相关标签:
2条回答
  • 2020-12-06 19:25

    I think the answer is that this isn't a 'feature' of PyQt, but a consequence inherent to the design that lets signals/slots work (remember that the signal/slot communication is going through a c++ layer as well).

    This is ugly, but does a bit of and end-run around your problem

    from PyQt4 import QtGui
    import time
    app = QtGui.QApplication([])
    
    class exception_munger(object):
        def __init__(self):
            self.flag = True
            self.txt = ''
            self.type = None
    
        def indicate_fail(self,etype=None, txt=None):
            self.flag = False
            if txt is not None:
                self.txt = txt
            self.type = etype
        def reset(self):
    
            tmp_txt = self.txt
            tmp_type = self.type
            tmp_flag = self.flag
            self.flag = True
            self.txt = ''
            self.type = None
            return tmp_flag, tmp_type, tmp_txt
    
    class e_manager():
        def __init__(self):
            self.old_hook = None
    
        def __enter__(self):
            em = exception_munger()
            def my_hook(type, value, tback):
                em.indicate_fail(type, value)
                sys.__excepthook__(type, value, tback) 
            self.old_hook = sys.excepthook
            sys.excepthook = my_hook
            self.em = em
            return self
    
        def __exit__(self,*args,**kwargs):
            sys.excepthook = self.old_hook
    
    def mang_fac():
        return e_manager()
    
    def assert_dec(original_fun):
    
        def new_fun(*args,**kwargs):
            with mang_fac() as mf:
                res = original_fun(*args, **kwargs)
                flag, etype, txt = mf.em.reset()
                if not flag:
                    raise etype(txt)
                return res
        return new_fun
    
    
    @assert_dec
    def my_test_fun():
        dialog = QtGui.QDialog()
        button = QtGui.QPushButton('I crash')
        layout = QtGui.QHBoxLayout()
        layout.addWidget(button)
        dialog.setLayout(layout)
        def crash(): 
            time.sleep(1)
            raise Exception('Crash!')
        button.clicked.connect(crash)
    
    
        button.click()
    
    my_test_fun()
    print 'should not happen'
    

    This will not print 'should not happen' and gives you something to catch with your automated tests (with the correct exception type).

    In [11]: Traceback (most recent call last):
      File "/tmp/ipython2-3426rwB.py", line 68, in crash
    Exception: Crash!
    ---------------------------------------------------------------------------
    Exception                                 Traceback (most recent call last)
    <ipython-input-11-6ef4090ab3de> in <module>()
    ----> 1 execfile(r'/tmp/ipython2-3426rwB.py') # PYTHON-MODE
    
    /tmp/ipython2-3426rwB.py in <module>()
    
    /tmp/ipython2-3426rwB.py in new_fun(*args, **kwargs)
    
    Exception: Crash!
    
    In [12]: 
    

    The stack trace is jacked up, but you can still read the first one that was printed out.

    0 讨论(0)
  • 2020-12-06 19:27

    This is not the answer, you silly website. Stop forcing us to fit into a preconceived notion of an idealized thread template.

    The un-answer is to use my test framework setUp() to hook in an exception handler:

    def setUp(self):
        self.no_exceptions = True
    
        def testExceptionHook(type, value, tback):
            self.no_exceptions = False
            sys.__excepthook__(type, value, tback)
    
        sys.excepthook = testExceptionHook
    

    Where that says "self.no_exceptions = False", I would much rather simply say self.fail(''). However, because Python's unit test library insists on throwing exceptions just to register test failures, and because PyQt insists on snarfing all exceptions, we have a deadlock.

    To fit into unittest.TestCase's silly preconceived notion of an idealized test case, I have to instead set a variable, then detect it in the teardown:

    def tearDown(self):
        self.assertTrue(self.no_exceptions)
    

    This is still not ideal, but at least it will force me to spend more time paying attention to the errors, instead of spending that time complaining about them on technical websites.

    The root question: How to turn off PyQt's magic error handler? - remains unanswered...

    0 讨论(0)
提交回复
热议问题