在tkinter中使用grid布局时如何添加可正常工作的滚动条?
解决方案
Tkinter原生Scrollbar无法直接绑定普通Frame实现滚动,不管你用grid还是pack布局,都需要通过Canvas作为中间载体实现滚动容器,你原有grid布局的逻辑不需要改动,只需要套一层滚动容器结构即可。
核心修改点:
- 在Create页面新增「外层容器Frame + Canvas + 滚动条 + 内容Frame」的四层滚动结构,你原有输入组件的grid布局逻辑全部迁移到内部内容Frame上,原有排列规则完全保留
- 滚动条仅初始化一次,删除了每次点击新增按钮重复创建滚动条的冗余逻辑
- 绑定了内容尺寸变化自动更新滚动区域、鼠标滚轮滚动的交互逻辑
修改后可直接运行的完整代码:
from tkinter import * import tkinter as tk class Insert(tk.Tk): def __init__(self): tk.Tk.__init__(self) container = tk.Frame(self) container.grid_columnconfigure(0, weight=1) container.grid_rowconfigure(0, weight=1) container.pack(side="top", fill="both", expand=True) self.frameslist = {} for frame in (Create,): frame_occurrence = frame.__name__ active_frame = frame(parent=container, controller=self) self.frameslist[frame_occurrence] = active_frame active_frame.grid(row=0, column=0, sticky="snew") self.show_frame("Create") def show_frame(self, frame_occurrence): active_frame = self.frameslist[frame_occurrence] active_frame.tkraise() class Create(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller # -------------------------- 新增滚动容器结构开始 -------------------------- # 外层容器,放Canvas和滚动条 self.scroll_container = tk.Frame(self) self.scroll_container.pack(fill="both", expand=True) # 垂直滚动条 self.v_scroll = tk.Scrollbar(self.scroll_container, orient=VERTICAL) self.v_scroll.pack(side=RIGHT, fill=Y) # Canvas载体,用来承载内容Frame self.canvas = tk.Canvas(self.scroll_container, yscrollcommand=self.v_scroll.set) self.canvas.pack(side=LEFT, fill="both", expand=True) self.v_scroll.config(command=self.canvas.yview) # 绑定鼠标滚轮滚动事件 self.canvas.bind_all("<MouseWheel>", lambda e: self.canvas.yview_scroll(int(-1*(e.delta/120)), "units")) self.canvas.bind("<Configure>", lambda e: self.canvas.config(scrollregion=self.canvas.bbox("all"))) # 内容Frame,你原来的所有组件都放在这个Frame里,原有grid逻辑不变 self.content_frame = tk.Frame(self.canvas) self.canvas.create_window((0,0), window=self.content_frame, anchor="nw") # 内容Frame尺寸变化时更新滚动区域 def update_scrollregion(event): self.canvas.config(scrollregion=self.canvas.bbox("all")) self.content_frame.bind("<Configure>", update_scrollregion) # -------------------------- 新增滚动容器结构结束 -------------------------- #For all widgets (nested list, 2 widgets per row) self.inputlist = [] #For just the entries self.newinputlist = [] #Create two labels, add them into the inputlist to be iterated labels = [tk.Label(self.content_frame, text="Field"), tk.Label(self.content_frame, text="Values")] self.inputlist.append(labels[:]) #Insert the labels from the list for toplabels in range(1): self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5) self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5) #Create the first two entry boxes, append them to the inputlist, and newinput list first_entries = [tk.Entry(self.content_frame, borderwidth=5), tk.Text(self.content_frame, borderwidth=5, height= 5, width=20)] self.newinputlist.append(first_entries[:]) self.inputlist.append(first_entries[:]) #Insert the entries from the newinputlist for x in range(0, len(self.newinputlist) + 1): self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5) #Create two buttons (Both share same row), append them to list button_input_1 = [tk.Button(self.content_frame, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self.content_frame, text="BACK")] self.inputlist.append(button_input_1[:]) #Insert buttons at the bottom of the grid for button in range(len(self.inputlist) - 2, len(self.inputlist)): self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5) self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5) def add_insert(self): #Create two new entries, append them to the list add_input = [tk.Entry(self.content_frame, borderwidth=5), tk.Text(self.content_frame, borderwidth=5, height= 5, width=20)] self.inputlist.insert(-1, add_input) self.newinputlist.append(add_input) #Because there are new entry boxes, old grid should be forgotten for widget in self.content_frame.children.values(): widget.grid_forget() #Use the index for the row, get all widgets and place them again for index, widgets in enumerate(self.inputlist): widget_one = widgets[0] widget_two = widgets[1] widget_one.grid(row=index, column=0, padx=10, pady=5) widget_two.grid(row=index, column=1, padx=10) if __name__ == "__main__": app = Insert() app.maxsize(0, 500) app.mainloop()
内容的提问来源于stack exchange,提问作者Sloxy
相关产品推荐
相关产品推荐

