Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tkinter: Scrollable frame, how?

2,232 views
Skip to first unread message

Stefan Dupont-Christ

unread,
Jan 16, 1998, 3:00:00 AM1/16/98
to

Hello,

I'd like to built a frame with a variable number of
widgets in it. This frame should be scrollable like
a scrollable listbox. Since class Frame does not have
methods xview() and yview() I cannot (?) assign
scrollbars to it. I tried to put the frame on a
canvas object but didn't manage it to get it right.
Does anyone know the trick?

Any help is appreciated.

--Stefan

--------------------
Stefan Dupont-Christ
s...@muwiinfa.geschichte.uni-mainz.de

Matthias Klose

unread,
Jan 16, 1998, 3:00:00 AM1/16/98
to

Stefan Dupont-Christ <s...@muwiinfa.geschichte.uni-mainz.de> writes:

> Hello,
>
> I'd like to built a frame with a variable number of
> widgets in it. This frame should be scrollable like
> a scrollable listbox. Since class Frame does not have
> methods xview() and yview() I cannot (?) assign
> scrollbars to it. I tried to put the frame on a
> canvas object but didn't manage it to get it right.
> Does anyone know the trick?

here is an example posted in comp.lang.tcl and translated to Tkinter.

#! /usr/bin/env python

import string

from Tkinter import _cnfmerge
from Tkinter import *

def main():
global firstUnit, hsb, f, ff

root = Tk()
frame = root
button = Button(frame, text="QUIT", command=frame.quit)
button.pack(side=TOP, fill=X)

# Allow the main window to be resized
root.minsize(1, 1)

# Construct the horizontal scrollbar used to scroll the listboxes
hsb = Scrollbar(frame, orient='horizontal', command=hscroll)
hsb.pack(side=BOTTOM, fill=X)

# The frame ".f" is the "clipping window".
f = Frame(frame, height=300, width=600)
f.pack(fill=BOTH, expand=1)

# The frame ".f.f" is the window that gets clipped. This is the window
# that is moved left and right by the horizontal scrollbar. The five
# listboxes are all children of this window.
ff = Frame(f)

# Construct the 5 listboxes and fill them with some arbitrary data
for i in range(5):
lb = Listbox(ff, exportselection=0)
for j in range(10 + i * 5):
lb.insert('end', 'Item %d/%d' % (i, j))
sb = Scrollbar(ff, orient='vertical', command=lb.yview)
lb.configure(yscrollcommand=sb.set)
lb.pack(side=LEFT, fill=Y)
sb.pack(side=LEFT, fill=Y)

# We have to reset the scrollbar whenever either the clipping window or
# the scrolling window are resized.
ff.bind('<Configure>', confcallback)
f.bind('<Configure>', confcallback)
#ff.bind('<Configure>', lambda e: hscroll(firstUnit))
#f.bind('<Configure>', lambda e: hscroll(firstUnit))

root.mainloop()

# The global variable "FirstUnit" stores and integer which determines where
# the left edge of .f.f is relative to the left edge of .f. When FirstUnit==0,
# the left edges correspond. When FirstUnit==1, the left edge of .f.f is
# 10 pixels to the left of the left edge of .f. And so forth.
firstUnit = 0


# This function causes the .f.f window to slide left and right within
# the clipping window .f. It also updates the position of the scrollbar.
def hscroll(x):
global firstUnit, hsb, f, ff

x = string.atoi(x)

# totalUnits is an integer proportional to the width of .f.f
totalUnits = ff.winfo_reqwidth() / 10

# windowUnits is an integer which is proportional to the amount of
# .f.f which is actual visible. If totalUnits is, say, 100, and exactly
# half of .f.f is visible within .f, then windowUnits will be 50.
windowUnits = string.atoi(f.cget('width')) / 10

# Limit the range of x so that we don't scroll too far left or right.
if x + windowUnits > totalUnits:
x = totalUnits - windowUnits
if x < 0:
x = 0
firstUnit = x

print 'scroll: first=%s, total=%s, window=%s' % (x, totalUnits, windowUnits)

