Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling unhandled exception in GUI

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

enter image description here

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

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?

like image 965
Fenikso Avatar asked Apr 04 '13 14:04

Fenikso


2 Answers

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.

like image 128
RobinDunn Avatar answered Oct 18 '22 20:10

RobinDunn


In Java, if TaskDialog is not available, you may be able to use JOptionPane, as shown here. From a thread other than the event dispatch thread, wrap the call using EventQueue.invokeLater(), as suggested here. Also consider adding an optional provision to invoke Desktop#mail().

image

like image 30
trashgod Avatar answered Oct 18 '22 21:10

trashgod