wxPython recursion overflow

I’ve attached a pared-down version as well as the runtime log. Unfortunately the log isn’t very helpful. I am running wx-2.9.5-msw (windows 7) against ActiveState python 2.7.5. From what I’m told, the original program runs fine against wxPython 2.8.12. Neither the original nor this sample will run against 2.9.5.0. I’ve left lots of cruft in ClustersList.init, because the error will vary depending which lines are commented (on_size runs once but the Exception reoccurs infinitely, etc).

Disclaimer: this isn’t my program (you can find the original source @ http://code.google.com/p/voiceid/), and I don’t know very much about wxPython.

Sincerely,

voiceidplayer.log (2.04 KB)

voiceidplayer.py (3.99 KB)

Hi,

I've attached a pared-down version as well as the runtime log. Unfortunately the log isn't very helpful. I am running wx-2.9.5-msw (windows 7) against ActiveState python 2.7.5. From what I'm told, the original program runs fine against wxPython 2.8.12. Neither the original nor this sample will run against 2.9.5.0. I've left lots of cruft in ClustersList.__init__, because the error will vary depending which lines are commented (on_size runs once but the Exception reoccurs infinitely, etc).

Disclaimer: this isn't my program (you can find the original source @ Google Code Archive - Long-term storage for Google Code Project Hosting.), and I don't know very much about wxPython.

You have the following three lines in there:
         self.Bind(wx.EVT_SIZE, self.on_size)
         self.info.Bind(wx.EVT_SIZE, self.on_size)
         self.list.Bind(wx.EVT_SIZE, self.on_size)

If you look at self.on_size it does a Refresh/Layout of three things and I think/guess is causing your infinite loop and I think it is due to the call to Layout.

I commented the all out and the program runs and resizing the window still does what I would expect it to do. If doing that shows some issues when e.g. 'self.list' or 'self.info' is changed then just do a "Refresh" on the problem item.

Werner

···

On 12/12/2013 23:13, orbisvicis@gmail.com wrote:

Thanks, I'll explore removing *.Bind/on_size() and using the built-in
auto-layout, but I was hoping for a fix to this particular issue.

I've discovered that I can duplicate the problem with only these two lines:
        self.list.Bind(wx.EVT_SIZE, self.my_on_size)
    def my_on_size(self, event):
        self.Layout()

It seems that self.Layout() will modify self.list's layout. Which will
in turn generate an EVT_SIZE event. Normally the main loop would queue
this new event and only process the subsequent events on the queue
once the current has finished. Since self.Layout() causes a recursion
stack overflow (not simply an infinite) loop, some method of
UltimateListCtrl must be generating an EVT_SIZE event and suspending
the current event until the child event returns.

I've discovered that the following two lines can also duplicate the problem:
        self.info.Bind(wx.EVT_SIZE, self.my_on_size)
    def my_on_size(self, event):
        self.Layout()

Once again, self.Layout will modify self.lists's layout. Which will in
turn generate an EVT_SIZE on self.list, which will not propagate since
EVT_SIZE is not a command event. So how does my_on_size() get called
again? Most likely some method of UltimateListCtrl invoke's a method
of the Sizer class to which both self.info and self.list belong. This
in turn triggers an EVT_SIZE of self.info.

Which explains why the following does *not* cause the problem:
        self.Bind(wx.EVT_SIZE, self.my_on_size)
    def my_on_size(self, event):
        self.Layout()

Since self is not a member of the Sizer and EVT_SIZE event do not
propagate, self will not trigger my_on_size().

So to sum, there is clearly a bug with UltimateListCtrl against
wxPython 2.9.5, but I did not find any relevant changes in the
wxPython 2.9 Migration Guide. These are the only particular
configurations that work:

···

===========================================================
        self.Bind(wx.EVT_SIZE, self.my_on_size)
        #self.info.Bind(wx.EVT_SIZE, self.my_on_size)
        #self.list.Bind(wx.EVT_SIZE, self.my_on_size)

    def my_on_size(self, event):
        self.list.Refresh()
        self.list.Layout()
        self.info.Refresh()
        self.info.Layout()
        self.Refresh()
        self.Layout()

        self.Bind(wx.EVT_SIZE, self.my_on_size)
        self.info.Bind(wx.EVT_SIZE, self.my_on_size)
        self.list.Bind(wx.EVT_SIZE, self.my_on_size)

    def my_on_size(self, event):
        self.list.Refresh()
        self.list.Layout()
        self.info.Refresh()
        self.info.Layout()
        self.Refresh()
        #self.Layout()

sincerely,

p.s. I cc'd the maintainer mentioned in lib/agw/ultimatelistctrl.py.