2015-08-18 54 views
3

基于Dynamically changing scrollregion of a canvas in Tkinter的示例,我试图实现一个框架,您可以使用tkinter在可滚动框架中添加和删除条目。我的问题是,删除条目后,保留项目的框架不会调整大小。添加条目时,它会正确调整大小。我叫update_layout()在两种情况下:可滚动框架在Python中使用tkinter无法正确调整大小

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

回答

1

我从事类似的东西,所以我把我的代码,并与您的合并它的答案。

这是一个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 
相关问题