[英]Scrollable Frame does not resize properly using tkinter
Based on the example from Dynamically changing scrollregion of a canvas in Tkinter , I am trying to implement a Frame where you can add and delete entries in a scrollable Frame using tkinter.基于动态更改 Tkinter 中画布的滚动区域的示例,我正在尝试实现一个框架,您可以在其中使用 tkinter 在可滚动框架中添加和删除条目。 My Problem is that the Frame holding items does not resize after deleting entries.
我的问题是删除条目后,Frame 持有项目不会调整大小。 When adding entries, it resizes correctly.
添加条目时,它会正确调整大小。 I call
update_layout()
in both cases:我在两种情况下都调用
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()
I was working on something similar so I took my code and merged it with yours for the answer.我正在做类似的事情,所以我拿了我的代码并将其与您的代码合并以获得答案。
Here is a scrollingFrame class that will add scrollbars and remove them whenever the box is resized.这是一个 scrollingFrame 类,它将在调整框大小时添加滚动条并删除它们。 Then there is a second class for your message list that will tell the scrollingFrame to readjust itself as necessary whenever items are added/deleted.
然后,您的消息列表还有第二个类,它会告诉 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.