PyQt事件循环和ipython中的异常 [英] Exceptions in PyQt event loop and ipython

查看:415
本文介绍了PyQt事件循环和ipython中的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个显示一些小部件和按钮的PyQt程序。

I have a PyQt program that is displaying some widgets and buttons.

我希望程序既可以作为独立的python实例运行,也可以在ipython环境中运行。在这种情况下,我在Jupyter控制台中使用以下magic命令(以前我必须在启动ipython qtconsole时使用--gui = qt)

I want the program to run either as a standalone python instance, or inside an ipython environment. In this case, I use the following magic command in Jupyter console (formerly I had to use --gui=qt when launching the ipython qtconsole)

%pylab qt

为了拥有一个程序两种方式都有,我的主要模块有以下几行:

In order to have a program that works both ways, my main module has the following lines:

APP = QtGui.Qapplication.instance() # retrieves the ipython qt application if any
if APP is None:
    APP = QtGui.QApplication(["foo"]) # create one if standalone execution

if __name__=='__main__':
    APP.exec_() # Launch the event loop here in standalone mode 

这是我的问题:事件循环生成的异常很难被用户检测到,因为它们在后台控制台中弹出。我想捕获事件循环中发生的任何异常,并显示警告(对于QMainWindow状态栏中的intance,以使用户意识到发生了异常)。

Here is my problem: exceptions generated by the event loop are very hard to detect by the user because they pop-out in the background console. I would like to catch any exception occuring in the event loop, and display a warning (for intance in the QMainWindow status bar to make the user aware that an exception occured).

我尝试了几种策略,但PyQt和Ipython的内部机器之间似乎存在一个阴谋,使其无法实现:

I have tried several strategies, but there seems to be a conspiracy between PyQt's and Ipython's internal machineries to make this impossible:

  • Reimplement sys.excepthook (see Preventing PyQt to silence exceptions occurring in slots): not working because ipython keeps overwritting sys.excepthook
  • Detecting if IPython is running, and then using IPYTHON.set_custom_exc (Opening an IPython shell on any (uncatched) exception): Unforunately, qt event loop exceptions do not trigger the handler.
  • Overwrite QApplication.notify: bad luck, the native QApplication.notify function that I intend to call in the derived function is not throwing exceptions, nor does the return value (boolean) reflect the correct execution of the slots. The answer in this thread is interesting: How to log uncatched exceptions of a QApplication?, however, it seems that this strategy works in Qt c++, but the python wrapper of notify just prints the exceptions to the console instead of raising them.

这是一个长期困扰我的问题。有没有人有解决方案?

It is a problem that keeps bothering me since a long time. Does anyone have a solution?

推荐答案

实际上,开发人员的回答指出了我正确的方向:
问题是每次执行一个ipython单元时,都会对一个新的sys.excepthook进行monkeypatched,一旦执行完毕,sys.excepthook就会返回到前一个(参见ipkernel / kernelapp.py)。

Actually, the dev's answer pointed me in the right direction: the problem is that each time an ipython cell is executed, a new sys.excepthook is monkeypatched, once the execution is done, the sys.excepthook is brought back to the previous one (see ipkernel/kernelapp.py).

因此,在正常的ipython单元格指令中更改sys.excepthook不会改变在qt事件循环期间执行的异常错误。

Because of this, changing sys.excepthook in a normal ipython cell instruction will not change the excepthook that is executed during the qt event loop.

一个简单的解决方案是在qt事件中monkeypatch sys.excepthook:

A simple solution is to monkeypatch sys.excepthook inside a qt event:

from PyQt4 import QtCore, QtGui
import sys
from traceback import format_exception

def new_except_hook(etype, evalue, tb):
    QtGui.QMessageBox.information(None, 
                                  str('error'),
                                  ''.join(format_exception(etype, evalue, tb)))

def patch_excepthook():
    sys.excepthook = new_except_hook
TIMER = QtCore.QTimer()
TIMER.setSingleShot(True)
TIMER.timeout.connect(patch_excepthook)
TIMER.start()

这个方法的一个好处是它适用于独立和ipython执行。

A nice thing about this method is that it works for standalone and ipython execution alike.

我想也可以设想通过在每个小部件的event_handler中调用patch_excepthook来修补不同版本的new_except_hook,具体取决于触发异常的小部件。

I guess one could also imagine to monkey patch a different version of new_except_hook depending on what widget is triggering the exception by calling patch_excepthook inside the event_handler of each widget.

这篇关于PyQt事件循环和ipython中的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