基于“在Tkinter中动态更改画布的滚动区域”中的示例,我试图实现一个Frame,您可以在其中使用tkinter在可滚动Frame中添加和删除条目。我的问题是删除条目后,框架容纳项目不会调整大小。添加条目时,它会正确调整大小。我update_layout()在两种情况下都打电话:
update_layout()
from tkinter import * class ScrollableContainer(Frame): """A scrollable container that can contain a number of messages""" def __init__(self, master, **kwargs): Frame.__init__(self, master, **kwargs) #holds canvas & scrollbars # configure row and col to take additional space if there is some self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) # create canvas self.canv = Canvas(self, bd=0, highlightthickness=0) # create scrollbars self.hScroll = Scrollbar(self, orient='horizontal', command=self.canv.xview) self.hScroll.grid(row=1, column=0, sticky='we') self.vScroll = Scrollbar(self, orient='vertical', command=self.canv.yview) self.vScroll.grid(row=0, column=1, sticky='ns') # set postiotion of canvas in (self-)Frame self.canv.grid(row=0, column=0, sticky='nsew') self.canv.configure(xscrollcommand=self.hScroll.set, yscrollcommand=self.vScroll.set) # create frame to hold messages in canvas self.frm = Frame(self.canv, bd=2, bg='gray') #holds messages self.frm.grid_columnconfigure(0, weight=1) # create empty tkinter widget (self.frm) on the canvas self.canv.create_window(0, 0, window=self.frm, anchor='nw', tags='inner') # update layout self.update_layout() # on change of size or location this event is fired. The event provides new width an height to callback function on_configure self.canv.bind('<Configure>', self.on_configure) self.widget_list = [] # update and resize layout def update_layout(self): print('update') self.frm.update_idletasks() self.canv.configure(scrollregion=self.canv.bbox('all')) self.size = self.frm.grid_size() # resize canvas and scroll region depending on content def on_configure(self, event): print('on_configure') # get new size of canvas w,h = event.width, event.height # get size of frm required to display all content natural = self.frm.winfo_reqwidth() self.canv.itemconfigure('inner', width= w if w>natural else natural) self.canv.configure(scrollregion=self.canv.bbox('all')) # add new entry and update layout def add_message(self, text): print('add message') # create var to represent states int_var = IntVar() cb = Checkbutton(self.frm, text=text, variable=int_var) cb.grid(row=self.size[1], column=0, padx=1, pady=1, sticky='we') self.widget_list.append(cb) self.update_layout() # delete all messages def del_message(self): print('del message') for it in self.widget_list: it.destroy() self.update_layout() root = Tk() root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) sc = ScrollableContainer(root, bd=2, bg='black') sc.grid(row=0, column=0, sticky='nsew') def new_message(): test = 'Something Profane' sc.add_message(test) def del_message(): sc.del_message() b = Button(root, text='New Message', command=new_message) b.grid(row=1, column=0, sticky='we') del_b = Button(root, text='Del Message', command=del_message) del_b.grid(row=2, column=0, sticky='we') root.mainloop()
我正在做类似的事情,所以我拿了我的代码并将其与您的代码合并以得到答案。
这是一个scrollingFrame类,它将在滚动框调整大小时添加并删除滚动条。然后是您的消息列表的第二个类,该类将告诉scrollingFrame在添加/删除项目时根据需要重新调整自身。
class scrollingFrame(Frame): def __init__(self, parentObject, background): Frame.__init__(self, parentObject, background = background) self.canvas = Canvas(self, borderwidth=0, background = background, highlightthickness=0) self.frame = Frame(self.canvas, background = background) self.vsb = Scrollbar(self, orient="vertical", command=self.canvas.yview, background=background) self.canvas.configure(yscrollcommand=self.vsb.set) self.vsb.grid(row=0, column=1, sticky=N+S) self.hsb = Scrollbar(self, orient="horizontal", command=self.canvas.xview, background=background) self.canvas.configure(xscrollcommand=self.hsb.set) self.hsb.grid(row=1, column=0, sticky=E+W) self.canvas.grid(row=0, column=0, sticky=N+S+E+W) self.window = self.canvas.create_window(0,0, window=self.frame, anchor="nw", tags="self.frame") self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.frame.bind("<Configure>", self.onFrameConfigure) self.canvas.bind("<Configure>", self.onCanvasConfigure) def onFrameConfigure(self, event): #Reset the scroll region to encompass the inner frame self.canvas.configure(scrollregion=self.canvas.bbox("all")) def onCanvasConfigure(self, event): #Resize the inner frame to match the canvas minWidth = self.frame.winfo_reqwidth() minHeight = self.frame.winfo_reqheight() if self.winfo_width() >= minWidth: newWidth = self.winfo_width() #Hide the scrollbar when not needed self.hsb.grid_remove() else: newWidth = minWidth #Show the scrollbar when needed self.hsb.grid() if self.winfo_height() >= minHeight: newHeight = self.winfo_height() #Hide the scrollbar when not needed self.vsb.grid_remove() else: newHeight = minHeight #Show the scrollbar when needed self.vsb.grid() self.canvas.itemconfig(self.window, width=newWidth, height=newHeight) class messageList(object): def __init__(self, scrollFrame, innerFrame): self.widget_list = [] self.innerFrame = innerFrame self.scrollFrame = scrollFrame # Keep a dummy empty row if the list is empty self.placeholder = Label(self.innerFrame, text=" ") self.placeholder.grid(row=0, column=0) # add new entry and update layout def add_message(self, text): print('add message') self.placeholder.grid_remove() # create var to represent states int_var = IntVar() cb = Checkbutton(self.innerFrame, text=text, variable=int_var) cb.grid(row=self.innerFrame.grid_size()[1], column=0, padx=1, pady=1, sticky='we') self.widget_list.append(cb) self.innerFrame.update_idletasks() self.scrollFrame.onCanvasConfigure(None) # delete all messages def del_message(self): print('del message') for it in self.widget_list: it.destroy() self.placeholder.grid() self.innerFrame.update_idletasks() self.scrollFrame.onCanvasConfigure(None) deviceBkgColor = "#FFFFFF" root = Tk() # Makes the window root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) root.wm_title("Title") # Makes the title that will appear in the top left root.config(background = deviceBkgColor) myFrame = scrollingFrame(root, background = deviceBkgColor) myFrame.grid(row=0, column=0, sticky=N+S+E+W) msgList = messageList(myFrame, myFrame.frame) def new_message(): test = 'Something Profane' msgList.add_message(test) def del_message(): msgList.del_message() b = Button(root, text='New Message', command=new_message) b.grid(row=1, column=0, sticky='we') del_b = Button(root, text='Del Message', command=del_message) del_b.grid(row=2, column=0, sticky='we') root.mainloop() #start monitoring and updating the GUI