【问题标题】:Communicate classes via tkinter's bind method通过 tkinter 的 bind 方法通信类
【发布时间】:2015-04-02 05:15:57
【问题描述】:

我正在使用tkinter 开发一个带有GUI 的包。现在通过 tkinter 的 bind 方法通信类时出现问题。下面列出了一个代表我想要做的简单代码:

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

selects 类通过Listbox 小部件在lists 中选择一个值,并通过属性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'

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

【问题讨论】:

    标签: python class tkinter


    【解决方案1】:

    您在创建sl 后立即调用sl.returnvalue()。但是,此时sl.getvalue() 从未被调用,这意味着sl.selectvalue 尚不存在。

    如果我理解您想要正确执行的操作,您应该在创建 sl (sl = selects(root)) 之后将调用 root.mainloop() 移动到右侧。这样,Tk 会命中主循环,该循环一直运行到窗口被销毁,也就是用户双击其中一个值时。然后,sl.getvalue() 已经运行,程序可以继续调用sl.returnvalue() 而不会出错。


    由于您实际上并未在该部分代码中调用主循环,因此我已更改您的代码以反映这一点,并且仍然可以按照您的意愿工作。其中的一个关键方法是wait_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()
    

    【讨论】:

    • 谢谢。 +1 为您的答案,因为它解决了当前示例中的问题。但是在我的包中,root.mainloop()被称为整个包的背景,而小部件ListboxEntry是在tk.Toplevel()上构造的,而不是直接在tk.Tk()上构造的,我该如何处理这种情况?跨度>
    • 好的,你想让它像一个弹出对话框窗口,用户必须从中选择一些东西才能继续吗?
    • 是的,这就是我真正想做的。并希望将用户选择的价值传递给其他班级。
    • 好的,看看我所做的编辑。我想这就是你想要的。
    • 您的修改在我的包中执行正确,谢谢。
    猜你喜欢
    • 2018-10-12
    • 1970-01-01
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多