# Adjust the position of the scrollbar thumb
hsb.set(totalUnits, windowUnits, x, x + windowUnits)

# Adjust the position of .f.f within the clipping window .f
ff.place(anchor='nw', relheight=1,
x=-10*x, y=0, width=ff.winfo_reqwidth())

def confcallback(e):
print 'confcallback'
hscroll('%d' % firstUnit)


if __name__ == '__main__' or sys.argv[0] == __name__:
main()

"""
# From: d...@katie.vnet.net (D. Richard Hipp)
# Newsgroups: comp.lang.tcl
# Subject: Re: scrolling a frame
# Date: 16 Apr 1996 09:59:44 -0400
# Organization: Hipp, Wyrick & Company, Inc.
#
# This example illustrates how to construct a viewport-like widget using
# a frame and the placer geometry manager. Five listboxes are constructed
# and placed in a window which is too narrow to hold them all. A horizontal
# scrollbar allows the listboxes to be moved left or right in their viewport
# window.

# Allow the main window to be resized
wm minsize . 1 1

# Construct the horizontal scrollbar used to scroll the listboxes
scrollbar .hsb -orient horizontal -command HScroll
pack .hsb -side bottom -fill x

# The frame ".f" is the "clipping window".
frame .f -height 300 -width 600
pack .f -fill both -expand 1

# The frame ".f.f" is the window that gets clipped. This is the window
# that is moved left and right by the horizontal scrollbar. The five
# listboxes are all children of this window.
frame .f.f

# Construct the 5 listboxes and fill them with some arbitrary data
set i 1
while {$i<=5} {
listbox .f.f.lb$i -yscrollcommand ".f.f.sb$i set" -exportselection 0
foreach j {0 1 2 3 4 5 6 7 8 9} {.f.f.lb$i insert end "$i$j"}
scrollbar .f.f.sb$i -orient vertical -command ".f.f.lb$i yview"
pack .f.f.lb$i .f.f.sb$i -side left -fill y
incr i
}

# We have to reset the scrollbar whenever either the clipping window or
# the scrolling window are resized.
bind .f.f <Configure> {HScroll $FirstUnit}
bind .f <Configure> {HScroll $FirstUnit}

# The global variable "FirstUnit" stores and integer which determines where
# the left edge of .f.f is relative to the left edge of .f. When FirstUnit==0,
# the left edges correspond. When FirstUnit==1, the left edge of .f.f is
# 10 pixels to the left of the left edge of .f. And so forth.
set FirstUnit 0

# This function causes the .f.f window to slide left and right within
# the clipping window .f. It also updates the position of the scrollbar.
#
proc HScroll {x} {
global FirstUnit

# totalUnits is an integer proportional to the width of .f.f
set totalUnits [expr int([winfo reqwidth .f.f]/10)]

# windowUnits is an integer which is proportional to the amount of
# .f.f which is actual visible. If totalUnits is, say, 100, and exactly
# half of .f.f is visible within .f, then windowUnits will be 50.
set windowUnits [expr int([winfo width .f]/10)]

# Limit the range of x so that we don't scroll too far left or right.
if {$x+$windowUnits>$totalUnits} {
set x [expr {$totalUnits - $windowUnits}]
}
if {$x<0} {
set x 0
}
set FirstUnit $x

# Adjust the position of the scrollbar thumb
.hsb set $totalUnits $windowUnits $x [expr $x+$windowUnits]

# Adjust the position of .f.f within the clipping window .f
place .f.f -anchor nw -relheight 1 -x [expr -10*$x] -y 0 \
-width [winfo reqwidth .f.f]
}
"""

Matthias Klose

unread,
Jan 16, 1998, 3:00:00 AM1/16/98
to

Stefan Dupont-Christ <s...@muwiinfa.geschichte.uni-mainz.de> writes:

> Hello,
>
> I'd like to built a frame with a variable number of
> widgets in it. This frame should be scrollable like
> a scrollable listbox. Since class Frame does not have
> methods xview() and yview() I cannot (?) assign
> scrollbars to it. I tried to put the frame on a
> canvas object but didn't manage it to get it right.
> Does anyone know the trick?

