Python 框架的 Tkinter 滚动条
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16188420/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Tkinter scrollbar for frame
提问by Chris Aung
My objective is to add a vertical scroll bar to a frame which has several labels in it. The scroll bar should automatically enabled as soon as the labels inside the frame exceed the height of the frame. After searching through, I found thisuseful post. Based on that post I understand that in order to achieve what i want, (correct me if I am wrong, I am a beginner) I have to create a Framefirst, then create a Canvasinside that frame and stick the scroll bar to that frame as well. After that, create another frame and put it inside the canvas as a window object. So, I finally come up with this:
我的目标是向其中包含多个标签的框架添加垂直滚动条。一旦框架内的标签超过框架的高度,滚动条就会自动启用。搜索后,我发现了这篇有用的帖子。基于那篇文章,我明白为了实现我想要的,(如果我错了,请纠正我,我是初学者)我必须先创建一个Frame,然后Canvas在该框架内创建一个并将滚动条粘贴到该框架上好。之后,创建另一个框架并将其作为窗口对象放入画布中。所以,我终于想出了这个:
from Tkinter import *
def data():
for i in range(50):
Label(frame,text=i).grid(row=i,column=0)
Label(frame,text="my text"+str(i)).grid(row=i,column=1)
Label(frame,text="..........").grid(row=i,column=2)
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
root=Tk()
sizex = 800
sizey = 600
posx = 100
posy = 100
root.wm_geometry("%dx%d+%d+%d" % (sizex, sizey, posx, posy))
myframe=Frame(root,relief=GROOVE,width=50,height=100,bd=1)
myframe.place(x=10,y=10)
canvas=Canvas(myframe)
frame=Frame(canvas)
myscrollbar=Scrollbar(myframe,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="right",fill="y")
canvas.pack(side="left")
canvas.create_window((0,0),window=frame,anchor='nw')
frame.bind("<Configure>",myfunction)
data()
root.mainloop()
- Am I doing it right? Is there better/smarter way to achieve the output this code gave me?
- Why must I use grid method? (I tried place method, but none of the labels appear on the canvas.)
- What so special about using
anchor='nw'when creating window on canvas?
- 我做得对吗?有没有更好/更聪明的方法来实现这段代码给我的输出?
- 为什么我必须使用网格方法?(我尝试了 place 方法,但画布上没有出现任何标签。)
anchor='nw'在画布上创建窗口时使用有什么特别之处?
Please keep your answer simple, as I am a beginner.
请保持您的答案简单,因为我是初学者。
采纳答案by Bryan Oakley
Am i doing it right?Is there better/smarter way to achieve the output this code gave me?
我做得对吗?有没有更好/更聪明的方法来实现这段代码给我的输出?
Generally speaking, yes, you're doing it right. Tkinter has no native scrollable container other than the canvas. As you can see, it's really not that difficult to set up. As your example shows, it only takes 5 or 6 lines of code to make it work -- depending on how you count lines.
一般来说,是的,你做得对。除了画布之外,Tkinter 没有本机可滚动容器。如您所见,设置起来真的并不难。如您的示例所示,只需 5 或 6 行代码即可使其工作——这取决于您如何计算行数。
Why must i use grid method?(i tried place method, but none of the labels appear on the canvas?)
为什么我必须使用网格方法?(我尝试了放置方法,但画布上没有出现任何标签?)
You ask about why you must use grid. There is no requirement to use grid. Place, grid and pack can all be used. It's simply that some are more naturally suited to particular types of problems. In this case it looks like you're creating an actual grid -- rows and columns of labels -- so grid is the natural choice.
你问为什么你必须使用网格。没有要求使用网格。放置、网格和包装都可以使用。只是有些更自然地适合特定类型的问题。在这种情况下,看起来您正在创建一个实际的网格——标签的行和列——所以网格是自然的选择。
What so special about using anchor='nw' when creating window on canvas?
在画布上创建窗口时使用 anchor='nw' 有什么特别之处?
The anchor tells you what part of the window is positioned at the coordinates you give. By default, the center of the window will be placed at the coordinate. In the case of your code above, you want the upper left ("northwest") corner to be at the coordinate.
锚点告诉您窗口的哪个部分位于您提供的坐标处。默认情况下,窗口的中心将放置在该坐标处。对于上面的代码,您希望左上角(“西北”)位于坐标处。
回答by Gonzo
Please note that the proposed code is only valid with Python 2
请注意,建议的代码仅适用于 Python 2
Here is an example:
下面是一个例子:
from Tkinter import * # from x import * is bad practice
from ttk import *
# http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
class VerticalScrolledFrame(Frame):
"""A pure Tkinter scrollable frame that actually works!
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = 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())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
if __name__ == "__main__":
class SampleApp(Tk):
def __init__(self, *args, **kwargs):
root = Tk.__init__(self, *args, **kwargs)
self.frame = VerticalScrolledFrame(root)
self.frame.pack()
self.label = Label(text="Shrink the window to activate the scrollbar.")
self.label.pack()
buttons = []
for i in range(10):
buttons.append(Button(self.frame.interior, text="Button " + str(i)))
buttons[-1].pack()
app = SampleApp()
app.mainloop()
It does not yet have the mouse wheel bound to the scrollbar but it is possible. Scrolling with the wheel can get a bit bumpy, though.
它尚未将鼠标滚轮绑定到滚动条,但它是可能的。不过,用滚轮滚动可能会有点颠簸。
edit:
编辑:
to 1)
IMHO scrolling frames is somewhat tricky in Tkinter and does not seem to be done a lot. It seems there is no elegant way to do it.
One problem with your code is that you have to set the canvas size manually - that's what the example code I posted solves.
到 1)
恕我直言,滚动帧在 Tkinter 中有些棘手,似乎并没有做太多。似乎没有优雅的方法来做到这一点。
您的代码的一个问题是您必须手动设置画布大小 - 这就是我发布的示例代码解决的问题。
to 2)
You are talking about the data function? Place works for me, too. (In general I prefer grid).
to 2)
你说的是数据函数?Place 也适用于我。(一般来说我更喜欢网格)。
to 3)
Well, it positions the window on the canvas.
到 3)
好吧,它将窗口定位在画布上。
One thing I noticed is that your example handles mouse wheel scrolling by default while the one I posted does not. Will have to look at that some time.
我注意到的一件事是,您的示例默认处理鼠标滚轮滚动,而我发布的示例则没有。有时间一定要看看。
回答by Aditi Raghuvanshi
We can add scroll bar even without using Canvas. I have read it in many other post we can't add vertical scroll bar in frame directly etc etc. But after doing many experiment found out way to add vertical as well as horizontal scroll bar :). Please find below code which is used to create scroll bar in treeView and frame.
即使不使用 Canvas,我们也可以添加滚动条。我在许多其他帖子中读过它,我们不能直接在框架中添加垂直滚动条等等。但是在做了很多实验之后找到了添加垂直和水平滚动条的方法:)。请找到以下用于在树视图和框架中创建滚动条的代码。
f = Tkinter.Frame(self.master,width=3)
f.grid(row=2, column=0, columnspan=8, rowspan=10, pady=30, padx=30)
f.config(width=5)
self.tree = ttk.Treeview(f, selectmode="extended")
scbHDirSel =tk.Scrollbar(f, orient=Tkinter.HORIZONTAL, command=self.tree.xview)
scbVDirSel =tk.Scrollbar(f, orient=Tkinter.VERTICAL, command=self.tree.yview)
self.tree.configure(yscrollcommand=scbVDirSel.set, xscrollcommand=scbHDirSel.set)
self.tree["columns"] = (self.columnListOutput)
self.tree.column("#0", width=40)
self.tree.heading("#0", text='SrNo', anchor='w')
self.tree.grid(row=2, column=0, sticky=Tkinter.NSEW,in_=f, columnspan=10, rowspan=10)
scbVDirSel.grid(row=2, column=10, rowspan=10, sticky=Tkinter.NS, in_=f)
scbHDirSel.grid(row=14, column=0, rowspan=2, sticky=Tkinter.EW,in_=f)
f.rowconfigure(0, weight=1)
f.columnconfigure(0, weight=1)
回答by Mikhail T.
Please see my class that is a scrollable frame. It's vertical scrollbar is binded to <Mousewheel>event as well. So, all you have to do is to create a frame, fill it with widgets the way you like, and then make this frame a child of my ScrolledWindow.scrollwindow. Feel free to ask if something is unclear.
请参阅我的可滚动框架类。它的垂直滚动条也绑定到<Mousewheel>事件。所以,你所要做的就是创建一个框架,用你喜欢的方式填充小部件,然后让这个框架成为我的ScrolledWindow.scrollwindow. 如果有什么不清楚的,请随时询问。
Used a lot from @ Brayan Oakley answers to close to this questions
使用了很多@ Brayan Oakley 的回答来解决这个问题
class ScrolledWindow(tk.Frame):
"""
1. Master widget gets scrollbars and a canvas. Scrollbars are connected
to canvas scrollregion.
2. self.scrollwindow is created and inserted into canvas
Usage Guideline:
Assign any widgets as children of <ScrolledWindow instance>.scrollwindow
to get them inserted into canvas
__init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs)
docstring:
Parent = master of scrolled window
canv_w - width of canvas
canv_h - height of canvas
"""
def __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs):
"""Parent = master of scrolled window
canv_w - width of canvas
canv_h - height of canvas
"""
super().__init__(parent, *args, **kwargs)
self.parent = parent
# creating a scrollbars
self.xscrlbr = ttk.Scrollbar(self.parent, orient = 'horizontal')
self.xscrlbr.grid(column = 0, row = 1, sticky = 'ew', columnspan = 2)
self.yscrlbr = ttk.Scrollbar(self.parent)
self.yscrlbr.grid(column = 1, row = 0, sticky = 'ns')
# creating a canvas
self.canv = tk.Canvas(self.parent)
self.canv.config(relief = 'flat',
width = 10,
heigh = 10, bd = 2)
# placing a canvas into frame
self.canv.grid(column = 0, row = 0, sticky = 'nsew')
# accociating scrollbar comands to canvas scroling
self.xscrlbr.config(command = self.canv.xview)
self.yscrlbr.config(command = self.canv.yview)
# creating a frame to inserto to canvas
self.scrollwindow = ttk.Frame(self.parent)
self.canv.create_window(0, 0, window = self.scrollwindow, anchor = 'nw')
self.canv.config(xscrollcommand = self.xscrlbr.set,
yscrollcommand = self.yscrlbr.set,
scrollregion = (0, 0, 100, 100))
self.yscrlbr.lift(self.scrollwindow)
self.xscrlbr.lift(self.scrollwindow)
self.scrollwindow.bind('<Configure>', self._configure_window)
self.scrollwindow.bind('<Enter>', self._bound_to_mousewheel)
self.scrollwindow.bind('<Leave>', self._unbound_to_mousewheel)
return
def _bound_to_mousewheel(self, event):
self.canv.bind_all("<MouseWheel>", self._on_mousewheel)
def _unbound_to_mousewheel(self, event):
self.canv.unbind_all("<MouseWheel>")
def _on_mousewheel(self, event):
self.canv.yview_scroll(int(-1*(event.delta/120)), "units")
def _configure_window(self, event):
# update the scrollbars to match the size of the inner frame
size = (self.scrollwindow.winfo_reqwidth(), self.scrollwindow.winfo_reqheight())
self.canv.config(scrollregion='0 0 %s %s' % size)
if self.scrollwindow.winfo_reqwidth() != self.canv.winfo_width():
# update the canvas's width to fit the inner frame
self.canv.config(width = self.scrollwindow.winfo_reqwidth())
if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height():
# update the canvas's width to fit the inner frame
self.canv.config(height = self.scrollwindow.winfo_reqheight())

