wxDialog ShowModal() function

not sure if this is the right place for such question, but I need some help, ShowModal() causes panda3d, inside a wxDialog, to get stuck, it does not render nothing and the screen is black.

ShowModal --> Note that this function creates a temporary event loop which takes precedence over the application’s main event loop (see wxEventLoopBase) and which is destroyed when the dialog is dismissed

I think this temporary event loop that interrupts panda.

Any suggestion?

Yes, I agree with your diagnosis. The best solution, I think, is not to use ShowModal(). That function is designed to pause the entire application, so it is behaving exactly as designed.

David

ShowModal() keeps the dialog focused, with Show() everything is fine but it can lose focus.

I have to write a custom wxDialog with that property. I dunno at the moment.

I will keep searching and practicing! I come back to report any goal.

def pandaShowModalDialog(dlg):
    # safely start modal dialog and keep Panda running
    def onTimer(event):
        try:
            base.taskMgr.step()
        except:
            pass
    pandaTimer = wx.Timer(dlg, - 1)
    dlg.Bind(wx.EVT_TIMER, onTimer, id = pandaTimer.GetId())
    # keep FPS resonable, smaller value make Wx unresponsive
    pandaTimer.Start(35)

    return dlg.ShowModal()

def wxStep():
    # process all pending Wx events
    eloop = wx.EventLoop.GetActive()
    app = wx.GetApp()
    if eloop and app:
        while eloop.Pending():
            eloop.Dispatch()
            app.ProcessIdle()

# merge Panda and Wx loops
def run():
    while True:
        base.taskMgr.step()
        wxStep()
        time.sleep(0.001)

P.S: For calling dialogs from Panda you can use wx.CallAfter method

I tried this:

dialog = Widgets.Dialog()

# safely start modal dialog and keep Panda running 
def onTimer(event): 
    try: 
        base.taskMgr.step() 
    except: 
        pass 

pandaTimer = wx.Timer(dialog, - 1) 
dialog.Bind(wx.EVT_TIMER, onTimer, id = pandaTimer.GetId()) 
# keep FPS resonable, smaller value make Wx unresponsive 
pandaTimer.Start(35) 
    
dialog.ShowModal()

and I got this response:
Assertion failed: _num_busy_threads == 0 at line 865 of c:\p\p3d\panda3d-1.6.2\panda\src\event\asyncTaskChain.cxx

another attempt: I used the Show() function instead ShowModal() and I wrote a basic task that calls dialog.SetFocus() and returns task.cont, almost there but the wx.Frame continues to respond to events such as mouse clicks or menu selections or button clicks! Next step is to avoid those events while the dialog does not exit. Not sure if this is a fine solution.

You can’t call TaskManager step in context of another one.
That is why I created my own loop calling Panda and Wx
one after another instead of processing Wx events in Panda
context.

def run():
    while True:
        base.taskMgr.step()
        wxStep()
        time.sleep(0.001) 

With this custom loop you can in Wx context call modal windows and run Panda’s task manager periodically with timer.

If you need to do the same in Panda context use wx.CallAfter:

def callDialog(res):
    dlg = MyDialog()
    ret = pandaShowModalDialog(dlg)
    dlg.Destroy()
    res[0] =  True
    res[1] = ret

def myContinuation(val):
    # do something
    pass

def waitDialogTask(cont, dialogRet):
    finished, result = dialogRet
    if finished:
        cont(result)
        return None
    else:
        return Task.cont

res = [False, None]

wx.CallAfter(callDialog, res)
taskMgr.add(waitDialogTask, 'MyDialogTask', extraArgs=[myContinuation, res])

You can disable/enable temporary main Wx window with win.Enable(False/True) but there is another problem.

When you use Show you must handle dialog
controls by yourself.

All standard Wx dialogs become useless becouse their controls
are not exported to Python level.

I see, I have a custom loop too that’s why I did not try yours.

That’s what I achieved, it does the same as I expected:

class MyDialog(wx.Dialog)
    def __init__(self):
        ...

        self.Bind(wx.EVT_BUTTON, self.OnClose, self.cancelButton)

        App.Enable(False) # wx.Frame
        
    def OnClose(self, event):
        App.Enable(True) # wx.Frame
        event.Skip()

Thank you, I will attempt your custom loop.