Behaviour problems with wx.ListCtrl.SetItemState

I think this is MSW only. It’s with wx version 2.6.3.2 (possibly there
since 2.5) and can be demonstrated by adding the following code to
ListCtrl demo - after line 233 (modified demo files are also attached)

    # unconditionally change selection from 2 to 4

    if self.currentItem == 2:

        self.log.WriteText("OnItem 2 Selected: Veto'd selection\n")

        self.list.SetItemState(2, 0, wx.LIST_STATE_SELECTED)

        wx.CallAfter(self.list.SetItemState, 4,

wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)

    # conditionally change selection from  6 to 8

    if self.currentItem == 6:

        msgbox = wx.MessageDialog(self, 'Select Item 8

instead?’,‘Message’, wx.YES_NO)

        if msgbox.ShowModal() == wx.ID_YES:

            self.log.WriteText("OnItem 6 Selected: Veto'd

selection\n")

            self.list.SetItemState(6 , 0, wx.LIST_STATE_SELECTED)

            wx.CallAfter(self.list.SetItemState, 8 ,

wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)

which results in:

OnItemSelected: 2, Billy Joel, The River Of Dreams, Rock

OnItem 2 Selected: Veto’d selection

OnItemDeselected: 2

OnItemSelected: 4, Billy Joel, Famous Last Words, Rock

OnItemDeselected: 4 <== Selected item is
immediately deselected

OnItemSelected: 6, Blue Man Group, Klein Mandelbrot, New Age

OnItem 6 Selected: Veto’d selection

OnItemDeselected: 6

OnItemSelected: 8, Blue Man Group, Drumbone, New Age

OnItemSelected: 6, Blue Man Group, Klein
Mandelbrot, New Age <== Deselected item is re-selected

OnItem 6 Selected: Veto’d selection <==
which brings up the dialog again

OnItemDeselected: 6

Adding the same code to ListCtrl_virtual (change all occurrences of
“self.list” to “self”), the unconditional change works OK but there is
the same problem after the conditional dialog.

Should these be reported back to wxWidgets (if they are reproducible)?

ListCtrl_virtual.py (4.2 KB)

ListCtrl.py (18.9 KB)

···

Regards,

David Hughes

David Hughes wrote:

I think this is MSW only. It's with wx version 2.6.3.2 (possibly there since 2.5) and can be demonstrated by adding the following code to ListCtrl demo - after line 233 (modified demo files are also attached)

which results in:

OnItemDeselected: 4 <== Selected item is immediately deselected

OnItemSelected: 6, Blue Man Group, Klein Mandelbrot, New Age <== Deselected item is re-selected
OnItem 6 Selected: Veto'd selection <== which brings up the dialog again
OnItemDeselected: 6

Adding the same code to ListCtrl_virtual (change all occurrences of "self.list" to "self"), the unconditional change works OK but there is the same problem after the conditional dialog.

I was about to write that I couldn't reproduce the problem, but they I tried selecting the items with the mouse instead of the keyboard and discovered that it is only an issue when selecting the items with the mouse. A little more play and I discovered that it only happens when you release the left mouse button. In fact if you hold the button down and take the cursor outside of the listctrl and then release it then you don't get the problem, so my guess is that the native control is doing something on the left-up event that is "completing" the selection started with the left-down (or something like that.)

I tried catching the EVT_LEFT_UP to try to work around this, but it appears to be being eaten by the native control. I also don't see anything in the wx code dealing with left up events.

I found that using a wx.FutureCall(100, ...) instead of the CallAfter works, but if the user holds the mouse button down for longer than a tenth of a second they the problem still happens. Using a timer much longer than that would start to look silly for the normal clicks. So instead I tried polling with wx.GetMouseState if it is safe to make the selection change, and if not then delay the change again. This works well.

         # unconditional change selection from 2 to 4
         if self.currentItem == 2:
             self.log.WriteText("OnItem 2 Selected: Veto'd selection\n")
             wx.FutureCall(50, self.SelectAndFocus, 2, 4)

         # conditionally change selection from 6 to 8
         if self.currentItem == 6:
             msgbox = wx.MessageDialog(self, 'Select Item 8 instead?','Message', wx.YES_NO)
             if msgbox.ShowModal() == wx.ID_YES:
                 self.log.WriteText("OnItem 6 Selected: Veto'd selection\n")
                 wx.FutureCall(50, self.SelectAndFocus, 6, 8)

         event.Skip()

     def SelectAndFocus(self, oldidx, newidx):
         if wx.GetMouseState().LeftDown():
             wx.FutureCall(50, self.SelectAndFocus, oldidx, newidx)
         else:
             self.list.Select(oldidx, False)
             self.list.Select(newidx, True)
             self.list.Focus(newidx)

Should these be reported back to wxWidgets (if they are reproducible)?

It would probably be a good idea if only to document that changing the selection from within a EVT_LIST_ITEM_SELECTED handler or immediately after the handler executes doesn't work on wxMSW. I'm not sure if anything can be done to make it work.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

David Hughes wrote:

I think this is MSW only. It's with wx version 2.6.3.2 (possibly there since 2.5) and can be demonstrated by adding the following code to ListCtrl demo - after line 233 (modified demo files are also attached)

  I was about to write that I couldn't reproduce the problem, but they I tried selecting the items with the mouse instead of the keyboard and discovered that it is only an issue when selecting the items with the mouse.

I should have explored the boundaries of the problem more thoroughly.

A little more play and I discovered that it only happens when you release the left mouse button. In fact if you hold the button down and take the cursor outside of the listctrl and then release it then you don't get the problem, so my guess is that the native control is doing something on the left-up event that is "completing" the selection started with the left-down (or something like that.)

I tried catching the EVT_LEFT_UP to try to work around this, but it appears to be being eaten by the native control. I also don't see anything in the wx code dealing with left up events.

I found that using a wx.FutureCall(100, ...) instead of the CallAfter works, but if the user holds the mouse button down for longer than a tenth of a second they the problem still happens. Using a timer much longer than that would start to look silly for the normal clicks. So instead I tried polling with wx.GetMouseState if it is safe to make the selection change, and if not then delay the change again. This works well.

        # unconditional change selection from 2 to 4
        if self.currentItem == 2:
            self.log.WriteText("OnItem 2 Selected: Veto'd selection\n")
            wx.FutureCall(50, self.SelectAndFocus, 2, 4)

        # conditionally change selection from 6 to 8
        if self.currentItem == 6:
            msgbox = wx.MessageDialog(self, 'Select Item 8 instead?','Message', wx.YES_NO)
            if msgbox.ShowModal() == wx.ID_YES:
                self.log.WriteText("OnItem 6 Selected: Veto'd selection\n")
                wx.FutureCall(50, self.SelectAndFocus, 6, 8)

        event.Skip()

    def SelectAndFocus(self, oldidx, newidx):
        if wx.GetMouseState().LeftDown():
            wx.FutureCall(50, self.SelectAndFocus, oldidx, newidx)
        else:
            self.list.Select(oldidx, False)
            self.list.Select(newidx, True)
            self.list.Focus(newidx)

A neat work around that's much better than the "flag and ignore the second event" method I've been using.

Should these be reported back to wxWidgets (if they are reproducible)?

It would probably be a good idea if only to document that changing the selection from within a EVT_LIST_ITEM_SELECTED handler or immediately after the handler executes doesn't work on wxMSW. I'm not sure if anything can be done to make it work.

I'll raise a bug report shortly and will assume you don't mind if I quote or summarise your findings and solution.

···

--
Regards,
David Hughes