Hubro
Hubro

Reputation: 59323

How do I override a Panel's minimum size?

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.


Update

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

Answers (3)

RobinDunn
RobinDunn

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

Mike Driscoll
Mike Driscoll

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

VZ.
VZ.

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

Related Questions