FAQ
Dear Pythonistas,

I've been trying to write a script that results in a set of widgets in a
scrollable widget. Since tkinter is "the" gui for Python (and about the
only one that I can be sure will not require additional software
installation), I went that route, and wrote the following:

from tkinter import *
class ShowList(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.grid()
self.draw_widgets()
def draw_widgets(self):
cframe = Frame()
cframe.grid(row=1, sticky=N+S+E+W)
canv = Canvas(cframe)
canv["scrollregion"]=canv.bbox(ALL)
canv.grid(row=0, column=0, sticky=N+S+E+W)
vscroll = Scrollbar(cframe, orient=VERTICAL, command=canv.yview)
hscroll = Scrollbar(cframe, orient=HORIZONTAL, command=canv.xview)
vscroll.grid(row=0, column=1, sticky=N+S)
hscroll.grid(row=1, column=0, sticky=E+W)
canv["xscrollcommand"] = hscroll.set
canv["yscrollcommand"] = vscroll.set
aframe = Frame(canv)
canv.create_window(0,0,window=aframe, anchor=N+W)
for i in range(0,100):
Label(aframe, text=str(i), anchor=N+W).grid(row=i, column=0)

root = Tk()
m=ShowList(root)
root.mainloop()


...which is great in that it does display the list of items (in this case, a
bunch of Labels) but not so great in the fact that the vertical scrollbar's
scroll thumb is the list of the entire scroll trough, making skip down/up a
screenful impossible. Both the horizontal scrollbar and the vertical
scrollbar will scroll until the list is off the screen (not the desired
behavior).

Initially, I thought the canv["scrollregion"] = canv.bbox(ALL) would take
care of the off-screen scrolling by making the scrollable region no larger
than it needs to be. Reading many examples of Python+tkinter code, intended
to demonstrate the way to code scrollbars and canvases, didn't enlighten me
enough to figure out the bug(s) in my code. Any suggestions as to what I
did wrong?

Many thanks in advance,

--agb

--
http://gall.mine.nu
free to get comics,free chat-1,dc++ (dcplusplus),
mute webcache,group update program,torrent,atomic time server,tool to find your ip

Search Discussions

  • Saul Spatz at Jun 22, 2011 at 3:22 am
    It works if you change it like so:

    from tkinter import *
    class ShowList(Frame):
    def __init__(self, root):
    Frame.__init__(self, root)
    self.grid()
    self.draw_widgets()
    def draw_widgets(self):
    cframe = Frame(self)
    cframe.grid(row=1, sticky=N+S+E+W)
    canv = Canvas(cframe)
    canv.grid(row=0, column=0, sticky=N+S+E+W)
    vscroll = Scrollbar(cframe, orient=VERTICAL, command=canv.yview)
    hscroll = Scrollbar(cframe, orient=HORIZONTAL, command=canv.xview)
    vscroll.grid(row=0, column=1, sticky=N+S)
    hscroll.grid(row=1, column=0, sticky=E+W)
    canv["xscrollcommand"] = hscroll.set
    canv["yscrollcommand"] = vscroll.set
    aframe = Frame(canv)
    id = canv.create_window(0,0,window=aframe, anchor=N+W)
    for i in range(0,100):
    Label(aframe, text=str(i), anchor=N+W).grid(row=i, column=0)
    aframe.update_idletasks()
    canv["scrollregion"]=canv.bbox(ALL)
    root = Tk()
    m=ShowList(root)
    root.mainloop()

    You need to do the update_idletasks to force the canvas to be mapped before you figure out the bounding box. Until the canvas is mapped to the screen, the bounding box is (0,0,1,1) so there no scrolling possible. (You can call update_ideltasks through any widget.)

    That said, I wonder why you're putting widgets in the frame instead of putting objects directly on the canvas. The way you're doing it you can't use tags, which are what really give the canvas its power.

    Saul
  • Agb at Jun 22, 2011 at 1:03 pm
    Saul Spatz wrote:

    <very helpful stuff snipped>
    You need to do the update_idletasks to force the canvas to be mapped
    before you figure out the bounding box. Until the canvas is mapped to the
    screen, the bounding box is (0,0,1,1) so there no scrolling possible.
    (You can call update_ideltasks through any widget.)
    Many thanks--this fixed the issue.
    That said, I wonder why you're putting widgets in the frame instead of
    putting objects directly on the canvas. The way you're doing it you can't
    use tags, which are what really give the canvas its power.
    The power of canvas is not really what I'm after. What I would like is a
    list of checkboxes (and, in a few other frames, several buttons); if a
    scrollable frame were available in tkinter, I'd use that. While canvas is
    powerful, its power is not needed for my purposes.
    Saul
    --
    http://gall.mine.nu
    free to get comics,free chat-1,dc++ (dcplusplus),
    mute webcache,group update program,torrent,atomic time server,tool to find your ip
  • Saul Spatz at Jun 22, 2011 at 3:30 am
    It works if you change it like so:

    from tkinter import *
    class ShowList(Frame):
    def __init__(self, root):
    Frame.__init__(self, root)
    self.grid()
    self.draw_widgets()
    def draw_widgets(self):
    cframe = Frame(self)
    cframe.grid(row=1, sticky=N+S+E+W)
    canv = Canvas(cframe)
    canv.grid(row=0, column=0, sticky=N+S+E+W)
    vscroll = Scrollbar(cframe, orient=VERTICAL, command=canv.yview)
    hscroll = Scrollbar(cframe, orient=HORIZONTAL, command=canv.xview)
    vscroll.grid(row=0, column=1, sticky=N+S)
    hscroll.grid(row=1, column=0, sticky=E+W)
    canv["xscrollcommand"] = hscroll.set
    canv["yscrollcommand"] = vscroll.set
    aframe = Frame(canv)
    id = canv.create_window(0,0,window=aframe, anchor=N+W)
    for i in range(0,100):
    Label(aframe, text=str(i), anchor=N+W).grid(row=i, column=0)
    aframe.update_idletasks()
    canv["scrollregion"]=canv.bbox(ALL)
    root = Tk()
    m=ShowList(root)
    root.mainloop()

    You have to call update_idletasks to force the canvas to be mapped to screen; until it is mapped, its bounding box is (0,0,1,1). You can call update_idletasks through any widget.

    That said, I wonder if it wouldn't be better to put canvas objects directly on the canvas, instead of putting widgets in a frame on the canvas. The way you're doing it, you can't give the widgets tags, which are what give the canvas its real power.
  • Saul Spatz at Jun 22, 2011 at 3:50 am
    This is the third time I've tried to post this reply. If you see multiple answers from me, that's why.

    Your script will work if you change it like so:

    from tkinter import *
    class ShowList(Frame):
    def __init__(self, root):
    Frame.__init__(self, root)
    self.grid()
    self.draw_widgets()
    def draw_widgets(self):
    cframe = Frame(self)
    cframe.grid(row=1, sticky=N+S+E+W)
    canv = Canvas(cframe)
    canv.grid(row=0, column=0, sticky=N+S+E+W)
    vscroll = Scrollbar(cframe, orient=VERTICAL, command=canv.yview)
    hscroll = Scrollbar(cframe, orient=HORIZONTAL, command=canv.xview)
    vscroll.grid(row=0, column=1, sticky=N+S)
    hscroll.grid(row=1, column=0, sticky=E+W)
    canv["xscrollcommand"] = hscroll.set
    canv["yscrollcommand"] = vscroll.set
    aframe = Frame(canv)
    id = canv.create_window(0,0,window=aframe, anchor=N+W)
    for i in range(0,100):
    Label(aframe, text=str(i), anchor=N+W).grid(row=i, column=0)
    aframe.update_idletasks()
    canv["scrollregion"]=canv.bbox(ALL)
    root = Tk()
    m=ShowList(root)
    root.mainloop()

    You need to call update_idletasks to force the canvas to be mapped to the screen before you compute the bounding box. Until it's mapped, the bounding box is (0,0,1,1), and of course you need to put the widgets on before you compute the bounding box. You can call update_idletasks through any widget.

    That said, I wonder if it wouldn't be better to put canvas objects directly on the canvas, instead of putting widgets in a frame. The way you'r doing it, you can't give the widgets tags, and tags are what give the canvas its power.

    Saul
  • Chris Angelico at Jun 22, 2011 at 5:30 am

    On Wed, Jun 22, 2011 at 1:50 PM, Saul Spatz wrote:
    This is the third time I've tried to post this reply. ?If you see multiple answers from me, that's why.
    All three came through on the mailing list, but out of order - this
    one came in second.

    Chris Angelico

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedJun 22, '11 at 1:10a
activeJun 22, '11 at 1:03p
posts6
users3
websitepython.org

3 users in discussion

Saul Spatz: 3 posts Agb: 2 posts Chris Angelico: 1 post

People

Translate

site design / logo © 2022 Grokbase