Skip to main content
 首页 » 编程设计

python之不随 GUI 更新的动态 GUI 的 Tkinter 动态滚动条

2024年08月19日32tuyile006

这与之前的一个问题有关: Tkinter dynamically create widgets from button

在我问上一个问题时,我认为在动态 GUI 周围添加一个可滚动的框架会很容易。相反,我遇到了一个问题,即在按下按钮后滚动条没有检测到新的框架和输入框。如何在不大量编辑 ScrollFrame 类的情况下解决这个问题?

我知道 Scrollbarframe 可以与其他小部件一起使用,只是动态组件导致了问题。当我将窗口的垂直尺寸缩小到超过 createWidgets 按钮的原始位置时,会出现滚动条,但对于其余动态创建的小部件,滚动条不存在。 Canvas 是否没有检测到框架的垂直尺寸会随着按钮的按下而增加?

注意:我知道通配符导入很糟糕。我只是用一个例子

from tkinter import * 
 
class AutoScrollbar(Scrollbar): 
   # A scrollbar that hides itself if it's not needed. 
   # Only works if you use the grid geometry manager! 
    def set(self, lo, hi): 
        if float(lo) <= 0.0 and float(hi) >= 1.0: 
            # grid_remove is currently missing from Tkinter! 
            self.tk.call("grid", "remove", self) 
        else: 
            self.grid() 
        Scrollbar.set(self, lo, hi) 
    def pack(self, **kw): 
        raise TclError("cannot use pack with this widget") 
    def place(self, **kw): 
        raise TclError("cannot use place with this widget") 
 
class ScrollFrame: 
    def __init__(self, master): 
 
        self.vscrollbar = AutoScrollbar(master) 
        self.vscrollbar.grid(row=0, column=1, sticky=N+S) 
        self.hscrollbar = AutoScrollbar(master, orient=HORIZONTAL) 
        self.hscrollbar.grid(row=1, column=0, sticky=E+W) 
 
        self.canvas = Canvas(master, yscrollcommand=self.vscrollbar.set,  
                        xscrollcommand=self.hscrollbar.set) 
        self.canvas.grid(row=0, column=0, sticky=N+S+E+W) 
 
        self.vscrollbar.config(command=self.canvas.yview) 
        self.hscrollbar.config(command=self.canvas.xview) 
 
        # make the canvas expandable 
        master.grid_rowconfigure(0, weight=1) 
        master.grid_columnconfigure(0, weight=1) 
 
        # create frame inside canvas 
        self.frame = Frame(self.canvas) 
        self.frame.rowconfigure(1, weight=1) 
        self.frame.columnconfigure(1, weight=1) 
 
    def update(self): 
        self.canvas.create_window(0, 0, anchor=NW, window=self.frame) 
        self.frame.update_idletasks() 
        self.canvas.config(scrollregion=self.canvas.bbox("all")) 
 
        if self.frame.winfo_reqwidth() != self.canvas.winfo_width(): 
            # update the canvas's width to fit the inner frame 
            self.canvas.config(width = self.frame.winfo_reqwidth()) 
        if self.frame.winfo_reqheight() != self.canvas.winfo_height(): 
            # update the canvas's width to fit the inner frame 
            self.canvas.config(height = self.frame.winfo_reqheight()) 
frames = [] 
widgets = []             
 
def createwidgets(): 
    global widgetNames 
    global frameNames 
 
    frame = Frame(o.frame, borderwidth=2, relief="groove") 
    frames.append(frame) 
 
    frame.pack(side="top", fill="x") 
 
    widget = Entry(frame) 
    widgets.append(widget) 
 
    widget.pack(side="left") 
 
root = Tk() 
o = ScrollFrame(root) 
label = Label(o.frame, text = "test") 
label1 = Label(o.frame, text = "test") 
label2 = Label(o.frame, text = "test") 
label3 = Label(o.frame, text = "test") 
label.pack() 
label1.pack() 
label2.pack() 
label3.pack() 
 
 
createWidgetButton = Button(o.frame, text="createWidgets",  
command=createwidgets) 
createWidgetButton.pack(side="bottom", fill="x") 
 
o.update() 
root.mainloop() 

这就是窗口完全展开时的样子

如果我要缩小窗口,它应该立即创建一个垂直滚动条,因为它会覆盖一个小部件。但是,滚动条就像程序仍处于初始状态一样。

不正确的滚动条(在滚动条出现的那一刻)

请您参考如下方法:

您需要确保更新 Canvas scrollregion每当您将小部件添加到内部框架时。最常见的解决方案是绑定(bind)到框架的 <Configure>事件,只要框架改变大小时就会触发。

在ScrollFrame.__init__创建框架后添加以下行:

self.frame.bind("<Configure>", self.reset_scrollregion) 

然后,将这个函数添加到ScrollFrame :

def reset_scrollregion(self, event): 
    self.canvas.configure(scrollregion=self.canvas.bbox("all")