Below is unfinished, undocumented (but working) code derived from a
Tcl example which was posted ages ago ...

It's a simple file browser. Each column consists of a scrolled frame
with two columns (basically an extended listbox to allow different
colors and more than one column in the listbox). The Columns are put
in another scrolled frame; clicking a directory creates another column
in the outer scrolled frame.

Comments are welcome, Matthias

from Tkinter import _cnfmerge
from Tkinter import *


def whinfo(name, w):
print '%s: w: %dx%d; winfo: %dx%d; req: %dx%d' \
% (name, int(w['width']), int(w['height']),
w.winfo_width(), w.winfo_height(),
w.winfo_reqwidth(), w.winfo_reqheight())

def sbinfo(name, w):
print '%s: w: %dx--; winfo: %dx%d; req: %dx%d' \
% (name, int(w['width']),
w.winfo_width(), w.winfo_height(),
w.winfo_reqwidth(), w.winfo_reqheight())

class ScrolledFrame(Frame):
def __init__(self, master, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))
apply(Frame.__init__, (self, master), cnf)

self.__unitHeight = 20
self.__unitWidth = 20

class BrowserColumn(Frame):
"""BrowserColumn -- a scrolled single column for the browser

a frame (.f) and a scrollbar (.vbar) are created in put in the master,
but remain unmanaged.
"""
def __init__(self, master, browser, rows, unitHeight, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))

apply(Frame.__init__, (self, master), cnf)

# `self' is the clipping window

# `browser' is the containing browser
self._browser = browser

# construct the vertical scrollbar used to scroll the listbox
self._vbar = Scrollbar(master, command=self._scrollCB)

# The frame `self.ff' is the window that gets clipped. This is
# the window that is moved up and down by the vertical scrollbar.
self.ff = Frame(self)

# reset the scrollbar whenever either the clipping window or


# the scrolling window are resized.

self.bind('<Configure>', self._configureCB)
self.ff.bind('<Configure>', self._configureCB)

# The _firstUnit stores and integer which determines where
# the left edge of `self.ff' is relative to the left edge of `self'.
# When _firstUnit == 0, the left edges correspond. When _firstUnit==1,
# the upper edge of `self.ff' is 10 pixels up of the upper edge
# of `self'. And so forth.
self._firstUnit = 0
self._unitHeight = unitHeight

# the currently selected item widget
self._selected = None

# list of items in column (tuple: item, nLabel, vLabel)
self.items = []

# keep track of the width of the two columns
self.max_w1 = self.max_w2 = 30
self.maxWidth = self.max_w1 + self.max_w2

# insert reference frames for the column widths in row 0
self.w1 = Frame(self.ff, name='w1', height=0, width=self.max_w1)
self.w1.grid(row=0, column=1)
self.w2 = Frame(self.ff, name='w2', height=0, width=self.max_w2)
self.w2.grid(row=0, column=2)

self['width'] = self.maxWidth
self['height'] = self._unitHeight * rows
self.rowCount = 0

def addItem(self, item, nLabel, vLabel=None):
# name and value are Labels; if value is None, name spans the two columns
self.rowCount = self.rowCount + 1
tupel = (item, nLabel, vLabel)
if self._col == 0 and self.rowCount == 1:
self.doSelect(tupel)
nLabel._row = self.rowCount
nLabel.bind('<1>', self._clicked, add=1)
width = nLabel.winfo_reqwidth()
if width > self.max_w1:
self.max_w1 = width
self.w1['width'] = width
if vLabel == None:
nLabel.grid(row=self.rowCount, column=1, columnspan=2, sticky='WE')
else:
nLabel.grid(row=self.rowCount, column=1, sticky='WE')
vLabel._row = self.rowCount
width = vLabel.winfo_reqwidth()
if width > self.max_w2:
self.max_w2 = width
self.w2['width'] = width
vLabel.grid(row=self.rowCount, column=2, sticky='WE')
if self.max_w1 + self.max_w2 > self.maxWidth:
self.maxWidth = self.max_w1 + self.max_w2
self['width'] = self.maxWidth
self.items.append(tupel)

