+class ScrolledPanel( wx.PyScrolledWindow ):
+
+ """ ScrolledPanel fills a "hole" in the implementation of
+ wx.ScrolledWindow, providing automatic scrollbar and scrolling
+ behavior and the tab traversal management that wxScrolledWindow
+ lacks. This code was based on the original demo code showing how
+ to do this, but is now available for general use as a proper class
+ (and the demo is now converted to just use it.)
+
+ It is assumed that the ScrolledPanel will have a sizer, as it is
+ used to calculate the minimal virtual size of the panel and etc.
+ """
+
+ def __init__(self, parent, id=-1, pos = wx.DefaultPosition,
+ size = wx.DefaultSize, style = wx.TAB_TRAVERSAL,
+ name = "scrolledpanel"):
+
+ wx.PyScrolledWindow.__init__(self, parent, id,
+ pos=pos, size=size,
+ style=style, name=name)
+ self.SetBestFittingSize(size)
+ self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus)
+
+
+ def SetupScrolling(self, scroll_x=True, scroll_y=True, rate_x=20, rate_y=20):
+ """
+ This function sets up the event handling necessary to handle
+ scrolling properly. It should be called within the __init__
+ function of any class that is derived from ScrolledPanel,
+ once the controls on the panel have been constructed and
+ thus the size of the scrolling area can be determined.
+
+ """
+ # The following is all that is needed to integrate the sizer and the
+ # scrolled window.
+ if not scroll_x: rate_x = 0
+ if not scroll_y: rate_y = 0
+
+ # Round up the virtual size to be a multiple of the scroll rate
+ sizer = self.GetSizer()
+ if sizer:
+ w, h = sizer.GetMinSize()
+ if rate_x:
+ w += rate_x - (w % rate_x)
+ if rate_y:
+ h += rate_y - (h % rate_y)
+ self.SetVirtualSize( (w, h) )
+ self.SetVirtualSizeHints( w, h )
+
+ self.SetScrollRate(rate_x, rate_y)
+ wx.CallAfter(self.Scroll, 0, 0) # scroll back to top after initial events
+
+
+ def OnChildFocus(self, evt):
+ # If the child window that gets the focus is not visible,
+ # this handler will try to scroll enough to see it.
+ evt.Skip()
+ child = evt.GetWindow()
+
+ sppu_x, sppu_y = self.GetScrollPixelsPerUnit()
+ vs_x, vs_y = self.GetViewStart()
+ cr = child.GetRect()
+ clntsz = self.GetClientSize()
+ new_vs_x, new_vs_y = -1, -1
+
+ # is it before the left edge?
+ if cr.x < 0 and sppu_x > 0:
+ new_vs_x = vs_x + (cr.x / sppu_x)
+
+ # is it above the top?
+ if cr.y < 0 and sppu_y > 0:
+ new_vs_y = vs_y + (cr.y / sppu_y)
+
+
+ # For the right and bottom edges, scroll enough to show the
+ # whole control if possible, but if not just scroll such that
+ # the top/left edges are still visible
+
+ # is it past the right edge ?
+ if cr.right > clntsz.width and sppu_x > 0:
+ diff = (cr.right - clntsz.width) / sppu_x
+ if cr.x - diff * sppu_x > 0:
+ new_vs_x = vs_x + diff + 1
+ else:
+ new_vs_x = vs_x + (cr.x / sppu_x)
+
+ # is it below the bottom ?
+ if cr.bottom > clntsz.height and sppu_y > 0:
+ diff = (cr.bottom - clntsz.height) / sppu_y
+ if cr.y - diff * sppu_y > 0:
+ new_vs_y = vs_y + diff + 1
+ else:
+ new_vs_y = vs_y + (cr.y / sppu_y)
+
+
+ # if we need to adjust
+ if new_vs_x != -1 or new_vs_y != -1:
+ #print "%s: (%s, %s)" % (self.GetName(), new_vs_x, new_vs_y)
+ self.Scroll(new_vs_x, new_vs_y)