Search code examples
pythonwxpythonlistctrl

Respond to Listctrl change exactly once


I'm working on a form using wxPython where I want want listctrl's list of values to change based on the selection of another listctrl. To do this, I'm using methods linked to the controlling object's EVT_LIST_ITEM_SELECTED and EVT_LIST_ITEM_DESELECTED events to call Publisher.sendMessage. The control to be changed has a method that is a subscriber to that publisher. This works: when the first listctrl is clicked, the second is refreshed.

The problem is that the data must be refreshed from the database and a message is sent for every selection and deselection. This means that even if I simply click on one item, the database gets queried twice (once for the deselection, then again for the selection). If I shift-click to multi-select 5 items, then 5 calls get made. Is there any way to have the listctrl respond to the set, rather than the individual selections?


Solution

  • The best solution seems to be to use wx.CallAfter with a flag to execute the follow-up procedure exactly once:

    import wx
    
    class MyFrame(wx.Frame):
        def __init__(self, *args, **kwds):
            wx.Frame.__init__(self, *args, **kwds)
            self.list_ctrl_1 = wx.ListCtrl(self, -1, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
            sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
            sizer_1.Add(self.list_ctrl_1, 1, wx.EXPAND, 0)
            self.list_ctrl_1.InsertColumn(0,"1")
            self.list_ctrl_1.InsertStringItem(0,"HELLO1")
            self.list_ctrl_1.InsertStringItem(0,"HELLO2")
            self.list_ctrl_1.InsertStringItem(0,"HELLO3")
            self.list_ctrl_1.InsertStringItem(0,"HELLO4")
            self.list_ctrl_1.InsertStringItem(0,"HELLO5")
            self.list_ctrl_1.InsertStringItem(0,"HELLO6")
            self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list_ctrl_1)
            self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.list_ctrl_1)
            self.dirty = False
        def Cleanup(self, StringToPrint):
            print 'No Longer Dirty!'
            self.dirty = False
    
        def OnItemSelected(self,event):
            print str(self.__class__) + " - OnItemSelected"
            if not self.dirty:
                self.dirty = True
                wx.CallAfter(self.Cleanup)
            event.Skip()
    
        def OnItemDeselected(self,event):
            print str(self.__class__) + " - OnItemDeselected"
            if not self.dirty:
                self.dirty = True
                wx.CallAfter(self.Cleanup)
            event.Skip()
    
    if __name__ == "__main__":
        app = wx.PySimpleApp(0)
        wx.InitAllImageHandlers()
        frame_1 = MyFrame(None, -1, "")
        app.SetTopWindow(frame_1)
        frame_1.Show()
        app.MainLoop()