def populate(self, contents):
# dummy populate ... should be overwritten in subclass
# column must be empty!
self._header['text'] = 'A Header'
for i in range(20):
if i % 2 == 0:
l1 = Label(self.ff, text='Component%d'%i, anchor=W)
l2 = None
else:
l1 = Label(self.ff, text='Variable%d'%i, anchor=W)
l2 = Label(self.ff, text=float(i)*1.234, anchor=E)
self.addItem('elem', l1, l2)

def depopulate(self):
# remove all labels in column
# --> remove all children of self.ff without w1 and w2
for w in self.ff.children:
if not w in (self.w1, self.w2):
w.grid.forget() # or remove only and remember list?
w.destroy()
self.rowCount = 0
self.items = []
# restore default width
self.max_w1 = self.max_w2 = 30
self.maxWidth = self.max_w1 + self.max_w2
self.w1['width'] = self.max_w1
self.w2['width'] = self.max_w2
self['width'] = self.maxWidth

def info(self):
whinfo('self', self)
whinfo(' ff', self.ff)
sbinfo('vbar', self._vbar)

def _configureCB(self, e):
# unitHeight is the height of a unit (a label)
#self._unitHeight = self.ff.winfo_reqheight() / len(self.items)
# totalUnits is an integer proportional to the height of `self.ff'
self._totalUnits = self.ff.winfo_reqheight() / self._unitHeight
# windowUnits is an integer proportional to the height of `self'
self._windowUnits = self.winfo_height() / self._unitHeight
self._scroll(self._firstUnit)

def _scrollCB(self, pos):
#print 'scrollY:', pos, self.vbar.get()
self._scroll(int(pos))

def _scroll(self, y):


# Limit the range of x so that we don't scroll too far left or right.

if y + self._windowUnits > self._totalUnits:
y = self._totalUnits - self._windowUnits
if y < 0:
y = 0
self._firstUnit = y

# Adjust the position of the scrollbar thumb

self._vbar.set(self._totalUnits, self._windowUnits,
y, y + self._windowUnits)

# Adjust the position of .f.f within the clipping window .f

self.ff.place(anchor='nw', relwidth=1, x=0, y=-self._unitHeight*y,
height=self.ff.winfo_reqheight())

def _clicked(self, e):
self.doSelect(self.items[e.widget._row - 1])

def doSelect(self, tupel):
if self._selected:
self._selected[1]['bg'] = tupel[1]['bg']
tupel[1]['bg'] = '#c3c3c3' #Listbox's selectBackground
self._selected = tupel

def rowSelectCB(self, e):
self._browser.columnSelected(e.widget._row - 1, self._col)


class ScrolledBrowser(Frame):
""" A scrolled browser is a frame, consisting of a scrolled frame
managed by the grid and a scrollbar; the grid consists of three
rows (header, separator, browser, scrollbar). The browser row
is filled with browser columns
"""
ColumnType = BrowserColumn

def __init__(self, master=None, contents=None, minRows=8, minCols=2,
unit=None, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))
apply(Frame.__init__, (self, master), cnf)

# construct the horizontal scrollbar used to scroll the browser
self.hbar = Scrollbar(self, orient='horizontal',
command=self._scrollCB)
self.hbar.pack(side=BOTTOM, fill=X)

# self.f is the clipping window
#self.f = Frame(self, width=600, height=200)
self.f = Frame(self)
self.f.pack(fill=BOTH, expand=1)

# The frame `self.ff' is the window that gets clipped. This is
# the window that is moved left and right by the horizontal scrollbar.
self.ff = Frame(self.f)

# put the header, separator and the browser columns in the clipped
# window
self.columns=[]

self.separator = Frame(self.ff, bg='black', height=2)
self.separator.grid(row=1, column=0, columnspan=100, sticky='WE')
#self.xxx = Frame(self.ff, bg='red', width=2)
#self.xxx.grid(row=0, column=100, rowspan=3, sticky='NS')

