在字典中为显示启动程序函数指定TkInter回调

2024-09-20 05:33:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我在构建一个Python函数时遇到了困难,该函数使用字典中保存的按钮规范来启动TkInter对象,命令绑定到菜单按钮。在

形势

我正在使用TkInter用Python构建GUI。我已经编写了一个Display类(基于Lutz中的GuiMaker类,“Programming Python”),它应该为各种实体提供一个数据输入窗口,因此任何实例的命令和显示都会有所不同。我想在字典文件中配置这些特定于实体的命令和显示。运行脚本时计算该文件,并在调用显示实例时将其字典传递给启动程序函数。但是实例的命令找不到我试图绑定到它们的实例方法。在

功能工程说明书

当使用专用函数中指定的配置启动显示实例时,这不是问题。例如,这很好:

def launchEmployee():
    display = ''
    menuBar = [('File', 0, [('Save', 0, (lambda: display.onSave()))])]
    title = 'Employee Data Entry'
    display_args = {'title': title,
                    'menuBar': menuBar}
    display = DisplayScreen(**display_args)

GuiMaker中的DisplayScreen子类,它具有处理menuBar对象以创建菜单的方法。它有一个onSave()方法。在

这样,当单击“Save”按钮时,Display实例会找到并运行自己的onSave()方法。在

字典文件中的规范不起作用

但是,当我试图从启动程序函数启动Display实例时,从单独文件中保存的字典中提取它的规范时,这就行不通了。在

配置文件:

^{pr2}$

脚本文件:

config = eval(open('config_file', 'r').read())

def launchDisplay(config):
    display = ''
    display = DisplayScreen(**config)

以这种方式运行,单击“保存”会生成一个错误,说明没有全局对象“display”。在

理论:DICTIONARY CASE在EVAL()调用时查找作用域中的对象

我推测,在函数的情况下,“display”是一个string对象,它缺少onSave()方法对于菜单栏的赋值来说不是问题,因为它对方法的检查在lambda函数内部被推迟。当Display实例被分配给“Display”对象时,这会重载前面对string对象的赋值,但Python仍然知道“Display”,并在要求其onSave()方法时访问它。在

如果是这样,则配置案例失败,因为在通过计算创建配置字典时,“display”对象根本不存在。这不会在eval()调用时导致错误,因为lambda函数在调用之前会将对象隐藏起来。但是当调用时,Python会在eval()调用时在作用域中查找“display”对象,在该对象中找不到任何内容,然后报告错误。在

但是:将EVAL()调用放在作用域中没有帮助

但我尝试过在创建'display'string对象之后,将dictionary文件的计算转移到函数中,但这也不起作用。在

所以:

这是怎么回事?
在实例化显示对象时,如何指定绑定到字典中要访问的命令的方法?在配置文件中指定这些屏幕似乎比在大量重复函数中指定要好。在


Tags: 文件对象实例方法lambda函数命令规范
1条回答
网友
1楼 · 发布于 2024-09-20 05:33:34

当您的lambda执行时是应用范围的时候,但问题有点微妙。在

在第一种情况下,lambdalaunchEmployee的嵌套函数,因此Python编译器(在编译封闭函数时)知道扫描其主体以查找对封闭函数的局部变量的引用,并适当地形成闭包

在第二种情况下,eval在编译封闭函数时向Python编译器隐藏嵌套函数,因此它甚至不知道它是一个封闭函数,也不知道它应该如何形成闭包。在

我建议您不要玩弄名称,而是在实例化后将新的display对象插入lambda中。这确实需要找到lambda(或多个lambda),但您可以通过系统地(例如递归地)遍历config值中的所有项,查找type(lambda:0)的实例,并采用一些约定,例如,'正在创建的小部件在这些lambda中被称为“widget”,是最后一个参数(具有默认值)'。在

因此,您可以将配置文件更改为:

'menuBar':[('File', 0, [('Save', 0, (lambda widget=None: widget.onSave()))])],
 'title': 'Employee Data Entry'}

并且,在display = DisplayScreen(**config)之后,config后处理如下:

^{pr2}$

诚然有些棘手的代码,但我看不到一个直接的方法来在指定的上下文中执行此操作,该上下文由一个eval组成,该字符串由一个lambda组成lambda

我通常推荐使用标准库模块inspect,但是由于这个后处理函数不可避免地要处理func_defaultsinspect只检查,不会像那样改变对象的内部结构),所以它的所有代码都在同一个非常深层的层次上进行搅动似乎更为一致。在

编辑:如果您不坚持让窗口小部件只是局部变量,而是可以使它们成为全局对象的属性,则一种更简单的方法是可行的。在

所以在模块级别,你可以说:

^{3}$

在您的函数中,您将新生成的小部件分配给widgets.foobar,而不是分配给裸名foobar。那么你的evaled lambda可以是这样的:

lambda: widgets.foobar.onSave()

一切都会好起来的,因为这样,就不需要闭包了(只需要闭包,也不需要闭包,因为在您最初的方法中,eval是为了在lambda需要时保存局部变量)。在

相关问题 更多 >