A ScrolledFrame, with several bugs.

   1 import Tkinter
   2 
   3 GM_KEYS = set(
   4         vars(Tkinter.Place).keys() +
   5         vars(Tkinter.Pack).keys() +
   6         vars(Tkinter.Grid).keys()
   7         )
   8 
   9 class ScrolledFrame(object):
  10     _managed = False
  11     # XXX These could be options
  12     x_incr = 5
  13     y_incr = 5
  14 
  15     def __init__(self, master=None, **kw):
  16         self.width = kw.pop('width', 200)
  17         self.height = kw.pop('height', 200)
  18 
  19         self._canvas = Tkinter.Canvas(master, **kw)
  20         self.master = self._canvas.master
  21         self._hsb = Tkinter.Scrollbar(orient='horizontal',
  22                 command=self._canvas.xview)
  23         self._vsb = Tkinter.Scrollbar(orient='vertical',
  24                 command=self._canvas.yview)
  25         self._canvas.configure(
  26                 xscrollcommand=self._hsb.set,
  27                 yscrollcommand=self._vsb.set)
  28 
  29         self._placeholder = Tkinter.Frame(self._canvas)
  30         self._canvas.create_window(0, 0, anchor='nw', window=self._placeholder)
  31 
  32         self._placeholder.bind('<Map>', self._prepare_scroll)
  33         for widget in (self._placeholder, self._canvas):
  34             widget.bind('<Button-4>', self.scroll_up)
  35             widget.bind('<Button-5>', self.scroll_down)
  36 
  37 
  38 
  39     def __getattr__(self, attr):
  40         if attr in GM_KEYS:
  41             if not self._managed:
  42                 # Position the scrollbars now.
  43                 self._managed = True
  44                 if attr == 'pack':
  45                     self._hsb.pack(side='bottom', fill='x')
  46                     self._vsb.pack(side='right', fill='y')
  47                 elif attr == 'grid':
  48                     self._hsb.grid(row=1, column=0, sticky='ew')
  49                     self._vsb.grid(row=0, column=1, sticky='ns')
  50             return getattr(self._canvas, attr)
  51 
  52         else:
  53             return getattr(self._placeholder, attr)
  54 
  55 
  56     def yscroll(self, *args):
  57         self._canvas.yview_scroll(*args)
  58 
  59 
  60     def scroll_up(self, event=None):
  61         self.yscroll(-self.y_incr, 'units')
  62 
  63 
  64     def scroll_down(self, event=None):
  65         self.yscroll(self.y_incr, 'units')
  66 
  67 
  68     def see(self, event):
  69         widget = event.widget
  70         w_height = widget.winfo_reqheight()
  71         c_height = self._canvas.winfo_height()
  72         y_pos = widget.winfo_rooty()
  73 
  74         if (y_pos - w_height) < 0:
  75             # Widget focused is above the current view
  76             while (y_pos - w_height) < self.y_incr:
  77                 self.scroll_up()
  78                 self._canvas.update_idletasks()
  79                 y_pos = widget.winfo_rooty()
  80         elif (y_pos - w_height) > c_height:
  81             # Widget focused is below the current view
  82             while (y_pos - w_height - self.y_incr) > c_height:
  83                 self.scroll_down()
  84                 self._canvas.update_idletasks()
  85                 y_pos = widget.winfo_rooty()
  86 
  87 
  88     def _prepare_scroll(self, event):
  89         frame = self._placeholder
  90         frame.unbind('<Map>')
  91 
  92         if not frame.children:
  93             # Nothing to scroll.
  94             return
  95 
  96         for child in frame.children.itervalues():
  97             child.bind('<FocusIn>', self.see)
  98 
  99         width, height = frame.winfo_reqwidth(), frame.winfo_reqheight()
 100         self._canvas.configure(scrollregion=(0, 0, width, height),
 101                 yscrollincrement=self.y_incr, xscrollincrement=self.x_incr)
 102 
 103         self._canvas.configure(width=self.width, height=self.height)
 104 

Usage example:

   1 root = Tkinter.Tk()
   2 
   3 sf = ScrolledFrame()
   4 sf.grid(row=0, column=0, sticky='nsew')
   5 sf.master.grid_columnconfigure(0, weight=1)
   6 sf.master.grid_rowconfigure(0, weight=1)
   7 
   8 for _ in range(10):
   9     lbl = Tkinter.Label(sf, text="Hi")
  10     lbl.pack()
  11     btn = Tkinter.Button(sf, text="Buh")
  12     btn.pack()
  13     entry = Tkinter.Entry(sf)
  14     entry.pack()
  15 
  16 root.mainloop()
  17 

tkinter: ScrolledFrame (last edited 2010-07-26 11:59:11 by localhost)