# unit is the reference label to determine the height of a row
if unit == None:
self.unit = Label(text='Reference Label')

self.minRows = minRows;
self.minCols = minCols;

# The _firstUnit stores and integer which determines where
# the left edge of `self.ff' is relative to the left edge of `self'.
# When _firstUnit == 0, the left edges correspond. When _firstUnit==1,
# the upper edge of `self.ff' is 10 pixels up of the upper edge
# of `self'. And so forth.
self._firstUnit = 0

self.addColumn(contents)

self.bind('<KeyPress>', self.keypressCB)
self.current = 0
self.focus_set()

# the row containing the BrowserColumn should expand ...
self.ff.rowconfigure(2, weight=1)

# set the initial size
self.setMinGeometry(rows=minRows, columns=minCols)

# reset the scrollbar whenever either the clipping window or


# the scrolling window are resized.

self.ff.bind('<Configure>', self._configureCB)
self.f.bind('<Configure>', self._configureCB)


def addColumn(self, contents):
col = 2 * len(self.columns)
column = self.ColumnType(self.ff, self,
self.minRows, self.unit.winfo_reqheight())
column.grid(row=2, column=col, sticky='WENS')
column._vbar.grid(row=2, column=col+1, sticky='NS')
header = Label(self.ff, text='')
header.grid(row=0, column=col, sticky='WE')
column._header = header
column._col = len(self.columns)
self.columns.append(column)
column.populate(contents)

def delColumns(self, fromCol):
# delete columns beginning with column 'fromCol' (must be > 0)
if fromCol < 1:
raise ValueError, 'cannot delete column 0'
colNum = len(self.columns) - 1
while colNum >= fromCol:
w = self.columns[-1]
w._header.grid_forget()
w._vbar.grid_forget()
w.grid_forget()
# break up circular reference
w._browser = None
del self.columns[-1]
w._header.destroy()
w._vbar.destroy()
w.destroy()
colNum = colNum - 1

def setMinGeometry(self, columns, rows):
# sets the minimum height of visible rows in the browser
minh = self.columns[0]._unitHeight * rows
self.ff.rowconfigure(2, minsize=minh)
for col in self.columns:
col['height'] = minh

# determine the size of the clipping frame
firstCol = self.columns[0]
self.f['width'] = \
(int(firstCol['width']) + firstCol._vbar.winfo_reqwidth()) * columns
self.f['height'] = firstCol._header.winfo_reqheight() \
+ self.separator.winfo_reqheight() \
+ int(firstCol['height'])
self['width'] = self.f.winfo_reqwidth()
self['height'] = self.f.winfo_reqheight() + self.hbar.winfo_reqheight()

def columnSelected(self, row, column):
pass

def info(self):
whinfo('self', self)
whinfo(' f', self.f)
whinfo(' ff', self.ff)
sbinfo('hbar', self.hbar)
whinfo('BCol', self.columns[0])

def _configureCB(self, e):
#minHeight = 10 * self.columns[0]._unitHeight
#self.ff.rowconfigure(2, minsize=minHeight)

#self['width'] = self.winfo_reqwidth()
#self['height'] = self.winfo_reqheight()
#self.configure(width=self.winfo_reqwidth())
#self.configure(height=self.winfo_reqheight())
self._scroll(self._firstUnit)

def _scrollCB(self, pos):
self._scroll(int(pos))

def _scroll(self, x):
# totalUnits is an integer proportional to the height of `self.ff'
totalUnits = int(self.ff.winfo_reqwidth()) / 10

# windowUnits is an integer which is proportional to the amount of

# `self.ff', which is actual visible. If totalUnits is, say, 100,
# and exactly half of `self.ff' is visible within `self', then
# windowUnits will be 50.
# XXX why not reqheight?
windowUnits = int(self.f.winfo_width()) / 10

# Limit the range of x so that we don't scroll too far left or right.
if x + windowUnits > totalUnits:
x = totalUnits - windowUnits
if x < 0:
x = 0

