Reputation: 59323
I'm trying to implement a panel subclass that will automatically insert vertical scrollbars if the content overflows its vertical bounds. I want the panel to work exactly like a normal panel in every respect but one: Instead of the minimum height of the panel being the minimum height of its sizer, it should have a static value (like 200), and when the height of the panel is lower than the height of its sizer, a vertical scrollbar should appear. In other words, when the height of the panel is higher than that minimum height of the sizer, it should function exactly like a normal panel.
The issue I'm having is that overriding functions like DoGetBestSize
and GetMinSize
on the panel is having no effect, as those functions are never called (I checked by inserting breakpoints). I'm guessing the panel's parent is communicating directly with the panel's sizer, which in this case is a normal box sizer set from outside the panel.
class ScrolledPanel(wx.Panel):
"""A panel that will automatically add scrollbars when needed."""
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.SetBackgroundColour('#FF0000')
# This function is never called.
def DoGetBestSize(self):
"""Override the minimum size of the panel."""
default = self.GetSizer().GetMinSize()
default.SetHeight(250)
return default
Is it possible to achieve the effect I'm after? If so, how do I circumvent the problem described above?
Here is the code in which I'm trying to use the scrolled panel.
I thought I'd try to override the SetSizer
function, take that sizer hostage and consult it regarding sizes, but not actually setting the sizer on the panel. That way the custom size functions like DoGetBestSize
would be called. It failed though. Even though no sizer has been set, those functions are still never called. I've tried both Panel
and PyPanel
. Here is my current scroll panel:
class ScrolledPanel(wx.PyPanel):
"""A panel that will automatically add scrollbars when needed."""
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.SetBackgroundColour('#FF0000')
# This function is called.
def SetSizer(self, sizer):
import pdb; pdb.set_trace()
self._pet_sizer = sizer
# The below functions are never called.
def DoGetBestSize(self):
"""Override the minimum size of the panel."""
import pdb; pdb.set_trace()
default = self.GetSizer().GetMinSize()
default.SetHeight(250)
return default
def DoGetClientSize(self):
import pdb; pdb.set_trace()
def DoGetSize(self):
import pdb; pdb.set_trace()
def DoGetVirtualSize(self):
import pdb; pdb.set_trace()
Upvotes: 1
Views: 716
Reputation: 6206
Overriding methods in wxPython currently requires using base classes that are designed to reflect some of the calls to C++ virtuals to the overridden methods in Python. That means that if you want to catch those in a panel you need to derive your class from wx.PyPanel instead of wx.Panel. See http://wiki.wxpython.org/OverridingMethods
Upvotes: 2
Reputation: 33071
Why are you reinventing the wheel? wxPython provides a ScrolledPanel widget: wx.lib.scrolledpanel
You should just use that. The wxPython demo has an example.
EDIT: Here's an example script with a scrolledPanel inside a sizer.
import wx
import wx.lib.scrolledpanel as scrolled
########################################################################
class MyScrolledPanel(scrolled.ScrolledPanel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
scrolled.ScrolledPanel.__init__(self, parent)
########################################################################
class MainPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
text = "one two buckle my shoe three four shut the door five six pick up sticks seven eight lay them straight nine ten big fat hen"
mainSizer = wx.BoxSizer(wx.VERTICAL)
sizer = wx.BoxSizer(wx.VERTICAL)
sPanel = MyScrolledPanel(self)
words = text.split()
for word in words:
hSizer = wx.BoxSizer(wx.HORIZONTAL)
label = wx.StaticText(sPanel, -1, word+":")
tc = wx.TextCtrl(sPanel, -1, word, size=(50,-1))
hSizer.Add(label, flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=10)
hSizer.Add(tc, flag=wx.LEFT, border=10)
sizer.Add(hSizer)
sPanel.SetSizer(sizer)
sPanel.SetAutoLayout(1)
sPanel.SetupScrolling()
mainSizer.Add(sPanel, 1, wx.EXPAND)
self.SetSizer(mainSizer)
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None)
panel = MainPanel(self)
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
Upvotes: 0
Reputation: 22688
You don't need to do anything special. If you call SetVirtualSize()
on any wxWindow
, including wxPanel
, it already will show scrollbars as needed and the sizers will use the virtual size to lay out this window contents.
Upvotes: 0