10

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

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

在此处输入图像描述

在此处输入图像描述

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

  1. 我必须编写一个装饰器黑客才能很好地打印另一个线程的异常。
  2. 即使功能正常,结果也很丑陋。

在此处输入图像描述

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

爪哇:

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 中是否有更好的方法?

4

2 回答 2

3

在 Python 中,您可以设置sys.execpthook一个函数,以便为未捕获的异常调用该函数。然后你就不需要装饰器了,你可以在你的钩子函数中集中处理异常。

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

于 2013-04-04T16:38:34.070 回答
3

在 Java 中,如果TaskDialog不可用,您也许可以使用,如此JOptionPane所示。从事件分派线程以外的线程,使用 包装调用,如此处所建议。还可以考虑添加一个可选规定来调用.EventQueue.invokeLater()Desktop#mail()

图片

于 2013-04-04T17:04:16.107 回答