wx.DirDialog crashes py2app app

When running my app in IDLE (mac) it runs fine, no crashes, but once built in py2app it crashes (completely disappears) when opening the second wx.DirDialog. I am in a multithreading enviroment, so am using wx.CallAfter. I can’t see anything wrong. Python 2.7.9 on OSX.

Here is an extract of my code:

class ReformatClass(wx.Panel):
    def __init__(self, parent, frame, run_params, infoPanel, refreshProviderlist):
        wx.Panel.__init__(self, parent)

        ##Other stuff####

        self.wildcard = "spreadsheet (*.xls,*.xlsx)|*.xls;*.xlsx;*.XLS;*.XLSX"

        wx.StaticText(self, -1, 'Excel Spreadsheet (.xls)', (33, 530), style=wx.ALIGN_CENTRE)
        self.spreadsheetIn1 = wx.TextCtrl(self, -1, pos=(35, 550), size=(550, 25))
        self.buttoSpreadsheet = wx.Button(self, -1, "Open", pos=(590, 548))
        self.buttoSpreadsheet.Bind(wx.EVT_BUTTON, self.openSpreadsheet)

        wx.StaticText(self, -1, 'Input Lookup Directory', (33, 590), style=wx.ALIGN_CENTRE)
        self.pathindir1 = wx.TextCtrl(self, -1, pos=(35, 610), size=(550, 25))
        self.buttonin = wx.Button(self, -1, "Open", pos=(590, 608))
        self.buttonin.Bind(wx.EVT_BUTTON, self.openindir)

        wx.StaticText(self, -1, 'Output Directory', (33, 650), style=wx.ALIGN_CENTRE)
        self.pathoutdir1 = wx.TextCtrl(self, -1, pos=(35, 670), size=(550, 25))
        self.buttonout = wx.Button(self, -1, "Open", pos=(590, 668))
        self.buttonout.Bind(wx.EVT_BUTTON, self.openoutdir)

        ##Other stuff####

    def openSpreadsheet(self, event):
        try:
            dlg = wx.FileDialog(self, message="Choose your Excel Spreadsheet file", defaultFile="", wildcard=self.wildcard, style=wx.OPEN | wx.CHANGE_DIR)
            if dlg.ShowModal() == wx.ID_OK:
                self.spreadsheetIn = dlg.GetPath()
            else:
                self.spreadsheetIn = None
                wx.CallAfter(self.spreadsheetIn1.Clear)
            if self.spreadsheetIn != None:
                wx.CallAfter(self.spreadsheetIn1.Clear)
                wx.CallAfter(self.spreadsheetIn1.WriteText, self.spreadsheetIn)
            dlg.Destroy()
        except Exception:
            self.errorMessage = traceback.format_exc()
            wx.CallAfter(self.infoPanel.OnException, self.errorMessage, self.a)

    def openindir(self, event):
        try:
            dlg = wx.DirDialog(self, "Please choose your input lookup directory:", style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON | wx.DD_CHANGE_DIR)
            if dlg.ShowModal() == wx.ID_OK:
                self.pathindir = dlg.GetPath()
            else:
                self.pathindir = None
                wx.CallAfter(self.pathindir1.Clear)
            if self.pathindir != None:
                wx.CallAfter(self.pathindir1.Clear)
                wx.CallAfter(self.pathindir1.WriteText, self.pathindir)
            dlg.Destroy()
        except Exception:
            self.errorMessage = traceback.format_exc()
            wx.CallAfter(self.infoPanel.OnException, self.errorMessage, self.a)

    def openoutdir(self, event):
        try:
            dlg = wx.DirDialog(self, "Please choose your output directory:", style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON | wx.DD_CHANGE_DIR)
            if dlg.ShowModal() == wx.ID_OK:
                self.pathoutdir = dlg.GetPath()
            else:
                self.pathoutdir = None
                wx.CallAfter(self.pathoutdir1.Clear)
            if self.pathoutdir != None:
                wx.CallAfter(self.pathoutdir1.Clear)
                wx.CallAfter(self.pathoutdir1.WriteText, self.pathoutdir)
            dlg.Destroy()
        except Exception:
            self.errorMessage = traceback.format_exc()
            wx.CallAfter(self.infoPanel.OnException, self.errorMessage, self.a)

And here is the py2app setup.py:


"""
This is a setup.py script generated by py2applet
Usage:
python setup.py py2app
"""

from setuptools import setup
APP = ['myapp.py']
DATA_FILES = []
OPTIONS = {
    'iconfile':'rainbow.icns',
    'plist': {'CFBundleShortVersionString':'1.1',}
}

setup(
    app=APP,
    name='myapp',
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

Any help would be appreciated.

Should I not be using wx.CallAfter at all?

kevjwells wrote:

Should I not be using wx.CallAfter at all?

Without your full code, or at least showing what is running in which threads it's hard to say for sure, but my 99% guess would be "No".

Among other things wx.CallAfter is useful when the non-GUI thread needs to have something executed in the GUI thread. (It adds an event object to the pending event queue which will be processed in the GUI thread as soon as it doesn't have any system events waiting.) It appears that what you are doing in your example code does not fit that pattern. For example, openSpreadsheet is an event handler, so it will be called in the context of the GUI thread, so it does not need to use wx.CallAfter to have self.spreadsheetIn1.Clear called in the GUI thread, because it is already running in that thread. Just call it directly.

···

--
Robin Dunn
Software Craftsman