wxPython, context menus, right-click focus [SOLVED]

So this morning I was celebrating having successfully gotten my existing Panda app embedded inside a wxPython frame. Everything worked perfectly! Yeah right…

Or to understand it a different way, consider this scenario:

Right-click somewhere
Context menu appears
Left-click on item in context menu
Context menu closes and action is performed
Right-click somewhere
Context menu appears
Right-click somewhere not in context menu (hoping for new menu)
Context-menu closes, but no new context menu appears (bad)
Right-click somewhere
Context menu appears

Note that Panda is responsible for handling right-clicks in my app, not wxPython. Okay, so at first I thought I could manually destroy the menu object every time a right-click was caught, but it wasn’t doing anything differently. Then I put trace in the event handler for right-clicks, and realized that once the menu was open, right-clicks were no longer being caught at all.

The next step was to try to give wxPython the responsibility of catching right-clicks, but unfortunately it didn’t catch anything at all. (Yes, I removed the handler from Panda at the same time.)

I know for a fact that wxPython is supposed to auto-close any existing context menu when a new one is opened, because the following demo program works as expected.

import wx

def rightclick(event=None):
    print 'right-click!'
    popup = wx.Menu()
    item = wx.MenuItem(popup, wx.NewId(), 'One')
    popup.AppendItem(item)
    popup.Bind(wx.EVT_MENU, lambda(event):0, id=item.GetId())

    sub = wx.Menu()
    subone = wx.MenuItem(sub, wx.NewId(), 'Sub')
    sub.AppendItem(subone)
    popup.Bind(wx.EVT_MENU, lambda(event):0, id=subone.GetId())
    popup.AppendMenu(wx.ID_ANY, 'Two', sub)

    frame.PopupMenu(popup, event.GetPosition())

app = wx.PySimpleApp()
frame = wx.Frame(None, -1, 'Sandbox', size=(640, 480))
frame.Bind(wx.EVT_RIGHT_UP, rightclick)
frame.Show(True)
app.MainLoop()

Anyone have thoughts about this? Everything is much appreciated.

UPDATE: One theory I have is that the first context menu hogs the entire main loop because preference is given to wxPython while it has pending events, as shown in the following code.

def run_wx(self, task):
    """Run any pending WxPython events."""
    while self.evtloop.Pending():
        self.evtloop.Dispatch()
        self.app.ProcessIdle()
    return Task.cont

I think this means that after the first context menu is shown, Panda can’t hear the second one because wxPython has control. Maybe. Idk. I wish I could solve that by telling wxPython to listen for right-clicks too, but when I do that, like I mentioned above, the handlers never fire.

At the time you hold down RMB, the menu is created and focus is given to the menu, and panda loses focus.
So the next time panda window receives RMD down event, that event will be ignored because panda believes RMB is still held down.

It’s easy to solve, accept the UP event instead (mouse3-up). But on Windows, all UP events are fired upon any window event, so you’d need a flag to mark the real RMB down event.

import direct.directbase.DirectStart
import os, wx

def rightclick():
    global RMBdown
    if not RMBdown: return
    RMBdown = False

    print 'right-click!'
    popup = wx.Menu()
    item = wx.MenuItem(popup, wx.NewId(), 'One')
    popup.AppendItem(item)
    popup.Bind(wx.EVT_MENU, lambda(event):0, id=item.GetId())

    sub = wx.Menu()
    subone = wx.MenuItem(sub, wx.NewId(), 'Sub')
    sub.AppendItem(subone)
    popup.Bind(wx.EVT_MENU, lambda(event):0, id=subone.GetId())
    popup.AppendMenu(wx.ID_ANY, 'Two', sub)

    frame.PopupMenu(popup)

def down():
    global RMBdown
    RMBdown = True

RMBdown = False

base.startWx()
frame = wx.Frame(None)
frame.Show()

base.accept('escape',os._exit,[0])
base.accept('mouse3',down)
base.accept('mouse3-up',rightclick)

run()

Very logical. I’m grateful, thanks!