Search code examples
pythonwxpythonright-clickobjectlistview

Python: Right click on objectlistview not showing item name selected


I have been working with example stated under http://wiki.wxpython.org/PopupMenuOnRightClick .

The output expected when right clicking on an item would be Perform <action> on <item selected> . However the output I get is Perform <action> on "."

The code I used to test the example is:

import wx

import sys
sys.path.append("..")

from ObjectListView import ObjectListView, ColumnDefn

### 2. Launcher creates wxMenu. ###
menu_titles = [ "Open",
                "Properties",
                "Rename",
                "Delete" ]

menu_title_by_id = {}
for title in menu_titles:
    menu_title_by_id[ wx.wx.NewId() ] = title

class Track(object):
    """
    Simple minded object that represents a song in a music library
    """
    def __init__(self, title, artist, album):
        self.title = title
        self.artist = artist
        self.album = album

def GetTracks():
    """
    Return a collection of tracks
    """
    return [
        Track("Sweet Lullaby", "Deep Forest", "Deep Forest"),
        Track("Losing My Religion", "U2", "Out of Time"),
        Track("En el Pais de la Libertad", "Leon Gieco", "Leon Gieco"),
    ]

class MyFrame(wx.Frame):

    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)

        self.Init()

    def Init(self):
        self.InitModel()
        self.InitWidgets()
        self.InitObjectListView()

    def InitModel(self):
        self.songs = GetTracks()

    def InitWidgets(self):
        panel = wx.Panel(self, -1)
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(panel, 1, wx.ALL|wx.EXPAND)
        self.SetSizer(sizer_1)

        self.myOlv = ObjectListView(panel, -1, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        sizer_2.Add(self.myOlv, 1, wx.ALL|wx.EXPAND, 4)
        panel.SetSizer(sizer_2)

        self.Layout()

    def InitObjectListView(self):
        self.myOlv.SetColumns([
            ColumnDefn("Title", "left", 120, "title"),
            ColumnDefn("Artist", "left", 100, "artist"),
            ColumnDefn("Album", "left", 100, "album")
        ])
        self.myOlv.SetObjects(self.songs)

        self.myOlv.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.RightClick)

    def RightClick(self,event):
         # record what was clicked
        self.list_item_clicked = right_click_context = event.GetString()

        menu = wx.wx.Menu()
        menu.Bind(wx.EVT_MENU, self.MenuSelectionCb)

        for (id,title) in menu_title_by_id.items():
            ### 3. Launcher packs menu with Append. ###
            menu.Append( id, title )

        ### 5. Launcher displays menu with call to PopupMenu, invoked on the source component, passing event's GetPoint. ###
        #self.frame.PopupMenu( menu, event.GetPoint() )
        frame_1.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()


class MyPopupMenu(wx.Menu):

    def __init__(self, parent):
        super(MyPopupMenu, self).__init__()

        self.parent = parent

        mmi = wx.MenuItem(self, wx.NewId(), 'Minimize')
        self.AppendItem(mmi)
        self.Bind(wx.EVT_MENU, self.OnMinimize, mmi)

        cmi = wx.MenuItem(self, wx.NewId(), 'Close')
        self.AppendItem(cmi)
        self.Bind(wx.EVT_MENU, self.OnClose, cmi)


    def OnMinimize(self, e):
        self.parent.Iconize()

    def OnClose(self, e):
        self.parent.Close()


if __name__ == '__main__':
    app = wx.PySimpleApp(1)
    wx.InitAllImageHandlers()
    frame_1 = MyFrame(None, -1, "ObjectListView Track Test")
    app.SetTopWindow(frame_1)
    frame_1.Show()
    app.MainLoop()

So, how to ensure to get the corresponding item name when right clicking and selecting an action for that item?

Notes:

  1. I am using wxpython 2.7.2.0 and tested it under Python 2.5 and Python 2.7
  2. This has been asked already under python: Right Click on list menu not showing item selected . However the solution provided (to change GetText to GetString) hadn't the expected results.

