Hello,

I am having a problem showing a popup menu in my wxpython script on fedora 5 (fc5); this problem does not occur on xp. I am using wxPython-2.6.3.2-1.fc5, unicode version.

I need to add an item to a popup menu after the user selects a file using wx.FileDialog, and then need to show the popup menu immediately after the file selection to inform the user of the files that have been selected thus far.

The popup menu will not show on fc5 if I first display the wx.FileDialog control - if I avoid wx.FileDialog the popup menu appears without a problem.

#!/usr/bin/python 
 
import wx, time, os 
 
thislimit = 5 
thispopupmenu = None 
 
 
def MakeThePopup(self): 
 
    global thispopupmenu 
 
    menu = wx.Menu() 
     
    ids = [] 
     
    for ii in range(thislimit):
        mnuid = wx.NewId() 
        ids.append(mnuid) 
        menu.Append(mnuid, "File " + str(ii+1)) 
     
    menu.AppendSeparator() 
    add_popupmenu_id = 10102 
    ids.append(add_popupmenu_id) 
    menu.Append(add_popupmenu_id, "Add a File") 
 
     
    for daid in ids: 
        wx.EVT_MENU(self, daid, PopupClicked) 
 
    thispopupmenu = menu 
     
### ---   end of MakeThePopup function   --- ### 
 
 
def PopupClicked(event): 
    
    # get the text of the menu item that was clicked
    mnulabel = event.GetEventObject().GetLabel(event.GetId())
    
    # let's be certain that a menu click is being processed 
    daframe.SetTitle(mnulabel + ' - ' + str(time.time())) 
    
    # activate wx.FileDialog if appropriate popup menu item is clicked 
    if (mnulabel == "Add a File"): 
        global thislimit 
        thislimit = thislimit + 1 
        daframe.SetTitle(mnulabel)
        dlg = wx.FileDialog(daframe, message="Select a File:",\
                        defaultDir=os.getcwd(), defaultFile="",\
                        wildcard="All files (*.*)|*.*",style=wx.OPEN)
                            
        ###  uncomment the following 2 lines of code and
        ###  the popup menu will not appear on fc5    
        
        #if dlg.ShowModal() == wx.ID_OK:
        #    thisresult = "a file was selected"
        
        ###  ######################
            
        # make the poup menu
        MakeThePopup(daframe.button)
        
        # show the popup menu over the button
        daframe.button.PopupMenu(thispopupmenu,wx.Point(5,5))
        # the popup fails to show if a wx.FileDialog has
        # been activated previously
         
        return True 
    
     
    event.Skip() 
     
### ---  end of PopupClicked function   --- ###     
 
 
class DisFrame(wx.Frame): 
    def __init__(self, *args, **kwargs): 
        wx.Frame.__init__(self, *args, **kwargs) 
        
        # make a panel with a button 
        panel = wx.Panel(self) 
        self.button = wx.Button(panel, -1, "Show popup menu") 
        self.button.Bind(wx.EVT_BUTTON, self.ButtonClicked) 
        bs = wx.BoxSizer(wx.HORIZONTAL) 
        bs.Add(self.button,0,wx.ALL,30) 
        panel.SetSizer(bs) 
         
        MakeThePopup(self) 
         
         
    def ButtonClicked(self, event): 
        
        # make the popup 
        MakeThePopup(self)
        
        # show the popup over the button 
        self.button.PopupMenu(thispopupmenu) 
     
        event.Skip() 
 
### ---  end of DisFrame class   --- ### 
 
 
 
if __name__ == '__main__': 
    app = wx.App() 
    daframe = DisFrame(None) 
    daframe.Show() 
    app.MainLoop()

The popup menu appears if running the above script on fc5.

However, if I uncomment the 2 lines of code that show the wx.FileDialog, the popup fails to appear on fc5 and I need to click 'Stop Executing' after closing the application if running the script through SciTE.

How can I show the popup menu after activating a wx.FileDialog control ?

Any help will be appreciated. Thanks.

_

Dani AI

Generated

Summary of the issue and context from : on Fedora/GTK the popup menu often fails to appear if a modal dialog (FileDialog/MessageDialog/ColorDialog) was just shown. Common attempts such as destroying the dialog or forcing a Refresh/Update (suggested by and ) do not reliably help on that platform.

Why this happens: GTK modal dialogs take an input grab and run their own nested event loop. When the modal closes, the toolkit’s internal grab/focus state isn’t always fully restored until the main event loop gets a chance to run again. Calling PopupMenu immediately while the toolkit is still tidying up can cause the menu to be suppressed or never mapped — which explains why the same code works on Windows but not on Linux/GTK.

Practical, robust approaches (safe alternatives and diagnostics): defer the popup until after the current event finishes rather than calling it synchronously right after closing the dialog. Typical methods are scheduling the popup for the next event-loop turn (framework facilities that run a callback once on the next idle/loop pass), posting a one-shot idle/custom event, or using a zero-delay timer. Avoid calling low-level re-entrant helpers (e.g., forcing a full yield/processEvents) because they can create subtle reentrancy bugs. Also confirm the popup is invoked on the correct parent window, create the menu at the time it will be shown, and always clean it up after use to avoid leaks.

Additional best practices: prefer modern Bind-style event binding for menu handlers; use stable item references or wx.ID_ANY instead of ad-hoc NewId loops; protect destruction with try/finally if needed; and reproduce the problem on the target GTK version while iterating on fixes. This platform-specific behavior is the reason the deferred-call solution recommended by Robin Dunn works reliably.

Recommended Answers

All 9 Replies

You might have to destroy the dialog after it has been used:

