如何鼠标悬停在python中的按钮组

时间:2014-10-09 12:27:27

标签: python-2.7 tkinter

我只是尝试鼠标悬停功能,一组5个按钮,鼠标悬停颜色 设置运行正常,只有一个按钮样本,但它没有给出正确的结果 当我尝试多个按钮时,它只影响最后一个绑定按钮,任何人都可以 帮我快速解决下面的代码有什么问题,我尝试使用bind和bind_all方法! 两者都未能提供所需的产出。

import os
from Tkinter import *
import Tkinter as tk
global btnPlaceY
btnPlaceY=10

class App():

    def __init__ (self):

        root['width']=400
        root['height']=600
        root['bg']='blue'
        self.btnGroupCreate()

    def btnDisplaymessage1(self):

        print "Function testing1"

    def btnDisplaymessage2(self):

        print "Function testing2"

    def btnGroupCreate(self):

        btnNameNameGroup="btnSample"
        self.btnWidget={}

        lstBtnTitle=['A','B','C','D','E']

        lstBtnCommands=[ lambda:self.btnDisplaymessage1(),
                         lambda:self.btnDisplaymessage2() ]

        for B in range(5):

            btnName=btnNameNameGroup+lstBtnTitle[B]  
            global btnPlaceY                
            btnPlaceY=btnPlaceY+70                  

            # Button commands
            if B==0:
                btnMenuCommand=lstBtnCommands[0]                    
            if B==1:
                btnMenuCommand=lstBtnCommands[1]
            if B==2:
                btnMenuCommand=lstBtnCommands[0]                    
            if B==3:
                btnMenuCommand=lstBtnCommands[1]                 

            if B==4:
                btnMenuCommand=lstBtnCommands[1] 
            if B==5:
                btnMenuCommand=lstBtnCommands[0]

            self.btnWidget[btnName]= tk.Button(root, width=4, height=2,text=lstBtnTitle[B],bg="gray",relief=FLAT,command=btnMenuCommand) #activebackground="red"
            widget = self.btnWidget[btnName]
            widget.bind_all("<Enter>", lambda event: event.widget.configure(bg="red"))
            widget.bind_all("<Leave>", lambda event: event.widget.configure(bg="gray"))
            self.btnWidget[btnName].pack()          
            self.btnWidget[btnName].place(x=40, y = btnPlaceY)

    def make_callback(self,btnName):
        print "make_callback"
        root['bg']='green'
        widget = self.btnWidget[btnName]
        def callback(event):
            print "callback"
            widget.configure(bg="red")
            root['bg']='green'
        return callback

root=tk.Tk()
app = App()
root.mainloop()

2 个答案:

答案 0 :(得分:1)

self.btnWidget[btnName].bind_all("<Enter>", lambda event:  self.btnWidget[btnName].configure(bg="red")) 
self.btnWidget[btnName].bind_all("<Leave>", lambda event:  self.btnWidget[btnName].configure(bg="yellow"))

在这些行中的lambda表达式中,btnName将具有在循环期间保持的 last 值,而不是绑定事件时的值。它将是&#34; btnSampleE&#34;,即使对于按钮A到D.此外,对于特定于窗口小部件的事件,您应该使用bind而不是bind_all

强迫&#34;早期绑定的典型方式&#34;行为,以便btnName保持其当前值,是为lambda提供一个默认参数,包含你需要的值。

self.btnWidget[btnName].bind("<Enter>", lambda event, x = btnName: self.btnWidget[x].configure(bg="red")) 
self.btnWidget[btnName].bind("<Leave>", lambda event, x = btnName: self.btnWidget[x].configure(bg="yellow"))

您可以为此参数使用您想要的任何变量名称;你甚至可以称之为btnName,因为本地值会使非本地值黯然失色。

self.btnWidget[btnName].bind("<Enter>", lambda event, btnName=btnName:  self.btnWidget[btnName].configure(bg="red")) 
self.btnWidget[btnName].bind("<Leave>", lambda event, btnName=btnName:  self.btnWidget[btnName].configure(bg="yellow"))

答案 1 :(得分:1)

问题是在Python中,诸如lambda函数之类的闭包关闭了周围块中的名称,而不是值。因此,在lambda中观察到的btnName的值取决于它何时被执行 - 因为事件只能在退出循环后发生,btnName是最后看到的。

有很多方法可以避免这个问题,其中一个是凯文的默认值技巧。但是,默认值方法容易出错 - 现在函数接受2个参数而不是1,并且可以在某些上下文中错误地调用。

因此,更好的方法是使用回调制造商:

def make_callback(btnName):
    def callback(event):
        self.btnWidget[btnName].configure(bg="red"))
    return callback

self.btnWidget[btnName].bind_all("<Enter>", make_callback(btnName))

然而,有人注意到btnWidget被重复,所以可以这样做

widget = self.btnWidget[btnName]

def make_callback(btnName):
    def callback(event):
        widget.configure(bg="red")
    return callback

widget.bind_all("<Enter>", make_callback(widget))

可以用lambdas写成:

widget = self.btnWidget[btnName]
widget.bind_all("<Enter>", 
    (lambda widget: lambda event: widget.configure(bg="red"))(widget))

但是,在这种情况下,这种技巧都不是必需的,因为回调中唯一需要的是事件发生的小部件。这在属性widget的TkInter事件中很容易获得,所以你需要做的就是

widget = self.btnWidget[btnName]
widget.bind_all("<Enter>", lambda event: event.widget.configure(bg="red"))
widget.bind_all("<Leave>", lambda event: event.widget.configure(bg="yellow"))