Python学习笔记

您所在的位置:网站首页 按钮点击事件绑定多个方法 Python学习笔记

Python学习笔记

2023-12-14 05:44| 来源: 网络整理| 查看: 265

Python学习笔记 - tkinter: 多个Button绑定类似函数的简单写法

在tkinter编程中,常会遇到这样的问题:多个Button绑定了一个类似的回调函数。例如,我们要编写一个做选择题的程序,设置了4个按钮,点击各个按钮,就分别给选择答案列表加上一个A, B, C, D。使用传统代码:

import tkinter # 导入tkinter模块 items = [] # 初始化选项列表 root = tkinter.Tk() # 创建窗口 root.title('普通代码程序') # 设置标题 root.geometry('500x300') # 设置窗口大小为500x300 root.resizable(width=False, height=False) # 禁止修改窗口大小 def choose_a(): items.append('A') def choose_b(): items.append('B') def choose_c(): items.append('C') def choose_d(): items.append('D') tkinter.Button(root, text='A选项', command=choose_a).pack() # root参数的完整形式是master=root tkinter.Button(root, text='B选项', command=choose_b).pack() tkinter.Button(root, text='C选项', command=choose_c).pack() tkinter.Button(root, text='D选项', command=choose_d).pack() # 更多选项以此类推 def show(): '''输出已选择的选项列表''' print(items) tkinter.Button(master=root, text='输出已选选项', command=show).pack() root.mainloop() # 窗口持久化

在上述代码中,4个choose函数过于麻烦。而且,当选项数量不确定时,这种方法即不可行。因此,使用lambda关键字。 lambda关键字可以声明一个匿名函数。例如,在按钮:

def func(): print('Hello, world!') tkinter.Button(master, command=func) # 注意master要填上父容器的实例

等价于下列代码:

def func(text: str): print(text) tkinter Button(master, command=lambda: func(text='Hello, world!'))

因此, 最开始的选择题代码, 可以改为:

import tkinter # 导入tkinter模块 items = [] # 初始化选项列表 root = tkinter.Tk() # 创建窗口 root.title('lambda代码程序1') # 设置标题root.geometry('500x300') # 设置窗口大小为500x300root.resizable(width=False, height=False) # 禁止修改窗口大小 def choose(item): items.append(item) tkinter.Button(root, text='A选项', command=lambda: choose('A')).pack() # 选项按钮 tkinter.Button(root, text='B选项', command=lambda: choose('B')).pack() tkinter.Button(root, text='C选项', command=lambda: choose('C')).pack() tkinter.Button(root, text='D选项', command=lambda: choose('D')).pack() def show(): '''输出已选择的选项列表''' print(items) tkinter.Button(master=root, text='输出已选选项', command=show).pack() root.mainloop() # 窗口持久化

当然,因为choose函数只有一行代码构成,因此可以写成:

import tkinter # 导入tkinter模块 items = [] # 初始化选项列表 root = tkinter.Tk() root.title('lambda代码程序2') # 设置标题root.geometry('500x300') # 设置窗口大小为500x300 root.resizable(width=False, height=False) # 禁止修改窗口大小 tkinter.Button(root, text='A选项', command=lambda: items.append('A')).pack() tkinter.Button(root, text='B选项', command=lambda: items.append('B')).pack() tkinter.Button(root, text='C选项', command=lambda: items.append('C')).pack() tkinter.Button(root, text='D选项', command=lambda: items.append('D')).pack() def show(): print(items) tkinter.Button(root, text='输出已选选项', command=show).pack() root.mainloop() # 窗口持久化

用for循环改进tkinter.Button的定义:

import tkinter # 导入tkinter模块 items = [] # 初始化选项列表root = tkinter.Tk() # 创建窗口 root = tkinter.Tk() root.title('lambda+for代码程序') # 设置标题root.geometry('500x300') # 设置窗口大小为500x300 root.resizable(width=False, height=False) # 禁止修改窗口大小 for item in ('A', 'B', 'C', 'D'): # 循环定义 tkinter.Button(root, text=item, command=lambda: items.append(item)).pack() def show() -> None: # 顺便说一句, -> None表示返回None. 也可以是bool, tuple, tkinter.Misc等等 print(items) tkinter.Button(root, text='输出已选选项', command=show).pack() root.mainloop() # 窗口持久化

总之, 可以写一个含有共同内容的function, 把不同内容作为参数,再用lambda关键字改写。 注:感谢**@xiansr**同志的提醒,之前的lambda写法确实不可用,目前已经修改,谢谢。 其实,在其他时候也可以使用lambda。这里仍然使用tkinter作为示例:

import tkinter # 导入tkinter模块 import tkinter.messagebox as msg # 导入消息框模块 root = tkinter.Tk() # 定义一个窗口 root.title('lambda示例窗口') root.geometry('500x300') root.resizable(width=False, height=False) def show(event: tkinter.Event, key: str) -> None: # event是tkinter.Misc中bind传入参数所留的参数位置,不留位置会报错 msg.showinfo(title='单击了按钮', message='单击了%s按钮' % key) # 也可改为'单击了{}按钮'.format(key) root.bind('', lambda: show(key='A')) # 绑定了A按钮事件,当A键被按下时执行 root.bind('', lambda: show(key='B')) root.bind('', lambda: show(key='C')) root.mainloop() # 窗口持久化

当然,如果你对Python已经比较熟悉,可以用类来实现。 关于类中__call__方法的说明:

class Class: # Python2.x必须写class Class(object): def __init__(self, func, *args, **kwargs): # 函数 self.func = func # 参数 self.args = args # 键值对形式的参数 self.kwargs = kwargs def __call__(self): ''' PyCharm可能默认为 def __call__(self, *args, **kwargs): 把形参的*args和**kwargs去掉即可 ''' self.func(self.args, self.kwargs) # return self.func(self.args, self.kwargs)

生成实例时,会自动调用__init__方法,这时把参数func, args, kwargs传入,当实例被调用时,执行__call__方法,请看下面的例子:

In[01]: # 定义类 class Homework: def __new__(self): # 只是演示需要,实际不需要自定义此方法。 # 生成实例时自动形成,次序在__init__之前。 super().__new__() print('已拿到作业表单,准备布置作业') def __init__(self, subject, page): # 科目 self.sub = subject # 练习册页数 self.page = page print('已布置作业') def __call__(self, data): print('提交{}作业,练习册第{}页,提交时间{}') # 有无返回值均可 return True In[02]: example = Homework('数学', 31) 已拿到作业表单,准备布置作业 已布置作业 In[03]: call = example('10月29日') 提交数学作业,练习册第31页,提交时间10月29日 In[04]: call Out[03]: True

由此可见,当示例被调用时(即Class('__init__参数')('__call__参数')),会执行类的__call__方法,返回值即__call__方法的返回值(默认为None)。 注:当没有__call__方法时,如果实例被调用,将会抛出异常,提示Uncallable。 因此,上文中的show函数,可以改为:

class Show: def __call__(self, event: tkinter.Event) msg.showinfo(title='单击了按钮', message='单击了%s按钮' % self.key) # 也可改为'单击了{}按钮'.format(key) def __init__(self, key: str): self.key: str = key

bind中的lambda语句可改为:

root.bind('', Show('A')) root.bind('', Show('B')) root.bind('', Show('C'))

调用实例的实际原理是:

Show('C')(event) # event由root.bind自动传入

编者技术有限,如有不足敬请指教!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3