【问题标题】:Scrollable Frame does not resize properly using tkinter使用 tkinter 无法正确调整可滚动框架的大小
【发布时间】:2015-08-18 05:01:27
【问题描述】:

基于Dynamically changing scrollregion of a canvas in Tkinter 的示例,我正在尝试实现一个框架,您可以在其中使用 tkinter 在可滚动框架中添加和删除条目。我的问题是在删除条目后,包含项目的框架不会调整大小。添加条目时,它会正确调整大小。在这两种情况下我都调用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()

【问题讨论】:

    标签: python tkinter scrollbar tkinter-canvas


    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 1970-01-01
      • 2016-07-26
      • 1970-01-01
      • 1970-01-01
      • 2015-11-12
      • 2011-05-24
      相关资源
      最近更新 更多