I am currently struggling using a Tkinter canvas within a scroll frame. By scroll frame I mean a Frame-Canvas structure given in the answer by Gonzo to another thread (Python Tkinter scrollbar for frame). I have adapted that concept and added the horizontal scroll bar. Now, on top of the interior frame I want to place a canvas that has a given minimum size. In the case this minimum size is smaller than the interior frame (or the entire window), there is no problem (since there is no scrolling necessary). However, if I resize the window (and hence the frame) to a value smaller than the canvas (which is the condition under which the scroll bars should be useful), the canvas is not scretched to the required size (in the example below, not to width=500px) rather it is truncated at the smaller size of the frame (width=400px). The scroll bars are active and they scroll the interior frame which is still at 500px.
Below I give you a few more details on the code and problem.
Thank you for looking into it. I appreciate any help on this problem, maybe I'm just doing something obviously wrong.
Cheers, C.
ScrollFrame class (stored in scrollframe.py):
from Tkinter import *
class ScrollFrame(Frame):
def __init__(self, parent, yscroll=True, xscroll=True, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
self.canvas = Canvas(self, bd=0, highlightthickness=0)
if xscroll:
hscrollbar = Scrollbar(self, orient=HORIZONTAL)
hscrollbar.pack(fill=X, side=BOTTOM, expand=FALSE)
self.canvas.config(xscrollcommand=hscrollbar.set)
hscrollbar.config(command=self.canvas.xview)
if yscroll:
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
self.canvas.config(yscrollcommand=vscrollbar.set)
vscrollbar.config(command=self.canvas.yview)
self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
# reset the view
self.canvas.xview_moveto(0)
self.canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(self.canvas, bg="blue")
self.interior_id = self.canvas.create_window(0, 0, window=interior, anchor=NW)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
print "interior req: ",size,(interior.winfo_width(),interior.winfo_height())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != self.canvas.winfo_width() or interior.winfo_reqheight() != sel f.canvas.winfo_height():
# update the canvas's width to fit the inner frame
self.canvas.config(width=interior.winfo_reqwidth(), height=interior.winfo_reqheight(),)
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != self.canvas.winfo_width() or interior.winfo_reqheight() != sel f.canvas.winfo_height():
# update the inner frame's width to fill the canvas
self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width(), height=self.c anvas.winfo_height())
self.canvas.bind('<Configure>', _configure_canvas)
The main code: from Tkinter import * from scrollframe import *
root = Tk()
root.geometry("400x300")
frame = Frame(root, bg="blue")
frame.pack(expand=True, fill=BOTH)
sf = ScrollFrame(frame, bg="yellow")
sf.pack(expand=True, fill=BOTH)
sf.interior.config(width=500, height=400)
canvas = Canvas(sf.interior, width=500, height=400, bg="green")
canvas.pack(expand=True, fill=BOTH, side="top")
root.mainloop()
When running, it looks like this: 1. canvas at top left corner (not yet scrolled) 2. canvas at bottom right corner (after scrolling)
The problem: the green canvas should be the requested size (500/400) but it appears to be the size of the grandmother frame where the scrollbars are attached to (400/300) rather than the interior frame, which is the one that is scrolled (500/400) and which is the mother frame of the canvas.