How can I use the native error icon and error sound in Windows using wxPython?

python, windows, wxpython

Solution

The `wx.MessageBox` calls the os-specific standard message/alert dialog box (on windows, that's MessageBox() from user32.dll). wxWidgets simply translates the flags you provide (like wx.OK and wx.ICON_INFORMATION) to the os-specific flags and options for the native message box.

You can obtain the os-specific icons through the `wx.ArtProvider`.

As far as sounds go, `wx.Sound` can only play sound files. However, if the application does not have to be portable, you can use the `winsound` module.

import wx
import winsound # windows only

class Frame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,None,wx.ID_ANY)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_INFORMATION)) )
        sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_QUESTION)) )
        sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_WARNING)) )
        sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_ERROR)) )
        self.SetSizerAndFit(sizer)
        self.Show()
        winsound.MessageBeep(winsound.MB_ICONASTERISK)
        winsound.PlaySound('SystemHand', winsound.SND_ASYNC | winsound.SND_ALIAS)

app = wx.PySimpleApp()
Frame()
app.MainLoop()

Problem

This simple line: ``` wx.MessageBox('Foo', 'Bar', wx.OK | wx.ICON_ERROR) ``` Gives me a message box with an error icon and the Windows error noise (this is not the same noise as `wx.Bell()`). I would like to create a custom error dialog for uncaught exceptions, where the traceback is available in a text control and such, and I would like to include both the Windows error icon and the noise. I know that both differ between versions of Windows, and the error noise can even be customized. Is there any straight-forward way of using these native Windows resources with wxPython? Bonus question; if the answer is no, what would be the most straight-forward way of doing what I'm trying to do? Results after accepted answer: I just wanted to show off the results after Anonymous Coward's excellent answer, as they far exceeded my expectations. This is the error dialog that now pops up on unhandled exceptions (on Windows 8): It also packs the modern Windows "UNNK!" error sound. This is the code behind the dialog. I put it in a separate module that overrides `sys.excepthook` when it's imported: ``` """This module, when imported, overrides the default unhandled exception hook with one that displays a fancy wxPython error dialog.""" import sys import textwrap import traceback import winsound import wx def custom_excepthook(exception_type, value, tb): dialog = ExceptionDialog(exception_type, value, tb) dialog.ShowModal() # Override sys.excepthook sys.excepthook = custom_excepthook class ExceptionDialog(wx.Dialog): """This class displays an error dialog with details information about the input exception, including a traceback.""" def __init__(self, exception_type, exception, tb): wx.Dialog.__init__(self, None, -1, title="Unhandled error", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) self.SetSize((640, 480)) self.SetMinSize((420, 200)) self.exception = (exception_type, exception, tb) self.initialize_ui() winsound.MessageBeep(winsound.MB_ICONHAND) def initialize_ui(self): extype, exception, tb = self.exception panel = wx.Panel(self, -1) # Create the top row, containing the error icon and text message. top_row_sizer = wx.BoxSizer(wx.HORIZONTAL) error_bitmap = wx.ArtProvider.GetBitmap( wx.ART_ERROR, wx.ART_MESSAGE_BOX ) error_bitmap_ctrl = wx.StaticBitmap(panel, -1) error_bitmap_ctrl.SetBitmap(error_bitmap) message_text = textwrap.dedent("""\ I'm afraid there has been an unhandled error. Please send the contents of the text control below to the application's developer.\ """) message_label = wx.StaticText(panel, -1, message_text) top_row_sizer.Add(error_bitmap_ctrl, flag=wx.ALL, border=10) top_row_sizer.Add(message_label, flag=wx.ALIGN_CENTER_VERTICAL) # Create the text control with the error information. exception_info_text = textwrap.dedent("""\ Exception type: {} Exception: {} Traceback: {}\ """) exception_info_text = exception_info_text.format( extype, exception, ''.join(traceback.format_tb(tb)) ) text_ctrl = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE | wx.TE_DONTWRAP) text_ctrl.SetValue(exception_info_text) # Create the OK button in the bottom row. ok_button = wx.Button(panel, -1, 'OK') self.Bind(wx.EVT_BUTTON, self.on_ok, source=ok_button) ok_button.SetFocus() ok_button.SetDefault() sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(top_row_sizer) # sizer.Add(message_label, flag=wx.ALL | wx.EXPAND, border=10) sizer.Add(text_ctrl, proportion=1, flag=wx.EXPAND) sizer.Add(ok_button, flag=wx.ALIGN_CENTER | wx.ALL, border=5) panel.SetSizer(sizer) def on_ok(self, event): self.Destroy() ``` The only improvement I could wish for is for the static text to flow and wrap automatically according to the width of the dialog, but I couldn't be bothered to make a custom control class just for that.

Original source