#if totalUnits <= windowUnits:
# x = 0
self._firstUnit = x

# Adjust the position of the scrollbar thumb

self.hbar.set(totalUnits, windowUnits, x, x + windowUnits)

# Adjust the position of .f.f within the clipping window .f

self.ff.place(anchor='nw', relheight=1, x=-10*x, y=0)

def keypressCB(self, e):
key = e.keysym
col = self.columns[self.current]
row = col._selected[1]._row - 1
item = col.items[row]
# print 'key at %d, %d: %s', (self.current, row, item)
if key == 'Up':
if row > 1:
col.doSelect(col.items[row - 1])
print 'selected:', col.items[row - 1][0]
# scrolling!
elif key == 'Down':
if row < len(col.items):
col.doSelect(col.items[row + 1])
print 'selected:', col.items[row + 1][0]
# scrolling!
elif key == 'Left':
if self.current > 0:
self.delColumns(self.current)
self.current = self.current - 1
elif key in ('Right', 'Return'):
if item[2] == None: # directory
self.columnSelected(row, self.current)
self.current = self.current + 1
nextCol = self.columns[self.current]
nextCol.doSelect(nextCol.items[0])

import os, stat
class DirBrowserColumn(BrowserColumn):
def __init__(self, master, browser, rows, unitHeight, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))
apply(BrowserColumn.__init__,
(self, master, browser, rows, unitHeight), cnf)

def populate(self, contents):
# contents: name of directory
if contents == '/':
self._header['text'] = '/'
else:
self._header['text'] = os.path.split(contents)[1]
if not os.path.isdir(contents):
raise ValueError, 'not a directory: %s' % contents
self._path = contents
for item in os.listdir(contents):
if item[0] == '.': continue
buf = os.stat(os.path.join(contents, item))
if stat.S_ISDIR(buf[0]):
l1 = Label(self.ff, text=item, fg='blue', anchor=W)
l1.bind('<1>', self.rowSelectCB)
l2 = None
else:
l1 = Label(self.ff, text=item, anchor=W)
l2 = Label(self.ff, text=buf[6], fg='gray', anchor=E)

self.addItem(item, l1, l2)

class DirScrolledBrowser(ScrolledBrowser):

ColumnType = DirBrowserColumn

#def __init__(self, master=None, contents=None, minRows=8, minCols=2,
def __init__(self, master=None, cnf={}, **kw):
if kw:
cnf = _cnfmerge((cnf, kw))
apply(ScrolledBrowser.__init__, (self, master), cnf)


def columnSelected(self, row, column):
colwidget = self.columns[column]
name = colwidget.items[row][0]
# print 'dir selected: %s (col %d, row %d)' % (name, column, row)
if column < len(self.columns) - 1:
self.delColumns(column + 1)
self.addColumn(os.path.join(colwidget._path, name))

def info():
whinfo('col ', column)
whinfo('info', ib)
whinfo('quit', qb)
print

def testDirBrowser(top):
qb = Button(top, text="QUIT", fg="red", command=root.quit)
qb.pack(side=BOTTOM, fill=X)
contents = os.environ.get('HOME', '/')
sb = DirScrolledBrowser(top, contents=contents)
sb.pack(side=TOP, fill=BOTH, expand=1)
ib = Button(top, text='Info', command=sb.info)
ib.pack(side=BOTTOM, fill=X)
minwidth = sb.winfo_reqwidth()
minheight = qb.winfo_reqheight() + ib.winfo_reqheight() + sb.winfo_reqheight()
top.minsize(minwidth, minheight)

def main():
global root
root = Tk()
testDirBrowser(root)
root.mainloop()

Larry Gwinn

unread,
Jan 21, 1998, 3:00:00 AM1/21/98
to

Stefan Dupont-Christ wrote in message
<34BF4C...@muwiinfa.geschichte.uni-mainz.de>...