Solution

  • To get the selected object inside of the RightClick event handler you can use objectlistviews GetSelectedObject() method, the code below is modified to use this method and then display the selected objects title.

    I don't think you will get the selected object from the event wx.EVT_LIST_ITEM_RIGHT_CLICK as it is not a objectlistview specific event, its a plain listview event.

    import wx
    
    import sys
    sys.path.append("..")
    
    from ObjectListView import ObjectListView, ColumnDefn
    
    ### 2. Launcher creates wxMenu. ###
    menu_titles = [ "Open",
                    "Properties",
                    "Rename",
                    "Delete" ]
    
    menu_title_by_id = {}
    for title in menu_titles:
        menu_title_by_id[ wx.NewId() ] = title
    
    class Track(object):
        """
        Simple minded object that represents a song in a music library
        """
        def __init__(self, title, artist, album):
            self.title = title
            self.artist = artist
            self.album = album
    
    def GetTracks():
        """
        Return a collection of tracks
        """
        return [
            Track("Sweet Lullaby", "Deep Forest", "Deep Forest"),
            Track("Losing My Religion", "U2", "Out of Time"),
            Track("En el Pais de la Libertad", "Leon Gieco", "Leon Gieco"),
        ]
    
    class MyFrame(wx.Frame):
    
        def __init__(self, *args, **kwds):
            wx.Frame.__init__(self, *args, **kwds)
    
            self.Init()
    
        def Init(self):
            self.InitModel()
            self.InitWidgets()
            self.InitObjectListView()
    
        def InitModel(self):
            self.songs = GetTracks()
    
        def InitWidgets(self):
            panel = wx.Panel(self, -1)
            sizer_1 = wx.BoxSizer(wx.VERTICAL)
            sizer_1.Add(panel, 1, wx.ALL | wx.EXPAND)
            self.SetSizer(sizer_1)
    
            self.myOlv = ObjectListView(panel, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
            sizer_2 = wx.BoxSizer(wx.VERTICAL)
            sizer_2.Add(self.myOlv, 1, wx.ALL | wx.EXPAND, 4)
            panel.SetSizer(sizer_2)
    
            self.Layout()
    
        def InitObjectListView(self):
            self.myOlv.SetColumns([
                ColumnDefn("Title", "left", 120, "title"),
                ColumnDefn("Artist", "left", 100, "artist"),
                ColumnDefn("Album", "left", 100, "album")
            ])
            self.myOlv.SetObjects(self.songs)
    
            self.myOlv.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.RightClick)
    
        def RightClick(self, event):
            # record what was clicked
            self.list_item_clicked = self.myOlv.GetSelectedObject()
    
            menu = wx.Menu()
            menu.Bind(wx.EVT_MENU, self.MenuSelectionCb)
    
            for (id_, title) in menu_title_by_id.items():
                ### 3. Launcher packs menu with Append. ###
                menu.Append(id_, title)
    
            ### 5. Launcher displays menu with call to PopupMenu, invoked on the source component, passing event's GetPoint. ###
            # self.frame.PopupMenu( menu, event.GetPoint() )
            frame_1.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.title
            print 'Perform "%(operation)s" on "%(target)s."' % vars()
    
    
    class MyPopupMenu(wx.Menu):
    
        def __init__(self, parent):
            super(MyPopupMenu, self).__init__()
    
            self.parent = parent
    
            mmi = wx.MenuItem(self, wx.NewId(), 'Minimize')
            self.AppendItem(mmi)
            self.Bind(wx.EVT_MENU, self.OnMinimize, mmi)
    
            cmi = wx.MenuItem(self, wx.NewId(), 'Close')
            self.AppendItem(cmi)
            self.Bind(wx.EVT_MENU, self.OnClose, cmi)
    
    
        def OnMinimize(self, e):
            self.parent.Iconize()
    
        def OnClose(self, e):
            self.parent.Close()
    
    
    if __name__ == '__main__':
        app = wx.App(True)
        wx.InitAllImageHandlers()
        frame_1 = MyFrame(None, -1, "ObjectListView Track Test")
        app.SetTopWindow(frame_1)
        frame_1.Show()
        app.MainLoop()