Python and multiple select with a ListCtrl
Question: I have a custom listctrl in my application that I would like the ability to
select multiple rows (and deselect) much like one would do in a ListBox.
Currently I have a listctrl that I am able to grab single selections; however,
once I click on another row in my listctrl, the previous "un-highlights". I
would like it to stay highlighted unless I click on it again -- so the user
knows which rows he/she has selected (exact same way a ListBox works). I tried
adding `wx.LC_MULTIPLE_SEL` to the listctrl line, but this did not work.

Help? Thanks!

I took this example from the following site by Mike Driscoll [Python
Blog](http://www.blog.pythonlibrary.org/2011/01/04/wxpython-wx-listctrl-tips-
and-tricks/). I adapted to it to grab row selections. Essentially I select a
row and the index is appended to a list called `InfoList`. When I select a new
row, it appends correctly, but I would like the row to stay "highlighted" in
the actual list. Then I could add another line to remove an item from the list
if I select it again in the listctrl.

    
    
    import wx
    import wx.lib.mixins.listctrl as listmix
    
    InfoList = []
    
    musicdata = {
    0 : ("Bad English", "The Price Of Love", "Rock"),
    1 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"),
    2 : ("George Michael", "Praying For Time", "Rock"),
    3 : ("Gloria Estefan", "Here We Are", "Rock"),
    4 : ("Linda Ronstadt", "Don't Know Much", "Rock"),
    5 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"),
    6 : ("Paul Young", "Oh Girl", "Rock"),
    }
    
    ########################################################################
    class TestListCtrl(wx.ListCtrl):
    
        #----------------------------------------------------------------------
        def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
                     size=wx.DefaultSize, style=0):
            wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
    
    ########################################################################
    class TestListCtrlPanel(wx.Panel, listmix.ColumnSorterMixin):
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
    
            self.list_ctrl = TestListCtrl(self, size=(-1,100),
                             style=wx.LC_REPORT
                             |wx.BORDER_SUNKEN
                             |wx.LC_SORT_ASCENDING
                             )
            self.list_ctrl.InsertColumn(0, "Artist")
            self.list_ctrl.InsertColumn(1, "Title", wx.LIST_FORMAT_RIGHT)
            self.list_ctrl.InsertColumn(2, "Genre")
    
            items = musicdata.items()
            index = 0
            for key, data in items:
                self.list_ctrl.InsertStringItem(index, data[0])
                self.list_ctrl.SetStringItem(index, 1, data[1])
                self.list_ctrl.SetStringItem(index, 2, data[2])
                self.list_ctrl.SetItemData(index, key)
                index += 1
    
            # Now that the list exists we can init the other base class,
            # see wx/lib/mixins/listctrl.py
            self.itemDataMap = musicdata
            listmix.ColumnSorterMixin.__init__(self, 3)
            self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list_ctrl)
    
            ### I ADDED THIS ###
            self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectMustHave, self.list_ctrl)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
            self.SetSizer(sizer)
    
        ### I ADDED THIS ###
        def OnSelectMustHave(self,event):        
            info = event.GetData()
            InfoList.append(info)
            print info,InfoList
    
        #----------------------------------------------------------------------
        # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
        def GetListCtrl(self):
            return self.list_ctrl
    
        #----------------------------------------------------------------------
        def OnColClick(self, event):
            print "column clicked"
            event.Skip()
    
    ########################################################################
    class MyForm(wx.Frame):
    
        #----------------------------------------------------------------------
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
    
            # Add a panel so it looks the correct on all platforms
            panel = TestListCtrlPanel(self)
    
    #----------------------------------------------------------------------
    # Run the program
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyForm()
        frame.Show()
        app.MainLoop()
    


Answer: A regular ListCtrl works:

    
    
    import wx
    
    ########################################################################
    class MyForm(wx.Frame):
    
        #----------------------------------------------------------------------
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
    
            # Add a panel so it looks the correct on all platforms
            panel = wx.Panel(self, wx.ID_ANY)
            self.index = 0
    
            self.list_ctrl = wx.ListCtrl(panel, size=(-1,100),
                             style=wx.LC_REPORT
                             |wx.BORDER_SUNKEN
                             )
            self.list_ctrl.InsertColumn(0, 'Subject')
            self.list_ctrl.InsertColumn(1, 'Due')
            self.list_ctrl.InsertColumn(2, 'Location', width=125)
    
            btn = wx.Button(panel, label="Add Line")
            btn.Bind(wx.EVT_BUTTON, self.add_line)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.list_ctrl, 1, wx.ALL|wx.EXPAND, 5)
            sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
            panel.SetSizer(sizer)
    
        #----------------------------------------------------------------------
        def add_line(self, event):
            line = "Line %s" % self.index
            self.list_ctrl.InsertStringItem(self.index, line)
            self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
            self.list_ctrl.SetStringItem(self.index, 2, "USA")
            self.index += 1
    
    #----------------------------------------------------------------------
    # Run the program
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyForm()
        frame.Show()
        app.MainLoop()
    

And if I modify this to use the two mixins you mentioned, it also works:

    
    
    import wx
    import wx.lib.mixins.listctrl as listmix
    
    ########################################################################
    class MyListCtrl(wx.ListCtrl, listmix.TextEditMixin, listmix.ColumnSorterMixin):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            """Constructor"""
            wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.BORDER_SUNKEN)
            listmix.ColumnSorterMixin.__init__(self, 3)
            listmix.TextEditMixin.__init__(self)
    
        #----------------------------------------------------------------------
        def GetListCtrl(self):
            return self
    
    ########################################################################
    class MyForm(wx.Frame):
    
        #----------------------------------------------------------------------
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
    
            # Add a panel so it looks the correct on all platforms
            panel = wx.Panel(self, wx.ID_ANY)
            self.index = 0
    
            self.list_ctrl = MyListCtrl(panel)
            self.list_ctrl.InsertColumn(0, 'Subject')
            self.list_ctrl.InsertColumn(1, 'Due')
            self.list_ctrl.InsertColumn(2, 'Location', width=125)
    
            btn = wx.Button(panel, label="Add Line")
            btn.Bind(wx.EVT_BUTTON, self.add_line)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.list_ctrl, 1, wx.ALL|wx.EXPAND, 5)
            sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
            panel.SetSizer(sizer)
    
        #----------------------------------------------------------------------
        def add_line(self, event):
            line = "Line %s" % self.index
            self.list_ctrl.InsertStringItem(self.index, line)
            self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
            self.list_ctrl.SetStringItem(self.index, 2, "USA")
            self.index += 1
    
    #----------------------------------------------------------------------
    # Run the program
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyForm()
        frame.Show()
        app.MainLoop()
    

I am using wxPython 2.6 and wxPython 2.8.12 on Windows 7. I can select
multiple items one at a time and deselect them as well.