>Hello,
>
>I'd like to built a frame with a variable number of
>widgets in it. This frame should be scrollable like
>a scrollable listbox. Since class Frame does not have
>methods xview() and yview() I cannot (?) assign
>scrollbars to it. I tried to put the frame on a
>canvas object but didn't manage it to get it right.
>Does anyone know the trick?
>

>Any help is appreciated.
>
>--Stefan
>
>--------------------

>Stefan Dupont-Christ
>s...@muwiinfa.geschichte.uni-mainz.de

Below is an example of a Canvas with a scrollbar that can scroll Tk widgets.
I hope this helps.

#!/usr/bin/env python
from Tkinter import *
class scrollCanvas(Canvas):
def __init__(self, master=None, scrollbar='both', cnf={}, **kw):
if scrollbar not in ('both','vertical','horizontal'):
raise AttributeError, ('%s: scrollbar must be one of the' +
' following: vertical,horizontal or both')
if (cnf and kw) or type(cnf) != type({}):
raise AttributeError, ('Configuration parameters must be sent' +
' as keywords or dictionary, not both')
if cnf:
kw = cnf
masterFrame = Frame(master)
masterFrame.pack()
topFrame = Frame(masterFrame)
topFrame.pack()
Canvas.__init__(self, topFrame, kw)
self.pack(side=LEFT,fill=BOTH, expand=1)
#create scrollbars
if scrollbar == 'both' or scrollbar == 'vertical':
#setup vertical scrollbar for canvas
self.scrollVertical = Scrollbar(topFrame,orient=VERTICAL)
self.configure(yscrollcommand=self.scrollVertical.set)
self.scrollVertical.config(command=self.yview)
self.scrollVertical.pack(side=RIGHT, anchor=E, fill=Y)

if scrollbar == 'both' or scrollbar == 'horizontal':
#setup horizontal scrollbar for canvas
bottomFrame = Frame(masterFrame)
bottomFrame.pack(anchor=S,side=BOTTOM,expand=1,fill=X)
self.scrollHorizontal = Scrollbar(bottomFrame,orient=HORIZONTAL)
self.configure(xscrollcommand=self.scrollHorizontal.set)
self.scrollHorizontal.config(command=self.xview)
self.scrollHorizontal.pack(side=BOTTOM,anchor=S,fill=X,expand=1)

#vars for max x and y position of widgets inserted
self.xPos = 0
self.yPos = 0
def addWidget(self, widget):
widget.update()
self.create_window(0,self.yPos,window=widget,anchor='nw')
self.yPos = self.yPos + widget.winfo_reqheight()
if self.xPos < widget.winfo_reqwidth():
self.xPos = widget.winfo_reqwidth()
self.configure(scrollregion=(0,0,self.xPos,self.yPos))
if __name__ == '__main__':
a =Tk()
b = scrollCanvas(a, 'both', width='1i',height='3i')
f = Frame(b)
for x in range(40):
Button(f,text='Button_%s'%x).pack()
b.addWidget(f)
f1 = Frame(b)
for x in range(20):
Label(f1,text='Label_Test_TO_SEE_HOW_LONG_THE_LABEL_CAN_GO_%s'
%x).pack()
b.addWidget(f1)
a.mainloop()

Larry Gwinn

Larry Gwinn

unread,
Jan 21, 1998, 3:00:00 AM1/21/98
to

Stefan Dupont-Christ wrote in message
<34BF4C...@muwiinfa.geschichte.uni-mainz.de>...
>Hello,
>
>I'd like to built a frame with a variable number of
>widgets in it. This frame should be scrollable like
>a scrollable listbox. Since class Frame does not have
>methods xview() and yview() I cannot (?) assign
>scrollbars to it. I tried to put the frame on a
>canvas object but didn't manage it to get it right.
>Does anyone know the trick?
>
>Any help is appreciated.
>
>--Stefan
>
>--------------------
>Stefan Dupont-Christ
>s...@muwiinfa.geschichte.uni-mainz.de

Below is an example of a Canvas widget with a scrollbar that can scroll Tk


widgets. I hope this helps.

Larry Gwinn

0 new messages