通过 tkinter 的绑定方法通信 类

Communicate classes via tkinter's bind method

我正在使用 tkinter 开发带有 GUI 的软件包。现在通过 tkinter 的 bind 方法与 classes 通信时出现问题。下面列出了代表我想做的事情的简单代码:

import Tkinter as tk

lists = [1,2,3,4,5,6,7]

class selects():

    def __init__(self,root):
        self.root = root
        self.selectwin()

    def selectwin(self):
        """ listbox and scrollbar for selection """
        sb = tk.Scrollbar(self.root)
        lb = tk.Listbox(self.root, relief ='sunken', cursor='hand2')
        sb.config(command=lb.yview)
        sb.pack(side=tk.RIGHT, fill=tk.Y)
        lb.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        lb.config(yscrollcommand=sb.set, selectmode='single')
        for value in lists: lb.insert(tk.END,value)

        lb.bind('<Double-1>',lambda event: self.getvalue())
        self.listbox = lb

    def getvalue(self):
        """ get the selected value """
        value = self.listbox.curselection()
        if value:
            self.root.quit()
            text = self.listbox.get(value)
            self.selectvalue = int(text)

    def returnvalue(self):
        return self.selectvalue


class do():

    def __init__(self):
        root = tk.Tk()
        sl = selects(root)
        # do something... for example, get the value and print value+2, as coded below
        value = sl.returnvalue()
        print value+2

        root.mainloop()


if __name__ == '__main__':
    do()

class selects 采用 Listbox 小部件 select 中的一个值 lists 和 return selected 值通过属性 returnvalue 使用。但是,当 运行 以上代码时会引发错误:

Traceback (most recent call last):
  File "F:\Analysis\Python\fpgui\v2\test2.py", line 47, in <module>
    do()
  File "F:\Analysis\Python\fpgui\v2\test2.py", line 41, in __init__
    value = sl.returnvalue()
  File "F:\Analysis\Python\fpgui\v2\test2.py", line 32, in returnvalue
    return self.selectvalue
AttributeError: selects instance has no attribute 'selectvalue'

我认为这个错误可以通过将 classes selectsdo 合并为一个 class 来解决。但是在我的包中,class selects 会被多个 class 调用,所以最好将 selects 作为一个独立的 class。此外,像这样的 class 之间的通信将经常应用在我的包中。例如,在使用 pick_eventmatplotlib 中选择一些信息后做一些事情,或者在使用 Entry 在另一个 class 中输入文本后更新一个 class 中的列表小部件。那么,对此有什么建议吗?提前致谢。

您在创建 sl 后立即调用 sl.returnvalue()。但是,此时sl.getvalue()还没有被调用,也就是说sl.selectvalue还不存在

如果我正确理解您想做什么,您应该在创建 sl (sl = selects(root)) 之后立即调用 root.mainloop()。这样,Tk 进入主循环,运行s 直到 window 被销毁,即用户双击其中一个值时。然后,sl.getvalue() 变成了 运行,程序可以继续调用 sl.returnvalue() 而不会出错。


由于您实际上并没有在该部分代码中调用主循环,因此我更改了您的代码以反映这一点,并且仍然可以按您希望的方式工作。其中的一个关键方法是 wait_window,它会在本地事件循环中停止执行,直到 window 被销毁。我用了this effbot page on Dialog Windows作为参考:

import Tkinter as tk

lists = [1,2,3,4,5,6,7]

class selects():

    def __init__(self,root):
        self.root = root
        self.selectwin()

    def selectwin(self):
        """ listbox and scrollbar for selection """
        sb = tk.Scrollbar(self.root)
        lb = tk.Listbox(self.root, relief ='sunken', cursor='hand2')
        sb.config(command=lb.yview)
        sb.pack(side=tk.RIGHT, fill=tk.Y)
        lb.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        lb.config(yscrollcommand=sb.set, selectmode='single')
        for value in lists: lb.insert(tk.END,value)

        lb.bind('<Double-1>',lambda event: self.getvalue())
        self.listbox = lb

    def getvalue(self):
        """ get the selected value """
        value = self.listbox.curselection()
        if value:
            self.root.quit()
            text = self.listbox.get(value)
            self.selectvalue = int(text)
            self.root.destroy() # destroy the Toplevel window without needing the Tk mainloop

    def returnvalue(self):
        return self.selectvalue


class do():

    def __init__(self, master):
        self.top = tk.Toplevel()
        self.top.transient(master) # Make Toplevel a subwindow ow the root window
        self.top.grab_set() # Make user only able to interacte with the Toplevel as long as its opened
        self.sl = selects(self.top)
        self.top.protocol("WM_DELETE_WINDOW", self.sl.getvalue) # use the if value: in getvalue to force selection
        master.wait_window(self.top) # Wait until the Toplevel closes before continuing

        # do something... for example, get the value and print value+2, as coded below
        value = self.sl.returnvalue()
        print value+2


if __name__ == '__main__':
    root = tk.Tk()
    d = do(root)
    root.mainloop()