【问题标题】:Handling unhandled exception in GUI在 GUI 中处理未处理的异常
【发布时间】:2013-04-05 01:04:16
【问题描述】:

我主要为精通技术的人编写一个小工具,例如程序员,工程师等。由于这些工具通常会随着时间的推移快速改进,我知道会有未处理的异常,用户不会介意。我希望用户能够向我发送回溯,以便我可以检查发生的情况并可能改进应用程序。

我通常做 wxPython 编程,但最近我做了一些 Java。我已经将TaskDialog 类连接到Thread.UncaughtExceptionHandler(),我对结果非常满意。特别是它可以捕获和处理来自任何线程的异常:

很长一段时间以来,我一直在 wxPython 中做类似的事情。然而:

  1. 我必须编写一个 decorator-hack 才能很好地打印来自另一个线程的异常。
  2. 即使功能正常,结果也很糟糕。

这里是 Java 和 wxPython 的代码,所以你可以看到我做了什么:

Java:

import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.JButton;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import com.ezware.dialog.task.TaskDialogs;

public class SwingExceptionTest {

    private JFrame frame;

    public static void main(String[] args) {

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (ClassNotFoundException e) {
        }
        catch (InstantiationException e) {
        }
        catch (IllegalAccessException e) {
        }
        catch (UnsupportedLookAndFeelException e) {
        }

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable e) {
                TaskDialogs.showException(e);
            }
        });

        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    SwingExceptionTest window = new SwingExceptionTest();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public SwingExceptionTest() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[]{0, 0};
        gridBagLayout.rowHeights = new int[]{0, 0};
        gridBagLayout.columnWeights = new double[]{0.0, Double.MIN_VALUE};
        gridBagLayout.rowWeights = new double[]{0.0, Double.MIN_VALUE};
        frame.getContentPane().setLayout(gridBagLayout);

        JButton btnNewButton = new JButton("Throw!");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                onButton();
            }
        });
        GridBagConstraints gbc_btnNewButton = new GridBagConstraints();
        gbc_btnNewButton.gridx = 0;
        gbc_btnNewButton.gridy = 0;
        frame.getContentPane().add(btnNewButton, gbc_btnNewButton);
    }

    protected void onButton(){
        Thread worker = new Thread() {
            public void run() { 
                throw new RuntimeException("Exception!");
            }
        };
        worker.start();
    }

}

wxPython:

import StringIO
import sys
import traceback
import wx
from wx.lib.delayedresult import startWorker


def thread_guard(f):
    def thread_guard_wrapper(*args, **kwargs) :
        try:
            r = f(*args, **kwargs)
            return r
        except Exception:
            exc = sys.exc_info()
            output = StringIO.StringIO()
            traceback.print_exception(exc[0], exc[1], exc[2], file=output)
            raise Exception("<THREAD GUARD>\n\n" + output.getvalue())
    return thread_guard_wrapper

@thread_guard
def thread_func():
    return 1 / 0

def thread_done(result):
    r = result.get()
    print r


class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.panel = wx.Panel(self)
        self.button = wx.Button(self.panel, label="Throw!")
        self.button.Bind(wx.EVT_BUTTON, self.OnButton)

        self.sizer = wx.BoxSizer()
        self.sizer.Add(self.button)

        self.panel.SetSizerAndFit(self.sizer)  
        self.Show()

    def OnButton(self, e):
        startWorker(thread_done, thread_func)

app = wx.App(True)
win = MainWindow(None, size=(600, 400))
app.MainLoop()

现在的问题:

我可以在 wxPython 中轻松地做一些类似于 Java 解决方案的事情吗?或者,在 Java 或 wxPython 中是否有更好的方法?

【问题讨论】:

  • 只有少数人会考虑程序员的问题:)

标签: java python swing exception wxpython


【解决方案1】:

在 Python 中,您可以将 sys.execpthook 设置为您希望为未捕获的异常调用的函数。那么你就不需要装饰器了,你可以在你的钩子函数中集中处理异常。

除了打印异常回溯文本并让它显示在标准输出窗口中,您还可以用它做一些更智能的事情,比如使用对话框来显示文本并拥有允许用户发送错误的控件信息返回给开发人员,忽略未来的错误,重新启动应用程序,或任何你想要的。

【讨论】:

  • 使用sys.execpthook 很棒。谢谢!但是,我仍然需要 @thread_guard 装饰器用于 wx.lib.delayedresult 工作线程。异常是从r = result.get() 行而不是return 1 / 0 行引发的,如果我不使用装饰器来保存原始异常中的信息,我将错过重要的调试信息。
【解决方案2】:

在 Java 中,如果 TaskDialog 不可用,您可以使用 JOptionPane,如图所示 here。从event dispatch thread 以外的线程,使用EventQueue.invokeLater() 包装调用,如here 建议的那样。还可以考虑添加一个可选条款来调用Desktop#mail()

【讨论】:

  • 我是 Java 新手,所以我可能没有关注。 1) 我知道我可以使用JOptionPane 而不是TaskDialog。但是在什么情况下TaskDialog会变成“不可用”呢?我已经在我的系统上安装了它,我打算创建一个可执行的 JAR。
  • 2) 我知道如果我想在另一个线程中对 GUI 做任何事情,我应该使用 EventQueue.invokeLater()。但是对于使用 Thread.setDefaultUncaughtExceptionHandler` 处理未处理的异常,它似乎在任何线程中都可以正常工作。我错过了什么吗?我应该将TaskDialogs.showException(e); 包装成EventQueue.invokeLater() 吗?
  • 1) 我的意思是不可用,因为您出于某种原因决定不使用它。 2) 由此产生的隐式数据竞争可能仍然是latent。 EDT 将自动重启,这可能会导致堆栈跟踪混乱,而没有invokeLater() 提供的顺序。
猜你喜欢
  • 2012-09-02
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-29
  • 1970-01-01
相关资源
最近更新 更多