Search code examples
wxpythonwxwidgets

wxPython: Setting focus with a wx.PostEvent does not work if destroying a modal dialog


I am having trouble with setting the focus of a button AFTER a modal dialog is destroyed.

Is there a call I can make to destroy the modal dialog or to stop it from being modal and then destroy it?

When the wx.PostEvent triggers an event it forces a Destroy on a modal dialog however as I understand it, it doesn't get destroyed immediately which means that the buttons are still disabled when I do a SetFocus().

import wx
import wx.lib.newevent
from threading import Thread
import time
processFinishes, EVT_PROCESS_FINISHES = wx.lib.newevent.NewEvent()

class Dummy(Thread):
    def __init__(self, arg):
        super(Dummy, self).__init__()
        self.arg = arg

    def run(self):
        time.sleep(15)
        print "Posting"
        wx.PostEvent(self.arg , processFinishes(result=(None)))


class MyRegion(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.button = wx.Button(self, label="Click me!")
        self.mybutton2 = wx.Button(self, label="I should have focus!!!")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.button, 0, wx.ALL)
        sizer.Add(self.mybutton2, 0, wx.ALL)
        self.SetSizerAndFit(sizer)
        self.Bind(wx.EVT_BUTTON, self.OnButton)
        self.Bind(EVT_PROCESS_FINISHES, self.OnFinish)
        self.progress = None
        self.t = Dummy(self)

    def OnButton(self, event):
        self.progress = wx.ProgressDialog("Processing",
                                          "Please wait while the processing finishes.")
        self.progress.Pulse()
        self.progress.ShowModal()
        self.t.start()

    def OnFinish(self, _event):
        print "destroyed"
        self.progress.Destroy()
        print "now setting foucs"
        self.mybutton2.SetFocus()

if __name__ == "__main__":
    app = wx.App()
    frame = MyRegion(None)
    frame.Show()
    app.MainLoop()

EDIT:

When attempting to stop the progress dialog from being modal, the following code gives me errors indicating that the ProgressDialog is not modal whereas it clearly is:

    .....
    def OnButton(self, event):
        # wx.ProgressDialog can also be created with
        # style=wx.wx.PD_APP_MODAL which cannot be made non-modal later on
        # i.e. EndModal() does not work
        self.progress = wx.ProgressDialog("Processing",
                                          "Please wait while the processing finishes.")
        self.progress.Pulse()
        self.progress.ShowModal()
        self.t.start()

    def OnFinish(self, _event):
        print "destroyed"
        # By changing this line
        # wx complains indicating that the progress dialog
        # is not modal whereas a ShowModal was used

        self.progress.EndModal(0) # -> produces a "wx._core.PyAssertionError: C++ assertion "IsModal()" failed at ..\..\src\msw\dialog.cpp(221) in wxDialog::EndModal(): EndModal() called for non modal dialog"
        print "now setting foucs"
        self.mybutton2.SetFocus()

Solution

  • The modal dialog reenables everything by the time ShowModal() returns, so just call OnFinish() directly from OnButton().

    However notice that wxProgressDialog is not a modal dialog at all (it would be pretty useless if it were as you wouldn't be able to call Update() on it!), so in its case you should just destroy it when you are done with it (or use wxPD_AUTO_HIDE style to tell it to go away on its own when maximal progress value is reached).