为什么带有内部框架的 canvas 上的 tkinter 滚动条在声明 pack_propagate(0) 时被禁用?
Why does the tkinter scrollbar on a canvas with inner frame get disabled on declaring pack_propagate(0)?
我的代码有一个Frame,一个Canvas inside the Frame,还有一个inner Frame inside Canvas。我想将条目框放在内部框架内,并通过 pack_propagate(0)
将它们适合内部框架,以避免使用条目小部件的宽度选项进行 pixel/font 宽度转换。但是,这会破坏内部框架的滚动功能。我想在内部框架中的第一个和最后一个 Entry 小部件的上方和下方动态添加 Entry 小部件,我目前正在使用 pack(before=)
。所以如果可能的话,我想坚持使用打包程序。
如何让滚动条重新工作?以下最小工作示例已 frame.pack_propagate(0)
被注释掉,因此条目小部件的大小未正确调整为列:
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
myframe = ttk.Frame(root)
myframe.pack()
sb = ttk.Scrollbar(myframe)
sb.pack(side=tk.RIGHT, fill=tk.Y, expand=1)
canvas = tk.Canvas(myframe, width=200, height=300,
scrollregion=(0, 0, 200, 300), yscrollcommand=sb.set)
canvas.pack()
frame = ttk.Frame(canvas, width=200, height=300) # inner Frame
canvas.create_window((0, 0), window=frame, anchor='nw')
sb.config(command=canvas.yview)
frame.bind("<Configure>", lambda event: canvas.configure(
scrollregion=canvas.bbox(tk.ALL)))
# frame.pack_propagate(0) # How to enable this and ensure scrollbar works?
s = tk.StringVar()
s.set("I'm a box")
for _ in range(100):
eb = ttk.Entry(frame, textvariable=s)
eb.pack()
root.mainloop()
Edit1:添加 StringVar()
并将 eb.grid()
更改为 eb.pack()
查看将内部 Frame
大小调整为 Canvas
的示例 - 它使用
self._canvas.bind('<Configure>', self.inner_resize)
和内部方法inner_resize()
self._canvas.itemconfig(self._window, width=event.width)
完整示例
import tkinter as tk
import tkinter.ttk as ttk
class ScrolledFrame(tk.Frame):
def __init__(self, parent, vertical=True, horizontal=False):
super().__init__(parent)
# canvas for inner frame
self._canvas = tk.Canvas(self, bg='red')
self._canvas.grid(row=0, column=0, sticky='news') # changed
# create right scrollbar and connect to canvas Y
self._vertical_bar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
if vertical:
self._vertical_bar.grid(row=0, column=1, sticky='ns')
self._canvas.configure(yscrollcommand=self._vertical_bar.set)
# create bottom scrollbar and connect to canvas X
self._horizontal_bar = tk.Scrollbar(self, orient='horizontal', command=self._canvas.xview)
if horizontal:
self._horizontal_bar.grid(row=1, column=0, sticky='we')
self._canvas.configure(xscrollcommand=self._horizontal_bar.set)
# inner frame for widgets
self.inner = tk.Frame(self._canvas)
self._window = self._canvas.create_window((0, 0), window=self.inner, anchor='nw')
# autoresize inner frame
self.columnconfigure(0, weight=1) # changed
self.rowconfigure(0, weight=1) # changed
# resize when configure changed
self.inner.bind('<Configure>', self.resize)
# resize inner frame to canvas size
self.resize_width = False
self.resize_height = False
self._canvas.bind('<Configure>', self.inner_resize)
def resize(self, event=None):
self._canvas.configure(scrollregion=self._canvas.bbox('all'))
def inner_resize(self, event):
# resize inner frame to canvas size
if self.resize_width:
self._canvas.itemconfig(self._window, width=event.width)
if self.resize_height:
self._canvas.itemconfig(self._window, height=event.height)
# --- main ----
root = tk.Tk()
sf = ScrolledFrame(root)
sf.resize_width = True # it will resize frame to canvas
sf.pack(fill='both', expand=True)
s = tk.StringVar()
s.set("I'm a box")
for _ in range(100):
eb = ttk.Entry(sf.inner, textvariable=s)
eb.pack(fill='x', expand=True)
root.mainloop()
我的代码有一个Frame,一个Canvas inside the Frame,还有一个inner Frame inside Canvas。我想将条目框放在内部框架内,并通过 pack_propagate(0)
将它们适合内部框架,以避免使用条目小部件的宽度选项进行 pixel/font 宽度转换。但是,这会破坏内部框架的滚动功能。我想在内部框架中的第一个和最后一个 Entry 小部件的上方和下方动态添加 Entry 小部件,我目前正在使用 pack(before=)
。所以如果可能的话,我想坚持使用打包程序。
如何让滚动条重新工作?以下最小工作示例已 frame.pack_propagate(0)
被注释掉,因此条目小部件的大小未正确调整为列:
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
myframe = ttk.Frame(root)
myframe.pack()
sb = ttk.Scrollbar(myframe)
sb.pack(side=tk.RIGHT, fill=tk.Y, expand=1)
canvas = tk.Canvas(myframe, width=200, height=300,
scrollregion=(0, 0, 200, 300), yscrollcommand=sb.set)
canvas.pack()
frame = ttk.Frame(canvas, width=200, height=300) # inner Frame
canvas.create_window((0, 0), window=frame, anchor='nw')
sb.config(command=canvas.yview)
frame.bind("<Configure>", lambda event: canvas.configure(
scrollregion=canvas.bbox(tk.ALL)))
# frame.pack_propagate(0) # How to enable this and ensure scrollbar works?
s = tk.StringVar()
s.set("I'm a box")
for _ in range(100):
eb = ttk.Entry(frame, textvariable=s)
eb.pack()
root.mainloop()
Edit1:添加 StringVar()
并将 eb.grid()
更改为 eb.pack()
查看将内部 Frame
大小调整为 Canvas
的示例 - 它使用
self._canvas.bind('<Configure>', self.inner_resize)
和内部方法inner_resize()
self._canvas.itemconfig(self._window, width=event.width)
完整示例
import tkinter as tk
import tkinter.ttk as ttk
class ScrolledFrame(tk.Frame):
def __init__(self, parent, vertical=True, horizontal=False):
super().__init__(parent)
# canvas for inner frame
self._canvas = tk.Canvas(self, bg='red')
self._canvas.grid(row=0, column=0, sticky='news') # changed
# create right scrollbar and connect to canvas Y
self._vertical_bar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
if vertical:
self._vertical_bar.grid(row=0, column=1, sticky='ns')
self._canvas.configure(yscrollcommand=self._vertical_bar.set)
# create bottom scrollbar and connect to canvas X
self._horizontal_bar = tk.Scrollbar(self, orient='horizontal', command=self._canvas.xview)
if horizontal:
self._horizontal_bar.grid(row=1, column=0, sticky='we')
self._canvas.configure(xscrollcommand=self._horizontal_bar.set)
# inner frame for widgets
self.inner = tk.Frame(self._canvas)
self._window = self._canvas.create_window((0, 0), window=self.inner, anchor='nw')
# autoresize inner frame
self.columnconfigure(0, weight=1) # changed
self.rowconfigure(0, weight=1) # changed
# resize when configure changed
self.inner.bind('<Configure>', self.resize)
# resize inner frame to canvas size
self.resize_width = False
self.resize_height = False
self._canvas.bind('<Configure>', self.inner_resize)
def resize(self, event=None):
self._canvas.configure(scrollregion=self._canvas.bbox('all'))
def inner_resize(self, event):
# resize inner frame to canvas size
if self.resize_width:
self._canvas.itemconfig(self._window, width=event.width)
if self.resize_height:
self._canvas.itemconfig(self._window, height=event.height)
# --- main ----
root = tk.Tk()
sf = ScrolledFrame(root)
sf.resize_width = True # it will resize frame to canvas
sf.pack(fill='both', expand=True)
s = tk.StringVar()
s.set("I'm a box")
for _ in range(100):
eb = ttk.Entry(sf.inner, textvariable=s)
eb.pack(fill='x', expand=True)
root.mainloop()