Search code examples
wxpython

scrollToTop not working correctly in ScrollPanel with RadioBox


I'm having a problem with a wxPython scrolled panel which contains a radiobox. The scroll bar jumps to the top when trying to select an item from the radiobox when changing focus from another panel. You then need to scroll and click again. A minimal example which reproduces the problem:

#!/bin/env python
import wx
import wx.lib.scrolledpanel as SP

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, - 1, "Frame", size=(300, 300))
        self.scrolledPanel = ScrollPanel(self, size=(-1, 200))
        self.panel = PlotTypePanel(self)
        hbox = wx.BoxSizer(wx.VERTICAL)
        hbox.Add(self.scrolledPanel, 0, wx.EXPAND | wx.ALL, 0)
        hbox.Add(self.panel, 1, wx.EXPAND | wx.ALL, 0)
        self.SetSizer(hbox)

class PlotTypePanel(wx.Panel):
    def __init__(self, parent, **kwargs):
        wx.Panel.__init__(self, parent,**kwargs)
        self.anotherradiobox =  wx.RadioBox(self,label='other',    
                                    style=wx.RA_SPECIFY_COLS,
                                    choices=["some", "other", "box"])

class ScrollPanel(SP.ScrolledPanel):
    def __init__(self, parent, **kwargs):
        SP.ScrolledPanel.__init__(self, parent, -1, **kwargs)
        self.parent = parent
        self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
        choices = [l for l in "abcdefghijklmnopqrstuv"]
        self.fieldradiobox = wx.RadioBox(self,label='letters',    
                                    style=wx.RA_SPECIFY_ROWS,
                                    choices=choices)
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.fieldradiobox, 0, wx.EXPAND|wx.ALL, 10)
        self.SetSizer(vbox)
        self.SetupScrolling(scroll_x=False, scrollToTop=False)

if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame()
    frame.Show(True)
    app.MainLoop() 

When I click on the other radio panel and back to the scrolled panel, as here,

enter image description here

it jumps to the top and doesn't select the radio button. I've checked and it seems the EVT_COMBOBOX is not triggered by this first click. I've also tried adding scrollToTop=False which didn't help. I'm using Python 2.7.3 with wxPython version 3.0.2.0.


Solution

  • OnChildFocus(self, evt)
    If the child window that gets the focus is not fully visible, this handler will try to scroll enough to see it.

    Parameters: evt – a ChildFocusEvent event to be processed.

    and apparently it works in this case, at least on Linux

    #!/bin/env python
    import wx
    import wx.lib.scrolledpanel as SP
    
    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, - 1, "Frame", size=(300, 300))
            self.scrolledPanel = ScrollPanel(self, size=(-1, 200))
            self.panel = PlotTypePanel(self)
            hbox = wx.BoxSizer(wx.VERTICAL)
            hbox.Add(self.scrolledPanel, 0, wx.EXPAND | wx.ALL, 0)
            hbox.Add(self.panel, 1, wx.EXPAND | wx.ALL, 0)
            self.SetSizer(hbox)
    
    class PlotTypePanel(wx.Panel):
        def __init__(self, parent, **kwargs):
            wx.Panel.__init__(self, parent,**kwargs)
            self.anotherradiobox =  wx.RadioBox(self,label='other',
                                        style=wx.RA_SPECIFY_COLS,
                                        choices=["some", "other", "box"])
    
    class ScrollPanel(SP.ScrolledPanel):
        def __init__(self, parent, **kwargs):
            SP.ScrolledPanel.__init__(self, parent, -1, **kwargs)
            self.parent = parent
            self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
            choices = [l for l in "abcdefghijklmnopqrstuv"]
            self.fieldradiobox = wx.RadioBox(self,label='letters',
                                        style=wx.RA_SPECIFY_ROWS,
                                        choices=choices)
            vbox = wx.BoxSizer(wx.VERTICAL)
            vbox.Add(self.fieldradiobox, 0, wx.EXPAND|wx.ALL, 10)
            self.SetSizer(vbox)
            self.Bind(wx.EVT_CHILD_FOCUS, self.on_focus)
            self.SetupScrolling(scroll_x=False, scrollToTop=False)
    
        def on_focus(self,event):
            pass
    
    if __name__ == '__main__':
        app = wx.App()
        frame = MyFrame()
        frame.Show(True)
        app.MainLoop()
    

    Note: It's not an issue but you have self.SetupScrolling declared twice.