如何在 tkinter 上创建动态可滚动网格

How to create a Dynamic Scrollable Grid on tkinter

当我说动态网格时,我说的是根据 window 宽度调整列数(如 bootstrap)的网格,因此它必须相应地调整“卡片” ,当我说可滚动时……好吧……更容易理解。

我试过两种方法:

  1. 创建动态网格,然后使其可滚动。
  2. 创建一个可滚动的网格,然后使其动态化。

两方面我都失败了!我发现要滚动,网格不能在简单的框架中,它必须在 Canvas 中。对于 canvas,我很难让它变得动态。

下面是我的动态网格代码

class DynaGrid(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.columns = None
        self.bind('<Configure>', self.re_grid)

    def re_grid(self, event=None):
        grid_width = self.winfo_width()
        slaves = self.grid_slaves()
        slaves_width = slaves[1].winfo_width()
        cols = grid_width // slaves_width
        if (cols == self.columns) | (cols == 0):  
            return
        for i, slave in enumerate(reversed(slaves)):
            slave.grid_forget()
            slave.grid(row=i // cols, column=i % cols)
        self.columns = cols

class CardFrame(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, bd=1, relief=tk.RAISED, **kwargs)

        tk.Label(self, text="Hello").pack()
   
def main():
    root = tk.Tk()
    frame = DynaGrid(root)
    frame.pack(fill=tk.BOTH, expand=True)

CardFrame(frame).grid() 
CardFrame(frame).grid()
CardFrame(frame).grid() 
CardFrame(frame).grid()
CardFrame(frame).grid() 
CardFrame(frame).grid()
CardFrame(frame).grid() 
CardFrame(frame).grid()
root.mainloop()

if __name__ == '__main__':
    main()

我不会浪费别人的时间在这里发布我的乱七八糟的 canvas 代码,我已经从 https://blog.tecladocode.com/tkinter-scrollable-frames/ 我已将其更改为使用我的“卡片”而不是标签。

import tkinter as tk
from tkinter import ttk


class ScrollableFrame(ttk.Frame):
    def __init__(self, container, *args, **kwargs):
        super().__init__(container, *args, **kwargs)
        canvas = tk.Canvas(self)
        scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
        self.scrollable_frame = ttk.Frame(canvas)

        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(
                scrollregion=canvas.bbox("all")
            )
        )

        canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")

        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

class TestFrame(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, bd=5, relief=tk.RAISED, **kwargs)

        tk.Label(self, text="name").pack(pady=10)
  
root = tk.Tk()
frame = ScrollableFrame(root)

for i in range(50):

    TestFrame(frame.scrollable_frame).grid()

frame.pack()
root.mainloop()

要使动态可滚动 canvas 这里棘手的部分是使用 canvas 中的 re_grid 函数。我不知道如何像在 dynaGrid 代码中那样正确地获得 window 宽度。

最后我想把这两个代码混在一起; Class 是某种带有横向滚动的动态网格的框架。

您的 canvas 是 class 中的一个框架, 所以,你需要做的是将你的重新网格函数绑定到你的主框架,并保持可滚动框架配置回调的原样。然后在你的 canvas 中改变网格,而不是你的重新网格函数中的 'dynamic grid'。就那么简单! 而且我认为这样说更容易:我想创建一个行为类似于文件资源管理器

的 window
import tkinter as tk
from tkinter import ttk


class ScrollableFrame(ttk.Frame):
    def _init_(self, container, *args, **kwargs):
        super()._init_(container, *args, **kwargs)
        canvas = tk.Canvas(self)
        scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
        self.scrollable_frame = ttk.Frame(canvas)
        self.columns=0

        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(
                scrollregion=canvas.bbox("all")
            )
        )

        self.bind('<Configure>', self.regrid)

        canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")

        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

    def regrid(self, event=None):
        print(type(self))
        grid_width = self.winfo_width()
        slaves = self.scrollable_frame.grid_slaves()
        print(len(slaves))
        slaves_width = slaves[1].winfo_width()
        cols = grid_width // slaves_width
        if (cols == self.columns) | (cols == 0):  # if the column number has not changed, abort
            return
        for i, slave in enumerate(reversed(slaves)):
            slave.grid_forget()
            slave.grid(row=i // cols, column=i % cols)
        self.columns = cols

class TestFrame(tk.Frame):
    def _init_(self, master=None, **kwargs):
        tk.Frame._init_(self, master, bd=5, relief=tk.RAISED, **kwargs)

        tk.Label(self, text="name").pack(pady=10)
        tk.Label(self, text=" info ........ info ").pack(pady=10)


root = tk.Tk()
frame = ScrollableFrame(root)

for i in range(10):
    TestFrame(frame.scrollable_frame).grid()
    TestFrame(frame.scrollable_frame).grid()

frame.pack(side="left", fill=tk.BOTH, expand=True)
root.mainloop()