将一个滚动条添加到Tkinter中的一组控件中

我正在使用Python来parsing日志文件中的条目,并使用Tkinter显示条目内容,迄今为止已经非常出色。 输出是一个标签小部件的网格,但有时会有更多的行比可以显示在屏幕上。 我想添加一个滚动条,看起来应该很容易,但我无法弄清楚。

文档暗示只有List,Textbox,Canvas和Entry小部件支持滚动条界面。 这些似乎都不适合显示小部件的网格。 可以在Canvas小部件中放置任意的小部件,但似乎必须使用绝对坐标,所以我将无法使用网格布局pipe理器?

我试图把部件网格放入一个框架,但似乎并不支持滚动界面,所以这是行不通的:

mainframe = Frame(root, yscrollcommand=scrollbar.set) 

任何人都可以提出一个方法来绕过这个限 我不希望在PyQt中重写,并增加我的可执行文件的大小,只是添加一个滚动条!

概观

您只能将滚动条与几个小部件相关联,并且根部件和Frame不是该部件组的一部分。

最常见的解决scheme是创build一个canvas小部件,并将滚动条与该小部件相关联。 然后,在该canvas中embedded包含标签小部件的框架。 确定框架的宽度/高度,并将其馈送到canvasscrollregion选项,以便滚动区域与框架的大小完全匹配。

在canvas上直接绘制文本项目不是很困难,所以如果框架embedded式canvas解决scheme太复杂,您可能需要重新考虑这种方法。 由于您正在创build一个网格,每个文本项目的坐标将非常容易计算,特别是如果每​​行都是相同的高度(这可能是如果您使用单个字体)。

直接在canvas上绘制,只需找出你正在使用的字体的行高(并有指令)。 那么,每个y坐标是row*(lineheight+spacing) 。 x坐标将是基于每一列中最宽项目的固定数字。 如果您为所有列提供了所有标签,则可以使用单个命令调整列中所有项目的x坐标和宽度。

面向对象的解决scheme

下面是一个框架embedded式canvas解决scheme的例子,使用面向对象的方法:

 import tkinter as tk class Example(tk.Frame): def __init__(self, root): tk.Frame.__init__(self, root) self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff") self.frame = tk.Frame(self.canvas, background="#ffffff") self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.vsb.set) self.vsb.pack(side="right", fill="y") self.canvas.pack(side="left", fill="both", expand=True) self.canvas.create_window((4,4), window=self.frame, anchor="nw", tags="self.frame") self.frame.bind("<Configure>", self.onFrameConfigure) self.populate() def populate(self): '''Put in some fake data''' for row in range(100): tk.Label(self.frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0) t="this is the second column for row %s" %row tk.Label(self.frame, text=t).grid(row=row, column=1) def onFrameConfigure(self, event): '''Reset the scroll region to encompass the inner frame''' self.canvas.configure(scrollregion=self.canvas.bbox("all")) if __name__ == "__main__": root=tk.Tk() Example(root).pack(side="top", fill="both", expand=True) root.mainloop() 

程序解决scheme

这是一个不使用对象的解决scheme:

 import tkinter as tk def populate(frame): '''Put in some fake data''' for row in range(100): tk.Label(frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0) t="this is the second column for row %s" %row tk.Label(frame, text=t).grid(row=row, column=1) def onFrameConfigure(canvas): '''Reset the scroll region to encompass the inner frame''' canvas.configure(scrollregion=canvas.bbox("all")) root = tk.Tk() canvas = tk.Canvas(root, borderwidth=0, background="#ffffff") frame = tk.Frame(canvas, background="#ffffff") vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview) canvas.configure(yscrollcommand=vsb.set) vsb.pack(side="right", fill="y") canvas.pack(side="left", fill="both", expand=True) canvas.create_window((4,4), window=frame, anchor="nw") frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas)) populate(frame) root.mainloop() 

注意:为了使这个工作在python 2.x中,在导入语句中使用Tkinter而不是tkinter