【问题标题】:Scrollbar widgets won't work when laid out in a grid滚动条小部件在网格中布局时不起作用
【发布时间】:2018-08-06 14:24:04
【问题描述】:

在将 Python3 与 tkinter 库一起使用时,有好几次我需要一个可滚动的框架(具有垂直和水平滚动功能)来包含一组相关的小部件。由于滚动条不容易附加到框架上(我听说你可以将滚动条附加到画布上,但不能附加到框架上),我决定创建自己的 ScrolledFrame 类——一个使用滚动条和画布小部件的类,该小部件在其中显示一个框架。

在大多数情况下,它运行良好。我使用.pack() 方法布置了滚动条和画布。但是因为它们是 pack()ed,所以垂直小部件一直向下到屏幕底部,而不是留下空白区域。 (运行我的代码,看看我的意思。)

#!/bin/env python3
# File:  scrolledframe.py
# Written by:  J-L


import tkinter as tk


class ScrolledFrame(tk.Frame):
    def __init__(self, parent, **kwargs):
        # Create the "scaffolding" widgets:
        self.outerFrame = outerFrame = tk.Frame(parent, **kwargs)
        self.canvas = canvas = tk.Canvas(outerFrame)
        self.vscroll = vscroll = tk.Scrollbar(outerFrame, orient='vertical')
        self.hscroll = hscroll = tk.Scrollbar(outerFrame, orient='horizontal')

        # Pack/grid the vscroll, hscroll, and canvas widgets into their frame:
        usePack = True
        if usePack:  # use .pack()
            vscroll.pack(side='right', fill='y')
            hscroll.pack(side='bottom', fill='x')
            canvas.pack(fill='both', expand=True)
        else:  # use .grid()
            canvas.grid(row=0, column=0, sticky='nsew')
            vscroll.grid(row=0, column=1, sticky='ns')
            hscroll.grid(row=1, column=0, sticky='ew')

        # Hook up the commands for vscroll, hscroll, and canvas widgets:
        vscroll.configure(command=canvas.yview)
        hscroll.configure(command=canvas.xview)
        canvas.configure(yscrollcommand=vscroll.set,
                         xscrollcommand=hscroll.set)

        # Now create this very obejct (the innerFrame) as part of the canvas:
        super().__init__(canvas)
        innerFrame = self
        canvas.create_window((0, 0), window=innerFrame)
        innerFrame.bind('<Configure>',
            lambda event: canvas.configure(scrollregion=canvas.bbox('all'),
                                           width=event.width,
                                           height=event.height))


    # Accessor methods to access the four widgets involved:
    def outer_frame(self):  return self.outerFrame
    def vscroll(self):  return self.vscroll
    def hscroll(self):  return self.hscroll
    def inner_frame(self):  return self  # (included for completeness)


    # When .pack(), .grid(), or .place() is called on this object,
    # it should be invoked on the outerFrame, attaching that to its
    # parent widget:
    def pack(self, **kwargs):  self.outerFrame.pack(**kwargs)
    def grid(self, **kwargs):  self.outerFrame.grid(**kwargs)
    def place(self, **kwargs):  self.outerFrame.place(**kwargs)


def doExample():
    # Create the main window:
    root = tk.Tk()
    root.title('ScrolledFrame Example')

    # Create the scrolledFrame and a quit button:
    scrolledFrame = ScrolledFrame(root)
    scrolledFrame.pack()
    tk.Button(root, text='Quit', command=root.quit).pack(side='bottom')

    # Create some labels to display inside the scrolledFrame:
    for i in range(1, 30+1):
        tk.Label(scrolledFrame,
                 text='This is the text inside label #{}.'.format(i)
                ).grid(row=i-1, column=0)

    # Start the GUI:
    root.mainloop()


if __name__ == '__main__':
    doExample()

说实话,这不是什么大问题,但我后来考虑使用.grid() 布局方法,将每个滚动条放在自己的行和自己的列中。

在我的代码的第 18 行附近,我添加了这一行:

usePack = True

如果您将其从 True 更改为 False,滚动条和画布小部件将使用 .grid() 而不是 .pack() 进行布局,然后您将能够看到我在说什么关于。

所以当我使用.grid() 来布局滚动条时,垂直滚动条下方的空间确实如我所愿,但现在滚动条都不起作用!

这对我来说似乎很奇怪,因为我不明白为什么简单地更改小部件的布局管理会使它们的行为有所不同。

问题 1:我做错了什么导致滚动条在使用 .grid() 布局时无法工作?

另外,我注意到,对于 .pack().grid(),只要我将窗口大小调整为比开始时的短,“退出”按钮就会移出窗口。

问题 2:有没有一种方法可以强制“退出”按钮留在窗口上(当窗口正在调整大小时),但会牺牲我的 ScrolledFrame?

提前感谢您的帮助!

【问题讨论】:

    标签: python python-3.x tkinter scrollbar frame


    【解决方案1】:

    我做错了什么导致滚动条在使用 .grid() 布局时无法工作?

    问题是你没有告诉grid 怎么处理多余的空间,以及当空间不足时怎么办。正因为如此,这些小部件恰好占据了它们所需的空间量。

    使用pack,您是在告诉它使用fillexpand 选项填充所有分配的空间。使用grid,您需要为画布所在的行和列赋予非零权重。

    添加这两行,grid 的行为与 pack 的行为相同:

    outerFrame.grid_rowconfigure(0, weight=1)
    outerFrame.grid_columnconfigure(0, weight=1)
    

    您可能还想在打包scrolledFrame 时使用fillexpand 选项,假设您希望它完全填满窗口。

    scrolledFrame.pack(fill="both", expand=True)
    

    有没有一种方法可以强制“退出”按钮留在窗口上(当窗口调整大小时),但会牺牲我的 ScrolledFrame?

    在调用pack 其他小部件之前,先调用pack。当没有足够的空间容纳所有小部件并且 tkinter 只需减小一个或多个小部件的大小以使其适合时,它首先会减小添加的最后一个小部件的大小。

    【讨论】:

    • 哇,感谢您的精彩回复!您不仅回答了我的两个问题,而且还回答了我的(未发布的)问题,即如何让 scrolledFrame 完全填满窗口。再次感谢!
    猜你喜欢
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多