views:

45

answers:

3

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?

A: 

You can try EVT_LIST_ITEM_RIGHT_CLICK. That should work. Otherwise you'd want to use a flag and check said flag every time the selection event fires to see if it needs to query the database or not. There's also the UltimateListCtrl, a pure python widget, that you can probably hack to do this too.

Mike Driscoll
I saw that event, but it's really the left-click that I'm trying to capture. I considered setting a flag, but I have no way of knowing which Selected event is the final one, so I have no way of acting on that flag.
Allan
A: 

You can push in a custom event handler

import wx

class MyEventHandler(wx.PyEvtHandler):
        def __init__(self,target):
            self.target = target
            wx.PyEvtHandler.__init__(self)

        def ProcessEvent(self,event):
            # there must be a better way of getting the event type,
            # but I couldn't find it
            if event.GetEventType() == wx.EVT_LEFT_DOWN.evtType[0]:
                print "Got Mouse Down event"
                (item,where) = self.target.HitTest(event.GetPosition())
                if item != -1:
                    print self.target.GetItem(item,0).GetText()
                    print where
                else:
                    print "Not on list item though"
                return True
            else:
                return False

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)
           self.myevthandler = MyEventHandler(self.list_ctrl_1)
           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.PushEventHandler(self.myevthandler)


if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame_1 = MyFrame(None, -1, "")
    app.SetTopWindow(frame_1)
    frame_1.Show()
    app.MainLoop()
iondiode
A: 

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()
Allan