Handling unhandled exception in GUI
Question: I am mostly writing a small tools for tech savvy people, e.g. programmers,
engineers etc. As those tools are usually quick hacks improved over time I
know that there are going to be unhandled exceptions and the users are not
going to mind. I would like the user to be able to send me the traceback so I
can examine what happened and possibly improve the application.

I usually do wxPython programming but I have done some Java recently. I have
hooked up the [`TaskDialog`](http://code.google.com/p/oxbow/) class to the
`Thread.UncaughtExceptionHandler()` and I am quite happy with the result.
Especially that it can catch and handle exceptions from any thread:

![enter image description here](http://i.stack.imgur.com/G0948.png)

![enter image description here](http://i.stack.imgur.com/Q6SI6.png)

I was doing something similar in wxPython for a long time. However:

  1. I had to write a decorator-hack to be able to print exceptions from another thread well.
  2. Even when functional, the result is quite ugly.

![enter image description here](http://i.stack.imgur.com/WeaUH.png)

Here is the code for both Java and wxPython so you can see what I have done:

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()
    

Now the question:

**Can I do easily something similar to Java solution in wxPython? Or maybe, is
there a better way in either Java or wxPython?**


Answer: In Python you can set `sys.execpthook` to a function that you want to be
called for uncaught exceptions. Then you won't need the decorators, you can
just deal with the exceptions centrally in your hook function instead.

And rather than just printing the exception traceback text and letting it be
displayed in the stock stdout window you can do something more intelligent
with it, like using a dialog to display the text and have controls that allow
the user to send the error information back to the developer, to ignore future
errors, to restart the application, or whatever you want.

