== Introduction == Make a popup menu when you right-click something. == What Objects are Involved == || Source Component || Component that was clicked. || || {1} Menu Launcher || Event handler, launches the menu. || || {2} Menu || Popup menu itself. || || {3} Menu Handlers || Menu responses to selection. || == Process Overview == 1. Register source's {{{EVT_}}}s to invoke {1} launcher. 2. {1} Launcher creates {2} {{{wxMenu}}}. 3. {1} Launcher packs {2} menu with {{{Append}}}. 4. {1} Launcher registers {3} menu handlers with {{{EVT_MENU}}}, on the {2} menu. 5. {1} Launcher displays {2} menu with call to {{{PopupMenu}}}, invoked on the source component, passing event's {{{GetPoint}}}. The {{{GetPoint}}} tells {{{PopupMenu}}} ''where'' to place the {{{PopupMenu}}}. (That is: Where the user right-clicked, the popup menu should appear.) == Special Concerns == In general: I don't know why {{{PopupMenu}}} isn't a global function. You ''must'' call it in the context of a wxWindow. The code below: There may be a way to communicate the list item clicked on to the menu handler via the event object, rather than the App object. == Code Sample == {{{ #!python from wxPython.wx import * menu_titles = [ "Open", "Properties", "Rename", "Delete" ] menu_title_by_id = {} for title in menu_titles: menu_title_by_id[ wxNewId() ] = title list_title = "files" list_items = [ "binding.py", "clipboard.py", "config.py", "debug.py", "dialog.py", "dispatch.py", "error.py", ] class App( wxPySimpleApp ): def OnInit( self ): # build frame frame = wxFrame(NULL, -1, "Hello from wxPython") self.frame = frame # we'll use in RightClickCb # build list list = wxListCtrl( frame, -1, style=wxLC_REPORT ) list.InsertColumn( 0, list_title ) for x in list_items: list.InsertStringItem(0,x) ### 1. Register source's EVT_s to invoke launcher. ### EVT_LIST_ITEM_RIGHT_CLICK( list, -1, self.RightClickCb ) # clear variables self.list_item_clicked = None # show & run frame.Show(1) return 1 def RightClickCb( self, event ): # record what was clicked self.list_item_clicked = right_click_context = event.GetText() ### 2. Launcher creates wxMenu. ### menu = wxMenu() for (id,title) in menu_title_by_id.items(): ### 3. Launcher packs menu with Append. ### menu.Append( id, title ) ### 4. Launcher registers menu handlers with EVT_MENU, on the menu. ### EVT_MENU( menu, id, self.MenuSelectionCb ) ### 5. Launcher displays menu with call to PopupMenu, invoked on the source component, passing event's GetPoint. ### self.frame.PopupMenu( menu, event.GetPoint() ) menu.Destroy() # destroy to avoid mem leak def MenuSelectionCb( self, event ): # do something operation = menu_title_by_id[ event.GetId() ] target = self.list_item_clicked print 'Perform "%(operation)s" on "%(target)s."' % vars() app = App() app.MainLoop() }}} === Comments === Commit code improvements above, comment freely below. -- LionKimbro <> Experiment with the icons {1} {2} {3}. Ideally, I'd get around to installing working Java on Mozilla, and then I could just diagram this process exactly like I want to... -- LionKimbro <> The above code has some problem in wx 2.5.3.1, in right click callback a call to {{{event.GetText()}}} returns empty string. Same for 2.6.0.0, so I think it's some "backward incompatibility" with wx... ;). -- Jarek Zgoda wx.Menu objects need to be explicitly destroyed (e.g. menu.Destroy()) in this situation. Otherwise, they will rack up the USER Objects count on Windows; eventually crashing a program when USER Objects is maxed out. -- U. Artie Eoff <>