如何在Tkinter网格布局中滚动多行文本框内容?
问题描述
我用Python的Tkinter写脚本,通过grid布局把Excel数据显示在窗口里,每个单元格对应一个Text组件。但数据多的时候看不全,这些Text都是动态生成的,不知道怎么加滚动功能。以下是生成文本框的代码片段:
# Extracting columns from the data and creating text widget with some background color column_names = df.columns i=0 for j, col in enumerate(column_names): text = Text(window, width=20, height=1, font=('Arial', 10,'bold'), bg = "#9BC2E6") text.grid(row=i,column=j, sticky="ew") text.tag_configure("tag_name", justify='center') text.insert(INSERT, col) text.tag_add("tag_name", "1.0", "end") # adding all the other rows into the grid for i in range(n_rows): for j in range(n_cols): text = Text(window, width=len(str(df.loc[i][j])), height=1, font=('Times', 8)) text.grid(row=i+1,column=j, sticky="ew") text.tag_configure("tag_name", justify='center') text.insert(INSERT, df.loc[i][j]) text.tag_add("tag_name", "1.0", "end") cells[(i,j)] = text for i in range(n_rows): if i >= len(buttons_list): button = Button(window, text="Copiar", bg = "#08ec9c", command=lambda row=i: copy_row(cells, n_cols, row)) button.grid(row=i+1,column=n_cols) buttons_list.append(button) else: buttons_list[i].grid(row=i+1,column=n_cols) if i >= len(remove_buttons_list): remove_button = Button(window, text="Remover", bg = "#ac4c1e", command=lambda row=i: remove_row(i)) remove_button.grid(row=i+1,column=n_cols+1) remove_buttons_list.append(remove_button) else: remove_buttons_list[i].grid(row=i+1,column=n_cols + 1) all_data = [df.iloc[i].values.tolist() for i in range(n_rows)]
实现滚动功能的方案
Tkinter里给动态网格加滚动的核心思路是:把所有Text组件和按钮放到一个内部Frame里,再把这个Frame挂载到Canvas上,最后用Scrollbar控制Canvas的滚动。具体修改步骤如下:
1. 创建滚动所需的核心组件
先把原来直接放在主窗口的控件转移到专用容器里,同时创建滚动条和画布:
# 先清空主窗口原有控件(如果之前有其他控件) for widget in window.winfo_children(): widget.destroy() # 创建垂直滚动条和画布 scrollbar = Scrollbar(window, orient="vertical") canvas = Canvas(window, yscrollcommand=scrollbar.set) scrollbar.config(command=canvas.yview) # 创建承载所有控件的内部Frame inner_frame = Frame(canvas) # 监听Frame尺寸变化,自动更新画布的滚动范围 inner_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) # 把内部Frame放到画布上 canvas.create_window((0, 0), window=inner_frame, anchor="nw") # 布局滚动条和画布 scrollbar.pack(side="right", fill="y") canvas.pack(side="left", fill="both", expand=True)
2. 修改原有代码,将控件挂载到内部Frame
把原来代码里所有控件的父组件(window)替换成inner_frame,也就是修改所有Text(...)和Button(...)的第一个参数,以及对应的grid调用:
修改表头部分:
column_names = df.columns i=0 for j, col in enumerate(column_names): # 把window换成inner_frame text = Text(inner_frame, width=20, height=1, font=('Arial', 10,'bold'), bg = "#9BC2E6") text.grid(row=i,column=j, sticky="ew") text.tag_configure("tag_name", justify='center') text.insert(INSERT, col) text.tag_add("tag_name", "1.0", "end")
修改数据行部分:
for i in range(n_rows): for j in range(n_cols): # 把window换成inner_frame text = Text(inner_frame, width=len(str(df.loc[i][j])), height=1, font=('Times', 8)) text.grid(row=i+1,column=j, sticky="ew") text.tag_configure("tag_name", justify='center') text.insert(INSERT, df.loc[i][j]) text.tag_add("tag_name", "1.0", "end") cells[(i,j)] = text
修改按钮部分:
for i in range(n_rows): if i >= len(buttons_list): # 把window换成inner_frame button = Button(inner_frame, text="Copiar", bg = "#08ec9c", command=lambda row=i: copy_row(cells, n_cols, row)) button.grid(row=i+1,column=n_cols) buttons_list.append(button) else: buttons_list[i].grid(row=i+1,column=n_cols) if i >= len(remove_buttons_list): # 把window换成inner_frame remove_button = Button(inner_frame, text="Remover", bg = "#ac4c1e", command=lambda row=i: remove_row(i)) remove_button.grid(row=i+1,column=n_cols+1) remove_buttons_list.append(remove_button) else: remove_buttons_list[i].grid(row=i+1,column=n_cols + 1)
3. 可选优化:列宽自适应窗口
如果希望列能随窗口宽度自动拉伸,避免横向滚动,可以给内部Frame的列设置权重:
# 给所有列(包括按钮列)设置拉伸权重 for j in range(n_cols + 2): inner_frame.grid_columnconfigure(j, weight=1)
逻辑说明
- 所有动态生成的控件都集中在
inner_frame里,这个Frame会随着内容多少自动调整高度 - Canvas会监听
inner_frame的尺寸变化,实时更新可滚动范围 - Scrollbar和Canvas绑定,拖动滚动条就能上下查看全部内容
内容的提问来源于stack exchange,提问作者Rrios
相关产品推荐
相关产品推荐

