Skip to main content
 首页 » 编程设计

python之在 Tkinter 中动态改变 Canvas 的滚动区域

2025年05月04日13rubylouvre

因此,我有一个继承自 frame 并包含 Canvas 和滚动条的自定义小部件,以及一个也继承自 frame 的自定义小部件,我想将其动态添加到 Canvas ,并根据需要调整 scrollregion 的大小。这是我的代码:

class MessageItem(Frame): 
"""A message to be contained inside a scrollableContainer""" 
 
def __init__(self, message, **kwds): 
    Frame.__init__(self, **kwds) 
    self.text = Label(self, text = message) 
    self.text.grid(column = 0, row = 0, sticky = N+S+E+W) 
 
class scrollableContainer(Frame): 
"""A scrollable container that can contain a number of messages""" 
 
def initContents(self): 
    """Initializes a scrollbar, and a canvas that will contain all the items""" 
 
    #the canvas that will contain all our items 
    self.canv = Canvas(self) 
    self.canv.grid(column = 0, row = 0, sticky = N+S+W) 
    #force Tkinter to draw the canvas 
    self.canv.update_idletasks() 
    #use the values from the canvas being drawn to determine the size of the scroll region 
    #note that currently, since the canvas contains nothing, the scroll region will be the same as  
    #the size of the canvas 
    geometry = self.canv.winfo_geometry() 
    xsize, ysize, xpos, ypos = parse_geometry_string(geometry)  
    self.canv['scrollregion'] = (0, 0, xsize, ysize)  
 
    #the scrollbar for that canvas 
    self.vscroll = Scrollbar(self, orient = VERTICAL, command = self.canv.yview ) 
    self.vscroll.grid(column = 1, row = 0, sticky = N+S+E) 
 
    self.canv["yscrollcommand"] = self.vscroll.set 
 
def __init__(self, **kwds): 
    Frame.__init__(self, **kwds) 
 
    #initialize the widget's contents 
    self.grid(sticky = N+S+E+W) 
    self.pack() 
    self.initContents() 
 
    #initialize the list of contents so we can append to it 
    self.contents = [] 
 
def addMessage(self, message): 
    #Add the message to the list of contents 
    self.contents.append(MessageItem(message)) 
    #Add the message to the grid 
    self.contents[(len(self.contents) - 1)].grid(column = 0, row = (len(self.contents) - 1)) 
    #set the new scrollable region for the canvas 
    scrollregionlist = self.canv['scrollregion'].split() 
    oldRegion = int(scrollregionlist[3]) 
    newRegion = oldRegion + parse_geometry_string(self.contents[ 
        (len(self.contents) - 1)].winfo_geometry())[3] 
    self.canv['scrollregion'] = (int(scrollregionlist[0]), int(scrollregionlist[1]), 
        int(scrollregionlist[2]), newRegion) 

我遇到的问题是 self.canv['scrollregion'] 似乎在 init 之外消失了。在 addMessage 方法中,在行中:

 scrollregionlist = self.canv['scrollregion'].split() 

self.canv 上的 scrollregion 属性返回一个空字符串,我可以通过放置一个来验证

 print self.canv['scrollregion'] 

在那行之前

请您参考如下方法:

您确定文本小部件在这里不够用吗?

无论如何,

from Tkinter import * 
 
 
class MessageItem(Frame): 
    """A message to be contained inside a scrollableContainer""" 
 
    def __init__(self, master, message, **kwds): 
        Frame.__init__(self, master, **kwds) 
        self.grid_rowconfigure(0, weight=1) 
        self.grid_columnconfigure(0, weight=1) 
        self.text = Label(self, text=message, anchor='w', bg='gold') 
        self.text.grid(row=0, column=0, sticky='nsew') 
 
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 
        self.grid_rowconfigure(0, weight=1) 
        self.grid_columnconfigure(0, weight=1) 
 
        self.canv = Canvas(self, bd=0, highlightthickness=0) 
        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') 
        self.canv.grid(row=0, column=0, sticky='nsew')         
        self.canv.configure(xscrollcommand=self.hScroll.set, 
                            yscrollcommand=self.vScroll.set) 
 
        self.frm = Frame(self.canv, bd=2, bg='green') #holds messages 
        self.frm.grid_columnconfigure(0, weight=1) 
 
        self.canv.create_window(0, 0, window=self.frm, anchor='nw', tags='inner') 
 
        self.messages = [] 
        for i in range(20): 
            m = MessageItem(self.frm, 'Something Profound', bd=2, bg='black') 
            m.grid(row=i, column=0, sticky='nsew', padx=2, pady=2) 
            self.messages.append(m) 
 
        self.update_layout()         
        self.canv.bind('<Configure>', self.on_configure) 
 
    def update_layout(self): 
        self.frm.update_idletasks() 
        self.canv.configure(scrollregion=self.canv.bbox('all')) 
        self.canv.yview('moveto','1.0') 
        self.size = self.frm.grid_size() 
 
    def on_configure(self, event): 
        w,h = event.width, event.height 
        natural = self.frm.winfo_reqwidth() 
        self.canv.itemconfigure('inner', width= w if w>natural else natural) 
        self.canv.configure(scrollregion=self.canv.bbox('all')) 
 
    def add_message(self, message): 
        m = MessageItem(self.frm, message, bd=2, bg='red') 
        m.grid(row=self.size[1], column=0, padx=2, pady=2, sticky='we') 
        self.messages.append(m) 
        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) 
 
b = Button(root, text='New Message', command=new_message) 
b.grid(row=1, column=0, sticky='we') 
 
root.mainloop()