如何在 tkinter 上创建动态可滚动网格
How to create a Dynamic Scrollable Grid on tkinter
当我说动态网格时,我说的是根据 window 宽度调整列数(如 bootstrap)的网格,因此它必须相应地调整“卡片” ,当我说可滚动时……好吧……更容易理解。
我试过两种方法:
- 创建动态网格,然后使其可滚动。
- 创建一个可滚动的网格,然后使其动态化。
两方面我都失败了!我发现要滚动,网格不能在简单的框架中,它必须在 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()
当我说动态网格时,我说的是根据 window 宽度调整列数(如 bootstrap)的网格,因此它必须相应地调整“卡片” ,当我说可滚动时……好吧……更容易理解。
我试过两种方法:
- 创建动态网格,然后使其可滚动。
- 创建一个可滚动的网格,然后使其动态化。
两方面我都失败了!我发现要滚动,网格不能在简单的框架中,它必须在 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'。就那么简单! 而且我认为这样说更容易:我想创建一个行为类似于文件资源管理器
的 windowimport 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()