== wx.ScrolledWindow with wx.TextCtrl == Examples of wx.ScrolledWindow with some TextCtrl. It is necessary to have a wx.ScrolledWindow or a wx.[[ScrolledWindow|PyScrolledWindow]] be managed by a sizer to gaurantee that the scrollbars appear when they are needed. The first example shows the basic way. When the focus goes to a TextCtrl the scrollbars do not move automatically. The second example moves the scrollbars when the focus goes to the control. This moves the TextCtrl in focus to the top-left of the Frame. It alse uses a wx.FlexGridSizer to efficiently arrange the controls. The third example updates the scrollbars accordingly when the sizer contents grows and shrinks programmatically. === First example, basic === Thanks to Gary Simmons for clarifying my code. {{{#!python #!/usr/bin/python import wx class FicheFrame( wx.Frame ) : def __init__( self ) : wx.Frame.__init__( self, None,-1, "FicheFrame", size=(300, 400) ) scrollWin = wx.PyScrolledWindow( self, -1 ) x = 20 # Magic numbers !? y = 20 for i in range( 50 ) : txtStr = " Text %02d : " % (i+1) stTxt = wx.StaticText( scrollWin, -1, txtStr, pos=(x, y) ) w, h = stTxt.GetSize() txtCtrl = wx.TextCtrl( scrollWin, -1, pos=(x+w+5, y) ) dy = h + 10 # calculate for next loop y += dy #end for scrollWin.SetScrollbars( 0, dy, 0, y/dy+1 ) scrollWin.SetScrollRate( 1, 1 ) # Pixels per scroll increment #end __init__ def #end class if __name__ == '__main__' : myapp = wx.App( redirect=False ) myAppFrame = FicheFrame() myAppFrame.Show() myapp.MainLoop() }}} === Second example: managed TextCtrl focus === This example moves the scrollbars automatically when the focus goes to a TextCtrl. The comments describe when this happens. Multi-line TextCtrl's can be substituted. {{{#!python #!/usr/bin/python import wx class FicheFrameWithFocus( wx.Frame ) : def __init__( self ) : wx.Frame.__init__( self, None, -1 ,"FicheFrameWithFocus", size=(275, 500) ) self.scroll = wx.ScrolledWindow( self, -1 ) self.frmPanel = wx.Panel( self.scroll, -1 ) # A sizer allows the horizontal scrollbar to appear when needed. flGrSzr = wx.FlexGridSizer( 50, 2, 2, 2 ) # rows, cols, hgap, vgap for i in range( 50 ) : textStr = " Champ (txt) %2d : " % (i+1) flGrSzr.Add( wx.StaticText( self.frmPanel, -1, textStr ) ) txtCtrl = wx.TextCtrl( self.frmPanel, -1, size=(100, -1), style=0 ) wx.EVT_SET_FOCUS( txtCtrl, self.OnFocus ) flGrSzr.Add( txtCtrl ) #end for # Create a border around frmPnlSizer to the edges of the frame. frmPnlSizer = wx.BoxSizer( wx.VERTICAL ) frmPnlSizer.Add( flGrSzr, proportion=1, flag=wx.ALL, border=20 ) self.frmPanel.SetSizer( frmPnlSizer ) self.frmPanel.SetAutoLayout( True ) self.frmPanel.Layout() self.frmPanel.Fit() # frmPnlSizer borders will be respected self.Center() self.MakeModal( True ) # Scrolling parameters must be set AFTER all controls have been laid out. self.frmPanelWid, self.frmPanelHgt = self.frmPanel.GetSize() self.unit = 1 self.scroll.SetScrollbars( self.unit, self.unit, self.frmPanelWid/self.unit, self.frmPanelHgt/self.unit ) #end __init__ def def OnFocus( self, event ) : """ This makes the selected (the one in focus) textCtrl to be automatically repositioned to the top-left of the window. One of the scrollbars must have been moved for this to happen. The benefits for doing this are dubious. but this demonstrates how it can be done. """ parentControl = self.FindWindowById( event.GetId() ) # The parent container control parentPosX, parentPosY = parentControl.GetPosition() hx, hy = parentControl.GetSizeTuple() clientSizeX, clientSizeY = self.scroll.GetClientSize() sx, sy = self.scroll.GetViewStart() magicNumber = 20 # Where did this value come from ?! sx = sx * magicNumber sy = sy * magicNumber if (parentPosY < sy ) : self.scroll.Scroll( 0, parentPosY/self.unit ) if ( parentPosX < sx ) : self.scroll.Scroll( 0, -1 ) if (parentPosX + sx) > clientSizeX : self.scroll.Scroll( 0, -1 ) if (parentPosY + hy - sy) > clientSizeY : self.scroll.Scroll( 0, parentPosY/self.unit ) #end OnFocus def #end FicheFrameWithFocus class if __name__ == '__main__' : myapp = wx.App( redirect=False ) appFrame = FicheFrameWithFocus() appFrame.Show() myapp.MainLoop() }}} === Third example: Scrollbars for variable-sized sizer === When the contents in a ScrolledWindow changes size, the scrollbar may need to appear or disappear. This example shrinks and grows a GridBagSizer by hiding and re-showing the wx.StaticText's within. {{{#!python import wx class AScrolledWindow(wx.ScrolledWindow): def __init__(self, parent): self.parent = parent wx.ScrolledWindow.__init__(self, parent, -1, style=wx.TAB_TRAVERSAL) gb = wx.GridBagSizer(vgap=0, hgap=3) self.sizer = gb self._labels = [] self._show_but = wx.Button(self, -1, "Show") self._hide_but = wx.Button(self, -1, "Hide") gb.Add(self._show_but, (0,0), (1,1)) gb.Add(self._hide_but, (0,1), (1,1)) for y in xrange(1,30): self._labels.append(wx.StaticText(self, -1, "Label #%d" % (y,))) gb.Add(self._labels[-1], (y,1), (1,1)) self._show_but.Bind(wx.EVT_BUTTON, self.OnShow) self._hide_but.Bind(wx.EVT_BUTTON, self.OnHide) self.SetSizer(self.sizer) fontsz = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT).GetPixelSize() self.SetScrollRate(fontsz.x, fontsz.y) self.EnableScrolling(True,True) def OnShow(self, event): for lab in self._labels: self.sizer.Show(lab, True) self.OnInnerSizeChanged() def OnHide(self, event): for lab in self._labels: self.sizer.Show(lab, False) self.OnInnerSizeChanged() def OnInnerSizeChanged(self): w,h = self.sizer.GetMinSize() self.SetVirtualSize((w,h)) class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, 'Programmatic size change') sz = wx.BoxSizer(wx.VERTICAL) pa = AScrolledWindow(self) sz.Add(pa, 1, wx.EXPAND) self.SetSizer(sz) def main(): wxapp = wx.App() fr = TestFrame() fr.Show(True) wxapp.MainLoop() if __name__=='__main__': main() }}}