Chinaunix首页 | 论坛 | 博客
  • 博客访问: 72572
  • 博文数量: 26
  • 博客积分: 628
  • 博客等级: 中士
  • 技术积分: 315
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-22 11:57
文章分类

全部博文(26)

文章存档

2012年(4)

2011年(19)

2010年(3)

我的朋友

分类: Python/Ruby

2011-04-27 17:41:17

看完上个程序的执行结果, 我们肯定要问:
"这到底是咋了??!! 'buttonHandler'为啥在事件循环开始前就执行了!!"

原因是下面这句导致的:

    self.button1 = Button(self.myContainer1,
command=self.buttonHandler(button_name, 1, 'Good stuff!'))

我们在这儿只是调用了buttonHandler这个函数, 而不是将它当作一个回调函数传给command. 这可不是我们想要的, 但我们确实是这么干了.

*注意一下* -->
 * buttonHandler是一个函数对象, 可以作为回调函数来进行绑定.
 * buttonHandler()(注意括号)却是一次实际的函数调用.

当下面这句:

    self.button1 = Button(self.myContainer1,
command=self.buttonHandler(button_name, 1, 'Good stuff!'))

被执行时, 它实际是在发生一次'buttonHandler'的调用. buttonHandler被执行后, 打印出一条消息, 并返回调用的结果(在这种情况下是None对象). 这时按钮的'command'参数被绑定到调用返回的结果上. 简单的说就是command被绑定到了None对象上. 那就是为啥当你按按钮时, 啥也不发生的原因了.

*有解决办法么?*

这个...有啥解决方案没? 有啥办法可以参数化或重用一个事件处理器函数?

当然. 有很多技术可以解决. 其中一种是使用python内置的'lambda'函数, 另外还有一种叫做'currying'.

在本程序中, 我们主要讨论如何用lambda, 下个程序我们再来讲currying.

我不打算解决lambda和currying是如何工作的 -- 这太复杂了, 并且也跑题跑的太远了. 所以我打算将它们当成黑盒子来对待.

好了, 先来看看lambda.

*命令绑定*

最早, 我们认为下面的代码应该是可以工作的:

    self.button1 = Button(self.myContainer1,
command=self.buttonHandler(button_name, 1, 'Good stuff!'))

... 但我们看到它并不能如我们所想的那样工作.

想让代码按我们说的做就得重写这行:

    self.button1 = Button(self.myContainer1,
command=lambda
arg1=button_name, arg2=1, arg3='Good stuff!' :
self.buttonHandler(arg1, arg2, arg3)
)

*事件绑定*

嗯, 不错, lambda也可以用相同的办法来参数化事件绑定. 下面是原来的写法:

    self.button1.bind('',
self.buttonHandler_a(event, button_name, 1, 'Good stuff!'))

(它不能工作, 因为没办法将event参数包含在参数列表中), 我们可以用lambda这样写:

    self.button1.bind('',
lambda
event, arg1=button_name, arg2=1, arg3='Good stuff!' :
self.buttonHandler_a(event, arg1, arg2, arg3)
)

[注意这里'event'在这儿是一个参数 -- 它不是python的关键词或其他啥的. 这个例子使用'event'作为event参数, 也可以用'e'来表示event参数, 如果我们想的话, 也可以用'event_arg'来表示]

用lambda的一个好处是我们能(如果我们想的话)不传event参数. 如果我们不传入event参数, 可以直接调用self.buttonHandler, 而不是间接的通过调用self.buttonHandler_a函数来实现.

为了展示这项技术, 我们会将button2的事件绑定代码与button1的不写成一样的. 就像下面这样:

    self.button2.bind('',
lambda
event, arg1=button_name, arg2=2, arg3='Bad stuff!' :
self.buttonHandler(arg1, arg2, arg3)
)

*代码示例*

当程序运行时, 它就会如我们想的那样正常工作了.

注意当你按TAB, 你可以将焦点在OK和CANCEL之间切换.

你应该实验一下通过按键来触发OK按钮的操作. 当你通过按触发OK按钮时, buttonHandler_a会被调用, 你会看到打印出传进来的事件的信息.

在任何情况, 无论你是用鼠标还是鼠标的键来触发按钮操作, 它都会很好的工作.

  1. from Tkinter import *

  2. class MyApp:
  3.     def __init__(self, parent):
  4.         self.myParent = parent
  5.         self.myContainer1 = Frame(parent)
  6.         self.myContainer1.pack()
  7.         
  8.         #------------------ BUTTON #1 ------------------------------------
  9.         button_name = "OK"
  10.         
  11.         # command binding
  12.         self.button1 = Button(self.myContainer1,
  13.             command = lambda
  14.             arg1=button_name, arg2=1, arg3="Good stuff!" :
  15.             self.buttonHandler(arg1, arg2, arg3)
  16.             )
  17.         
  18.         # event binding -- passing the event as an argument
  19.         self.button1.bind("",
  20.             lambda
  21.             event, arg1=button_name, arg2=1, arg3="Good stuff!" :
  22.             self.buttonHandler_a(event, arg1, arg2, arg3)
  23.             )
  24.          
  25.         self.button1.configure(text=button_name, background="green")
  26.         self.button1.pack(side=LEFT)
  27.         self.button1.focus_force() # Put keyboard focus on button1
  28.         
  29.         #------------------ BUTTON #2 ------------------------------------        
  30.         button_name = "Cancel"
  31.         
  32.         # command binding
  33.         self.button2 = Button(self.myContainer1,
  34.             command = lambda
  35.             arg1=button_name, arg2=2, arg3="Bad stuff!":
  36.             self.buttonHandler(arg1, arg2, arg3)
  37.             )

  38.         # event binding -- without passing the event as an argument
  39.         self.button2.bind("",
  40.             lambda
  41.             event, arg1=button_name, arg2=2, arg3="Bad stuff!" :
  42.             self.buttonHandler(arg1, arg2, arg3)
  43.             )
  44.     
  45.         self.button2.configure(text=button_name, background="red")
  46.         self.button2.pack(side=LEFT)
  47.         
  48.         
  49.     def buttonHandler(self, argument1, argument2, argument3):
  50.         print " buttonHandler routine received arguments:" \
  51.             , argument1.ljust(8), argument2, argument3
  52.         
  53.      def buttonHandler_a(self, event, argument1, argument2, argument3):
  54.         print "buttonHandler_a received event", event
  55.         self.buttonHandler(argument1, argument2, argument3)
  56.         
  57.                 
  58. print "\n"*100 # clear the screen
  59. print "Starting program tt078."                                
  60. root = Tk()
  61. myapp = MyApp(root)
  62. print "Ready to start executing the event loop."
  63. root.mainloop()
  64. print "Finished executing the event loop."
阅读(711) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~