Canvas 在滚动框中
Canvas within scroll frame
我目前正在努力在滚动框架内使用 Tkinter canvas。我所说的滚动框架是指 Gonzo 对另一个线程 (Python Tkinter scrollbar for frame) 的回答中给出的 Frame-Canvas 结构。我已经采用了这个概念并添加了水平滚动条。现在,在内部框架的顶部,我想放置一个具有给定最小尺寸的 canvas。如果此最小尺寸小于内部框架(或整个 window),则没有问题(因为不需要滚动)。但是,如果我将 window(以及框架)的大小调整为小于 canvas 的值(这是滚动条应该有用的条件),则 canvas 是没有拉伸到所需的大小(在下面的示例中,不是宽度 = 500 像素),而是在框架的较小尺寸(宽度 = 400 像素)处被截断。滚动条处于活动状态,它们滚动仍然为 500px 的内部框架。
下面我给大家详细介绍一下代码和问题。
感谢您的关注。感谢您对此问题的任何帮助,也许我只是做了一些明显错误的事情。
干杯,
C.
ScrollFrame class(存储在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)
主要代码:
从 Tkinter 导入 *
从 scrollframe 导入 *
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()
当运行时,它看起来像这样:
1. canvas at top left corner (not yet scrolled)
2. canvas at bottom right corner (after scrolling)
问题:绿色 canvas 应该是请求的大小 (500/400) 但它似乎是滚动条附加到的祖母框架的大小 (400/300) 而不是内部框架,它是滚动的 (500/400) 并且是 canvas.
的母框架
_configure_canvas
方法是您问题的根源。它将 self.interior
框架的大小调整为 self.canvas
的宽度,并且由于 canvas
已与选项 expand=True, fill=BOTH
打包在一起,因此将其调整为 400x300。
此外,这个函数不是获得滚动框所必需的,唯一需要的是每次self.interior
调整大小时调整self.canvas
scrollregion。
修改后的代码如下:
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 frame width and update the scrollregion
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
self.canvas.config(scrollregion=self.canvas.bbox('all'))
interior.bind('<Configure>', _configure_interior)
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()
我目前正在努力在滚动框架内使用 Tkinter canvas。我所说的滚动框架是指 Gonzo 对另一个线程 (Python Tkinter scrollbar for frame) 的回答中给出的 Frame-Canvas 结构。我已经采用了这个概念并添加了水平滚动条。现在,在内部框架的顶部,我想放置一个具有给定最小尺寸的 canvas。如果此最小尺寸小于内部框架(或整个 window),则没有问题(因为不需要滚动)。但是,如果我将 window(以及框架)的大小调整为小于 canvas 的值(这是滚动条应该有用的条件),则 canvas 是没有拉伸到所需的大小(在下面的示例中,不是宽度 = 500 像素),而是在框架的较小尺寸(宽度 = 400 像素)处被截断。滚动条处于活动状态,它们滚动仍然为 500px 的内部框架。
下面我给大家详细介绍一下代码和问题。
感谢您的关注。感谢您对此问题的任何帮助,也许我只是做了一些明显错误的事情。
干杯, C.
ScrollFrame class(存储在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)
主要代码: 从 Tkinter 导入 * 从 scrollframe 导入 *
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()
当运行时,它看起来像这样: 1. canvas at top left corner (not yet scrolled) 2. canvas at bottom right corner (after scrolling)
问题:绿色 canvas 应该是请求的大小 (500/400) 但它似乎是滚动条附加到的祖母框架的大小 (400/300) 而不是内部框架,它是滚动的 (500/400) 并且是 canvas.
的母框架_configure_canvas
方法是您问题的根源。它将 self.interior
框架的大小调整为 self.canvas
的宽度,并且由于 canvas
已与选项 expand=True, fill=BOTH
打包在一起,因此将其调整为 400x300。
此外,这个函数不是获得滚动框所必需的,唯一需要的是每次self.interior
调整大小时调整self.canvas
scrollregion。
修改后的代码如下:
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 frame width and update the scrollregion
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
self.canvas.config(scrollregion=self.canvas.bbox('all'))
interior.bind('<Configure>', _configure_interior)
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()