###  uncomment the following 2 lines of code and
        ###  the popup menu will not appear on fc5    
        
        if dlg.ShowModal() == wx.ID_OK:
            # do something
            thisresult = "a file was selected"
        
        dlg.Destroy()
        
        ###  ######################

Thanks for the tip bumsfeld.

dlg.Destroy() should have been there (silly oversight on my part).

Unfortunately the popup menu still fails to appear on fc5 if activating wx.FileDialog and then destroying the dialog.

The program seems to 'hang' (though it's still functional) and requires using SciTE's 'Stop Executing' command to completely kill the running process.

Very puzzling.

I don't have Fedora, so I can't see the effect, but check if Refresh() or Update() will help ...

###  uncomment the following 2 lines of code and
        ###  the popup menu will not appear on fc5
        
        if dlg.ShowModal() == wx.ID_OK:
            # do something
            thisresult = "a file was selected"
        
        dlg.Destroy()
        daframe.Refresh()  # next event
        daframe.Update()   # immediate
        
        ###  ######################

Followed your advice vegaseat, but still no luck - popup menu does not show on fc5 if I use a wx.FileDialog:

if dlg.ShowModal() == wx.ID_OK:
    thisresult = "a file was selected"
dlg.Destroy()
daframe.Refresh()
daframe.Update()

My feable attempts to debug the problem have revealed that the popup menu is apparently created without a problem, but that the problem occurs when the program tries to show it above the button:

# make the popup menu
MakeThePopup(daframe.button)
daframe.SetTitle('popup menu is created')
        
# show the popup menu over the button
daframe.button.PopupMenu(thispopupmenu,wx.Point(5,5))
daframe.SetTitle('showing popup menu')

The above always produces a window title that reads 'popup menu is created' when a wx.FileDialog is used on fc5.

The title never reads 'showing popup menu' when using a file dialog box on fc5, indicating that the problem lies with the program's attempt to display the popup menu.

Are we any closer at finding a solution ?

_

I have made a simpler version of this program in the hope that someone will be able to offer a solution.

This version simply displays a message box after clicking a button, and then within the same function attempts to display a popup menu.

The popup menu will only appear on fc5 if you avoid displaying the message box:

#!/usr/bin/python

import wx, random


def PopupClicked(event):
    
    daframe.SetTitle(str(random.randint(0,100)))    
    event.Skip()
    


class DisFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        
        # make a panel with a button
        panel = wx.Panel(self)
        self.button = wx.Button(panel, -1, "Show popup menu")
        self.button.Bind(wx.EVT_BUTTON, self.ButtonClicked)
        bs = wx.BoxSizer(wx.HORIZONTAL)
        bs.Add(self.button,0,wx.ALL,30)
        panel.SetSizer(bs)
        random.seed()
        
        
    def ButtonClicked(self, event):

        dlg = wx.MessageDialog(daframe,
        message='a message',caption=' ',\
        style=wx.OK|wx.ICON_INFORMATION)
        
        # the popup menu will not appear if
        # this message dialog is shown
        dlg.ShowModal()
        dlg.Destroy()
        # comment-out the above 2 lines and
        # the popup menu will appear
                
        # make and show a popup menu
        menu = wx.Menu()
        menu.Append(101, " menu item 1 ")
        wx.EVT_MENU(self, 101, PopupClicked)
        self.PopupMenu(menu)
        menu.Destroy()
        
        event.Skip()



if __name__ == '__main__':
    app = wx.App()
    daframe = DisFrame(None)
    daframe.Show()
    app.MainLoop()

The same problem occurs whether you display a message box, color dialog or file dialog - the popup menu will only appear if you avoid displaying any type of dialog control beforehand.

How can I display the popup menu on fc5 after displaying the message box ?

_

An initial observation, your popup appears at the location of the dialog box, not in the frame. Is it associated with the wrong parent?

Thanks for the response vegaseat.

The popup fails to show whether I associate it with the main frame or the button (self.button).

I've experienced other problems with the popup menu on fc5 in relation to assigning id numbers via wx.NewId() during the creation of a popup, whereas there were no problems on xp.

Extremely frustrating. Thanks for the input.

_

The following solution was provided by Robin Dunn on a different forum, and makes use of wx.CallAfter():

#!/usr/bin/python

import wx, random

def PopupClicked(event):
      daframe.SetTitle(str(random.randint(0,100)))
      event.Skip()
  
class DisFrame(wx.Frame):
   def __init__(self, *args, **kwargs):
       wx.Frame.__init__(self, *args, **kwargs)
       # make a panel with a button
       panel = wx.Panel(self)
       self.button = wx.Button(panel, -1, "Show popup menu")
       self.button.Bind(wx.EVT_BUTTON, self.ButtonClicked)
       bs = wx.BoxSizer(wx.HORIZONTAL)
       bs.Add(self.button,0,wx.ALL,30)
       panel.SetSizer(bs)
       random.seed()

   def ButtonClicked(self, event):
       dlg = wx.MessageDialog(daframe,
                              message='a message',caption=' ',\
                              style=wx.OK|wx.ICON_INFORMATION)
       dlg.ShowModal()
       dlg.Destroy()
       wx.CallAfter(self.DoPopupMenu)
       

   def DoPopupMenu(self):
       menu = wx.Menu()
       menu.Append(101, " menu item 1 ")
       wx.EVT_MENU(self, 101, PopupClicked)
       self.button.PopupMenu(menu, wx.Point(5,5))
       menu.Destroy()


if __name__ == '__main__':
   app = wx.App()
   daframe = DisFrame(None)
   daframe.Show()
   app.MainLoop()

_

Thanks for letting us know, interesting solution!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.