向 Tkinter 中的一组小部件添加滚动条 [英] Adding a scrollbar to a group of widgets in Tkinter

查看:27
本文介绍了向 Tkinter 中的一组小部件添加滚动条的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Python 来解析日志文件中的条目,并使用 Tkinter 显示条目内容,到目前为止它非常出色.输出是一个标签小部件网格,但有时行数超出了屏幕上的显示范围.我想添加一个滚动条,看起来应该很容易,但我想不通.

I am using Python to parse entries from a log file, and display the entry contents using Tkinter and so far it's been excellent. The output is a grid of label widgets, but sometimes there are more rows than can be displayed on the screen. I'd like to add a scrollbar, which looks like it should be very easy, but I can't figure it out.

文档暗示只有列表、文本框、画布和条目小部件支持滚动条界面.这些似乎都不适合显示小部件网格.可以在 Canvas 小部件中放置任意小部件,但您似乎必须使用绝对坐标,所以我无法使用网格布局管理器?

The documentation implies that only the List, Textbox, Canvas and Entry widgets support the scrollbar interface. None of these appear to be suitable for displaying a grid of widgets. It's possible to put arbitrary widgets in a Canvas widget, but you appear to have to use absolute co-ordinates, so I wouldn't be able to use the grid layout manager?

我尝试将小部件网格放入框架中,但这似乎不支持滚动条界面,所以这不起作用:

I've tried putting the widget grid into a Frame, but that doesn't seem to support the scrollbar interface, so this doesn't work:

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

有人可以提出解决此限制的方法吗?我不想在 PyQt 中重写并将我的可执行图像的大小增加这么多,只是为了添加一个滚动条!

Can anyone suggest a way round this limitation? I'd hate to have to rewrite in PyQt and increase my executable image size by so much, just to add a scrollbar!

推荐答案

概述

您只能将滚动条与几个小部件相关联,并且根小部件和 Frame 不属于该组小部件.

至少有几种方法可以做到这一点.如果您需要一组简单的垂直或水平小部件,您可以使用文本小部件和 window_create 方法来添加小部件.这种方法很简单,但不允许小部件的复杂布局.

There are at least a couple of ways to do this. If you need a simple vertical or horizontal group of widgets, you can use a text widget and the window_create method to add widgets. This method is simple, but doesn't allow for a complex layout of the widgets.

一个更常见的通用解决方案是创建一个画布小部件并将滚动条与该小部件相关联.然后,在该画布中嵌入包含标签小部件的框架.确定框架的宽度/高度并将其输入画布 scrollregion 选项,以便滚动区域与框架的大小完全匹配.

A more common general-purpose solution is to create a canvas widget and associate the scrollbars with that widget. Then, into that canvas embed the frame that contains your label widgets. Determine the width/height of the frame and feed that into the canvas scrollregion option so that the scrollregion exactly matches the size of the frame.

为什么将小部件放在框架中而不是直接放在画布中?附加到画布的滚动条只能滚动使用 create_ 方法之一创建的项目.您不能使用 packplacegrid 滚动添加到画布的项目.通过使用框架,您可以在框架内部使用这些方法,然后为框架调用一次create_window.

Why put the widgets in a frame rather than directly in the canvas? A scrollbar attached to a canvas can only scroll items created with one of the create_ methods. You cannot scroll items added to a canvas with pack, place, or grid. By using a frame, you can use those methods inside the frame, and then call create_window once for the frame.

直接在画布上绘制文本项并不难,因此如果框架嵌入画布解决方案看起来过于复杂,您可能需要重新考虑该方法.由于您正在创建一个网格,因此每个文本项的坐标将非常容易计算,特别是如果每​​一行的高度相同(如果您使用的是单一字体,可能也是如此).

Drawing the text items directly on the canvas isn't very hard, so you might want to reconsider that approach if the frame-embedded-in-a-canvas solution seems too complex. Since you're creating a grid, the coordinates of each text item is going to be very easy to compute, especially if each row is the same height (which it probably is if you're using a single font).

要直接在画布上绘图,只需弄清楚您使用的字体的行高(并且有相应的命令).然后,每个 y 坐标为 row*(lineheight+spacing).x 坐标将是基于每列中最宽项目的固定数字.如果您为所有内容为其所在的列添加标签,则可以使用单个命令调整列中所有项目的 x 坐标和宽度.

For drawing directly on the canvas, just figure out the line height of the font you're using (and there are commands for that). Then, each y coordinate is row*(lineheight+spacing). The x coordinate will be a fixed number based on the widest item in each column. If you give everything a tag for the column it is in, you can adjust the x coordinate and width of all items in a column with a single command.

以下是使用面向对象方法的框架嵌入画布解决方案的示例:

Here's an example of the frame-embedded-in-canvas solution, using an object-oriented approach:

import tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):

        tk.Frame.__init__(self, parent)
        self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff")
        self.frame = tk.Frame(self.canvas, background="#ffffff")
        self.vsb = tk.Scrollbar(self, 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 = Example(root)
    example.pack(side="top", fill="both", expand=True)
    root.mainloop()

程序解决方案

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

Procedural solution

Here is a solution that doesn't use a class:

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()

这篇关于向 Tkinter 中的一组小部件添加滚动条的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