开发者社区> 问答> 正文

在Tkinter中向一组小部件添加滚动条

我使用Python来解析日志文件中的条目,并使用Tkinter来显示条目内容,到目前为止,这已经很好了。输出是一个标签小部件网格,但有时行数比屏幕上显示的行数还要多。我想添加一个滚动条,看起来应该很简单,但我不知道。 文档表明,只有列表、文本框、画布和输入小部件支持滚动条接口。这些工具似乎都不适合显示小部件网格。可以在画布小部件中放置任意小部件,但似乎必须使用绝对坐标,所以我不能使用网格布局管理器? 我试过把小部件网格放到一个框架中,但似乎不支持滚动条界面,所以这不起作用:

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

有人能提出一种绕过这个限制的方法吗?我不愿意为了添加一个滚动条而用PyQt重写并增加可执行映像的大小! 问题来源StackOverflow 地址:/questions/59386414/how-to-make-the-masterwindow-widget-to-scroll-a-tkinter-question

展开
收起
kun坤 2019-12-25 21:59:48 578 0
1 条回答
写回答
取消 提交回答
  • 您只能将滚动条与几个小部件关联,而根小部件和框架不属于该小部件组。 最常见的解决方案是创建一个canvas小部件,并将滚动条与该小部件关联起来。然后,在画布中嵌入包含标签小部件的框架。确定框架的宽度/高度,并将其输入到画布滚动区域选项中,以便滚动区域与框架的大小完全匹配。 为什么要将小部件放在框架中而不是直接放在画布中呢?附加到画布上的滚动条只能滚动使用create_方法之一创建的项。不能使用包、位置或网格滚动添加到画布中的项。通过使用框架,您可以在框架内使用这些方法,然后为框架调用create_window一次。 将文本项直接绘制到画布上并不十分困难,因此,如果框架嵌入到画布中解决方案过于复杂,您可能需要重新考虑这种方法。因为您正在创建一个网格,所以每个文本项的坐标将非常容易计算,特别是如果每一行的高度相同(如果使用单一字体,则很可能是相同的高度)。 对于直接在画布上绘图,只需计算出使用的字体的行高(有相应的命令)。然后,每个y坐标为行*(lineheight+间距)。x坐标是一个固定的数字,基于每一列中最宽的项。如果为它所在的列赋予所有内容一个标记,则可以使用单个命令调整列中所有项的x坐标和宽度。 下面是一个使用面向对象方法的框架嵌入到画布解决方案的例子:

    import tkinter as tk  # python 3
    # import Tkinter as tk  # python 2
    
    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()
    

    这里有一个不使用类的解决方案:

    import tkinter as tk  # python 3
    # import Tkinter as tk  # python 2
    
    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()
    
    2019-12-25 21:59:57
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载