【问题标题】:PyQt event handlers snarf exceptionsPyQt 事件处理程序捕获异常
【发布时间】:2013-01-07 17:18:41
【问题描述】:

这是说明问题的代码:

from PyQt4 import QtGui
app = QtGui.QApplication([])
dialog = QtGui.QDialog()
button = QtGui.QPushButton('I crash')
layout = QtGui.QHBoxLayout()
layout.addWidget(button)
dialog.setLayout(layout)
def crash(): raise Exception('Crash!')
button.clicked.connect(crash)
button.click()
print 'I should not happen'

当我运行它时,PyQt4 会为我处理错误。我的控制台显示带有“崩溃!”的堆栈跟踪等等,我看到'我不应该发生'。

这没什么用,因为我收到了一个包含许多处理程序的大型应用程序,我需要将它们的错误所有强加到我的脸上(以及我的 - 咳咳 - 自动化测试) .每次我运行时,错误都会从我的网络中逃脱,它们需要在每个处理程序中使用过多且无用的 try:except 块,以便将它们全部捕获。

换句话说,我希望好的代码非常好,而坏的代码非常糟糕。没有粉饰。

抱歉,如果这已经被问到了,但是当我电子搜索它时,我自然会得到成千上万的新手询问基本的错误处理问题(或者,更糟糕的是,我让新手询问如何关闭他们任性的异常!;)

如何覆盖 PyQt4 的默认错误处理,以便我自己传播或记录错误?也请不要回答 sys.excepthook - 它会捕获 PyQt4 没有捕获的错误。

【问题讨论】:

  • 我认为你的问题是异常发生在 PyQt 事件循环运行的地方(我认为这是一个不同的线程)所以你真的在问如何在线程之间移动异常.
  • sry no - 我发布的代码是完整的。没有 MainLoop 事件调度器;我只是直接调用 .click() 。
  • 即使它没有使用多线程,它仍然在运行事件循环机制(可以将函数执行移动到不同的线程),所以我认为问题仍然是在线程之间移动异常,只是来源和目的地是一样的。
  • 尝试 sys.excepthook了吗?它将捕获该异常。
  • print threading.currentThread() 在 .click() 之前和 def crash() 内部返回相同的对象。 (而且,一般来说,窗口架构是单线程和事件驱动的。)

标签: exception-handling pyqt pyqt4


【解决方案1】:

这不是答案,你这个愚蠢的网站。不要再强迫我们适应理想化线程模板的先入为主的概念了。

答案是使用我的测试框架 setUp() 来挂钩异常处理程序:

def setUp(self):
    self.no_exceptions = True

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

    sys.excepthook = testExceptionHook

如果说“self.no_exceptions = False”,我宁愿简单地说 self.fail('')。但是,由于 Python 的单元测试库坚持抛出异常只是为了注册测试失败,而且由于 PyQt 坚持捕获所有异常,我们陷入了死锁。

为了适应 unittest.TestCase 对理想化测试用例的愚蠢先入为主的概念,我必须改为设置一个变量,然后在拆解中检测它:

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

这仍然不理想,但至少它会迫使我花更多时间关注错误,而不是花时间在技术网站上抱怨它们。

根本问题:如何关闭 PyQt 的神奇错误处理程序? - 仍未得到答复...

【讨论】:

    【解决方案2】:

    我认为答案是这不是PyQt 的“功能”,而是让信号/插槽工作的设计所固有的结果(请记住,信号/插槽通信通过 c++ 层作为好)。

    这很丑陋,但确实解决了您的问题

    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'
    

    这不会打印“不应该发生”,并为您提供一些可以通过自动化测试捕获的信息(使用正确的异常类型)。

    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]: 
    

    堆栈跟踪被顶起,但您仍然可以读取打印出来的第一个。

    【讨论】:

    • tx - 我确实使用 sys.excepthook 错误。然而......无论我在钩子内写什么,我似乎都无法摆脱事件处理程序。我必须放弃编写一个如果有未处理的异常就死掉的程序的目标。我必须减少我的目标,只在测试框架中充分捕获异常。而且因为测试已经/有/一个框架,我不需要你的包装器......但根本问题仍然没有答案 - 如何关闭这个 PyQt 功能关闭
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-02
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    相关资源
    最近更新 更多