为什么在声明pack_propagate(0)时禁用内部框架的画布上的tkinter滚动条?

时间:2017-12-25 04:17:34

标签: python tkinter tkinter-canvas tkinter-entry

我的代码有一个框架,框架内的画布和画布内的内框架。我想将Entry框放在内框中,并通过pack_propagate(0)将它们放到内框中,以避免使用Entry小部件的宽度选项进行像素/字体宽度转换。但是,这会破坏内部Frame的滚动功能。我想在内部框架的第一个和最后一个Entry小部件的上方和下方动态添加Entry小部件,我目前正在使用pack(before=)。所以我想尽可能坚持打包机。

如何让滚动条再次运行?以下最小工作示例已注释掉frame.pack_propagate(0),因此Entry小部件的大小不正确:

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()

修改1:添加了StringVar()并将eb.grid()更改为eb.pack()

1 个答案:

答案 0 :(得分:0)

请参阅将内部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()