python - 在可滚动画布内创建可调整大小的 Tkinter 框架
问题描述
我正在使用 Python 和 Tkinter 开发一个应用程序,该应用程序需要各种输入表单。每个输入表单都需要一个滚动条,以防表单的父级太短而无法显示所有内容,因此在 Google 的帮助下,这就是我目前拥有的:
import tkinter as tk
def get_vertically_scrollable_frame(parent_frame: tk.Frame or tk.Tk) -> tk.Frame:
"""
:param parent_frame: The frame to place the canvas and scrollbar onto.
:return: A scrollable tk.Frame object nested within the parent_frame object.
"""
assert isinstance(parent_frame, tk.Frame) or isinstance(parent_frame, tk.Tk)
# Create the canvas and scrollbar.
canvas = tk.Canvas(master=parent_frame, bg="blue")
scrollbar = tk.Scrollbar(master=parent_frame, orient=tk.VERTICAL, command=canvas.yview)
# Let the canvas and scrollbar resize to fit the parent_frame object.
parent_frame.rowconfigure(0, weight=1)
parent_frame.columnconfigure(0, weight=1)
canvas.grid(row=0, column=0, sticky='news')
scrollbar.grid(row=0, column=1, sticky='nes')
# Link the canvas and scrollbar together.
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda x: canvas.configure(scrollregion=canvas.bbox("all")))
# Create the tk.Frame that is within the canvas.
canvas_frame = tk.Frame(master=canvas, bg="red")
canvas.create_window((0, 0), window=canvas_frame, anchor="nw")
# TODO: Let the canvas_frame object resize to fit the parent canvas.
canvas.columnconfigure(0, weight=1)
canvas.rowconfigure(0, weight=1)
# canvas_frame.grid(row=0, column=0, sticky="news") # Resizes the frame, but breaks the scrollbar.
return canvas_frame
if __name__ == "__main__":
window = tk.Tk()
window.rowconfigure(0, weight=1)
window.columnconfigure(0, weight=1)
parent_frame = tk.Frame(master=window)
parent_frame.grid(row=0, column=0, sticky="news")
scrollable_frame = get_vertically_scrollable_frame(parent_frame)
# Add the widgets to the new frame.
scrollable_frame.columnconfigure(0, weight=1) # Resize everything horizontally.
tk.Label(master=scrollable_frame, text="First name").grid(row=0, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=1, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=2, column=0, sticky="w")
tk.Label(master=scrollable_frame, text="Last name").grid(row=3, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=4, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=5, column=0, sticky="w")
tk.Label(master=scrollable_frame, text="Email").grid(row=6, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=7, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=8, column=0, sticky="w")
tk.Label(master=scrollable_frame, text="Favorite color").grid(row=9, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=10, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=11, column=0, sticky="w")
tk.Frame(master=scrollable_frame).grid(row=12, column=0, sticky="news")
scrollable_frame.rowconfigure(12, weight=1) # Vertically resize filler frame from last line.
tk.Button(master=scrollable_frame, text="Clear").grid(row=13, column=0, sticky="ews")
tk.Button(master=scrollable_frame, text="Submit").grid(row=14, column=0, sticky="ews")
window.mainloop()
这个函数接受一个空的 Tkinter 框架,在该框架上放置一个工作画布和滚动条,在画布中放置一个新框架,然后返回画布内的框架。
虽然滚动条在上面的代码中可以正常工作,但返回的嵌套 Tkinter 框架不会调整大小以适应其父画布的高度和宽度。如果父画布太大,它看起来像这样:
(蓝色区域是画布,红色区域是画布内的框架。)
为了解决这个问题,我使用网格手动将嵌套框架放置在画布上(请参阅 return 语句之前的注释代码)。画布内的框架开始自行调整大小,但滚动条停止工作。
有没有一种简单的方法可以让画布内的框架在不破坏滚动条的情况下自行调整大小?
解决方案
有没有一种简单的方法可以让画布内的框架在不破坏滚动条的情况下自行调整大小?
简单的?我想这取决于您对简单性的定义。这是可能的,但它需要几行额外的代码。
滚动条仅在您将框架添加到画布时才起作用,create_window
并且只有当您让框架达到容纳其所有子项所需的大小,然后相应地设置画布 bbox 时,滚动条才起作用。当窗口调整大小时,如果框架比画布小,您需要强制框架比它想要的大,但如果框架比画布大,您需要让框架成为它的首选尺寸。
解决方案看起来像下面的例子,我想不到。请注意使用标签可以轻松找到内部框架。您可以轻松地存储由返回的 idcreate_window
并改用它。这也利用了event
对象具有画布width
和属性的事实。height
def get_vertically_scrollable_frame(parent_frame: tk.Frame or tk.Tk) -> tk.Frame:
...
canvas.create_window((0, 0), window=canvas_frame, anchor="nw", tags=("canvas_frame",))
canvas.bind('<Configure>', handle_resize)
...
def handle_resize(event):
canvas = event.widget
canvas_frame = canvas.nametowidget(canvas.itemcget("canvas_frame", "window"))
min_width = canvas_frame.winfo_reqwidth()
min_height = canvas_frame.winfo_reqheight()
if min_width < event.width:
canvas.itemconfigure("canvas_frame", width=event.width)
if min_height < event.height:
canvas.itemconfigure("canvas_frame", height=event.height)
canvas.configure(scrollregion=canvas.bbox("all"))
推荐阅读
- r - 西里尔字母未正确显示在 RStudio 的 ggplot 和导出的 PDF 中
- javascript - ReactJS 无法区分元素
- python - 如何为 Django 中的产品引用 WishList 中的项目?
- python - 将张量展平回图像
- php - PHPSESSID - 设置时间到期问题(Ubuntu 中的 PHP 5.5.9)
- google-chrome - 在 karate-chrome 中更改 chrome 执行的默认路径以使用铬
- oauth-2.0 - Oauth2 与 ORY Hydra 或 ORY Kratos
- matplotlib - 如何将 `\n` 传播到 sympy.latex()
- sql - 从Oracle中按函数返回的表中选择一列
- amazon-redshift - Redshift 中的 NVL 或 COALESCE 哪个更快?