好吧,所以我几乎要为这件事失去理智了。
我正在开发一个小型应用程序,它从输入XML文件中获取问题,将其显示在屏幕上,获取答案并将其写回输出XML,使用tkinter作为GUI,使用ElementTree进行XML处理。每个问题可能有多个检查和子问题,根据需要在各自的框架上通常显示。
主窗口由main
创建为MainWindow
类实例(其中包含所有tkinter内容和一组有用的方法),位于代码的最后:
def main():
global app
window = Tk()
app = MainWindow(window)
window.mainloop()
if __name__ == '__main__':
main()
这应该使"应用程序"成为一个全局的,应该可以从代码上的任何地方访问,对吧?
显然不是吗?
我已经创建了Question类,它存储从XML中提取的属性,处理文本的显示,并为XML中的每个元素创建Check类,为XML中每个元素创建Subquestion类。
问题类如下:
class Question:
def __init__(self, element):
self.element = element
self.question_text = element.get('text')
self.ok = element.get('ok')
self.answer = element.get('r')
self.comment = element.get('comment')
if element.find('check') is None:
#print(app)
pass
else:
#print(app)
for check in element.iter('check'):
Check(check)
for subq in element.iter('subquest'):
SubQuestion(subq)
还有Check类,它处理复选框的创建以及将它们各自的答案写入ElementTree:
class Check:
def __init__(self, element):
self.element = element
self.check_text = element.get('text')
self.answer = element.get('r')
self.var = IntVar()
self.create_check()
def create_check(self):
print('check created')
print(app)
Checkbutton(app.frameChk, text=self.check_text, variable=self.var, command=self.write_check).pack(side=LEFT)
def write_check(self):
self.element.set('r', self.var.get())
print('check written')
到目前为止,一切都很好。它按预期工作,复选框出现并提交其值,没有问题
但是。。。您注意到Check类中的create_check
方法在创建复选框时引用了app.frameChk
吗?这是为复选框保留的Tk帧,由MainWindow的__init__
方法创建。这个create_check
方法访问app
没有任何问题,即使里面没有global
关键字。我甚至添加了print(app)
来确保这一点。
果不其然,每次创建复选框时,它都会将<__main__.MainWindow object at 0x0000024CCC557BE0>"
打印到控制台。
但是。。。在Question的__init__
方法(或其他任何方法)中,我似乎无法访问任何与app
相关的内容,它抛出classes_app.py", line 129, in __init__ - print(app) NameError: name 'app' is not defined
为什么?这是两个没有从其他类继承任何东西的类,并且在层次结构上是"相等"的,只是程序中的两个平均类(实际上,Question是创建Check实例的类!)。一个人认识到全球,而其他人却不认识?
最令人难以置信的是:Check
类中没有global
关键字,而且它仍然有效?
到目前为止,我已经尝试过:
1-在全局作用域中的main
函数之外声明app = MainWindow(window)
。没有什么
2-在程序中的每个函数中使用global
关键字,看看他们是否得到了它。没有。
3-将main
函数放在代码的最顶部,因此这将是第一个app
的声明似乎也没有多大作用。
4-上述解决方案和试错把戏的任何组合。
5-向python globals的代码女神赠送主板。
请注意,我对python还很陌生,所以我可能理解了一些关于全局变量的可怕错误,或者完全不理解,但根据文档的说法,用global
关键字声明的变量应该是全局变量,并且可以从代码中的任何和所有函数和方法访问全局变量,对吧?
如果你问我为什么如此需要app = MainWindow(window)
的全局性:会有一个全局函数来清理每个问题之间的小部件:
def destroy_widgets(parent):
for widget in parent.winfo_children():
widget.destroy()
因此,在类的方法中,我会像destroy_widgets(app.frameChk)
一样调用它。这就是我第一次遇到这个问题的原因。
我会发布整个代码,但它包含了所有这些tkinter定义,内容相当广泛。如果你们认为这是必要的,我当然会的。
任何帮助或见解都将不胜感激,并提前表示感谢,这个社区是金子!
变量的定义/生存期
关于此:
最令人难以置信的是:Check类中没有全局关键字,而且它仍然有效?
在本地上下文中创建或更改全局变量只需要global
-关键字,请参阅此答案。
此外,语句global app
还没有创建变量app
——它只是在第一次赋值时创建的。在您的示例中,这意味着app
仅在此处创建:app = MainWindow(window)
。如果Question
是在此语句之前创建的(在您的示例中,由Tk()
或由MainWindow(window)
创建),则app
还不存在。
你可以在翻译中尝试一下:
>>> global app
>>> print(app)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'app' is not defined
名称间距
另一个需要注意的重要事实是:如果你的代码被组织在不同的模块中,你必须注意名称空间。
示例假设我们有两个python文件:first.py
和second.py
:
first.py
:的内容
app = 5
second.py
:的内容
import first
print(first.app)
运行此工作:
$ python3 second.py
5
由于模块不同,仅仅放置print(app)
是不可行的。