问题描述
|
编程不是我的专业,但我正在尝试学习。
我一直在写一个程序,其工作原理如下:
from Tkinter import *
root=Tk()
def Secondwindow():
firstframe.destroy()
secondframe = Frame(root)
secondframe.pack()
secondcontent = Label(secondframe,text = \'second window content\').pack()
def Thirdwindow():
secondframe.destroy()
thirdframe = Frame(root)
thirdframe.pack()
thirdcontent = Label(thirdframe,text = \'third window content\').pack()
def Fourthwindow():
thirdframe.destroy()
fourthframe = Frame(root)
fourthframe.pack()
fourthcontent = Label(fourthframe,text = \'fourth window content\').pack()
thirdbutton = Button(thirdframe,text = \'Next ->\',command = Fourthwindow).pack()
secondbutton = Button(secondframe,command = Thirdwindow).pack()
firstframe = Frame(root)
firstframe.pack()
firstcontent = Label(firstframe,text = \'first window content\').pack()
firstbutton = Button(firstframe,command = Secondwindow).pack()
root.mainloop()
现在,它可以正常工作,但是随着我的程序变得越来越大和越来越复杂,我开始发现它既不美观也不易于维护。我想简单地按(或多或少)顺序编写每个函数,但是当程序读取对尚未定义的函数的引用时,这会导致名称错误(似乎程序不必为此担心)直到它必须运行该功能为止,届时它已经看到了功能定义,但是哦)。
拥有此功能(从函数内部调用的函数)而不必将下一个函数定义放在第一个函数定义中间的最简单方法是什么?提前致谢!
解决方法
我取消嵌套功能以查看错误是什么。您遇到的问题是这些函数尝试访问在另一个函数范围内定义的变量。那行不通。您要么像以前那样嵌套函数,以使它们的作用域重叠-这很尴尬-要么您必须使用全局变量-不太笨拙,但仍然很尴尬-或必须从函数传递变量名发挥作用。
但是,因为您在这里使用回调-相当先进! -执行第三个选项更加复杂。如果您真的想使此工作正常,我建议使用一种面向对象的方法。但是坦率地说,对于初学者来说,我建议从比这简单的东西开始。
最重要的是,您已经习惯了对规则进行范围界定。至少,我可以用您的代码进行解释。这是对您收到的NameErrors的解释。
def Secondwindow():
firstframe.destroy()
secondframe = Frame(root)
secondframe.pack()
secondcontent = Label(secondframe,text = \'second window content\').pack()
secondbutton = Button(secondframe,text = \'Next ->\',command = Thirdwindow).pack()
def Thirdwindow():
secondframe.destroy()
thirdframe = Frame(root)
thirdframe.pack()
thirdcontent = Label(thirdframe,text = \'third window content\').pack()
thirdbutton = Button(thirdframe,command = Fourthwindow).pack()
这两个函数看起来几乎一样。但是他们没有!原因如下:
def Secondwindow():
firstframe.destroy()
该行引用的是“ 3”,它是在全局范围内定义的(即在程序的“最低级别”。这意味着可以从任何地方访问它。)
secondframe = Frame(root)
secondframe.pack()
secondcontent = Label(secondframe,command = Thirdwindow).pack()
这些变量均在“ 5”的范围内定义。这意味着它们仅存在于“ 5”之内。一旦离开Secondwindow
,它们便不复存在。有充分的理由!
def Thirdwindow():
secondframe.destroy()
现在您遇到了问题。这会尝试访问secondframe
,但是ѭ9defined仅在Secondwindow
中定义。所以你得到了12英镑。
thirdframe = Frame(root)
thirdframe.pack()
thirdcontent = Label(thirdframe,command = Fourthwindow).pack()
同样,这些都仅在“ 14”的范围内定义。
现在,我无法解释完成这项工作所需的所有知识,但这是一个基本提示。您可以在函数的命名空间中创建全局变量,方法是说
global secondframe
secondframe = Frame(root)
通常,python假定函数中定义的变量是局部变量,因此您必须以其他方式告诉它。那就是ѭ16所做的。现在您真的不应该经常执行此操作,因为随着全局作用域中填充了越来越多的变量,使用它们变得越来越难。函数会创建较小的作用域(或在某些情况下称为“名称空间”),因此您不必跟踪所有名称(以确保在其中不使用相同的名称)两个地方,或者犯其他更灾难性的错误)。
通常,为避免创建全局变量,请让每个函数通过调用return secondframe
返回其定义的帧。然后,您可以向包含前一帧的每个函数添加一个函数参数,如def Thirdwindow(secondframe)
。但是由于您正在使用回调来调用Secondwindow
等,因此此方法会产生麻烦。这是一些使用lambda
语句解决该问题的代码。
from Tkinter import *
root=Tk()
def Secondwindow(firstframe):
firstframe.destroy()
secondframe = Frame(root)
secondframe.pack()
secondcontent = Label(secondframe,command = lambda: Thirdwindow(secondframe)).pack()
def Thirdwindow(secondframe):
secondframe.destroy()
thirdframe = Frame(root)
thirdframe.pack()
thirdcontent = Label(thirdframe,command = lambda: Fourthwindow(thirdframe)).pack()
def Fourthwindow(thirdframe):
thirdframe.destroy()
fourthframe = Frame(root)
fourthframe.pack()
fourthcontent = Label(fourthframe,text = \'fourth window content\').pack()
firstframe = Frame(root)
firstframe.pack()
firstcontent = Label(firstframe,text = \'first window content\').pack()
firstbutton = Button(firstframe,command = lambda: Secondwindow(firstframe)).pack()
root.mainloop()
但是解决此问题的最佳方法是使用面向对象的代码。不幸的是,这个话题太复杂了,无法进入。它只会给已经很长的帖子增加更多的修饰语。老实说,我认为您应该花一些时间来习惯功能和范围界定。
就是说,我花了些时间摆弄面向对象的变体。这里是:
from Tkinter import *
root=Tk()
class FrameRepeater(object):
def __init__(self,start=0,end=4):
self.frame = None
self.number = start
self.end = end
def new_frame(self):
if self.frame:
self.frame.destroy()
self.frame = Frame(root)
self.frame.pack()
self.content = Label(self.frame,text = \'window \' + str(self.number) + \' content\')
self.content.pack()
self.button = Button(self.frame,command = self.replace)
self.button.pack()
self.number += 1
def replace(self):
if self.number < self.end:
self.new_frame()
elif self.number >= self.end:
self.content.config(text=\'Press button again to quit\')
self.button.config(command=self.quit)
def quit(self):
self.frame.destroy()
root.destroy()
exit()
FrameRepeater().new_frame()
root.mainloop()
需要注意的几件事。首先,在像这样的代码行中,存在一个细微的错误:
thirdcontent = Label(thirdframe,text = \'third window content\').pack()
您将None
存储在thirdcontent
中,因为pack()
方法没有返回值。如果要保留对Label
的引用,则必须先保存该引用,然后再分别对ѭ26进行保存,就像上面在new_frame
中所做的一样。
其次,从我的replace
方法中可以看到,实际上不必破坏框架即可更改标签或按钮命令的文本!上面的内容仍然破坏了前三帧,只是为了展示其工作原理。
希望这可以帮助您入门!祝好运。
, 您可以向每个函数添加一个parent
变量,因为它或多或少是您递归的唯一动态部分:
def RecursiveWindow(parent):
parent.destroy()
frame = Frame(root)
frame.pack()
framContent = Label(frame,text = \'second window content\').pack()
if foo: # This won\'t go on forever,will it?
RecursiveWindow(self)
看起来您正在使用框架和前进按钮对应用程序进行编码,例如Windows安装程序或幻灯片。
与其拥有许多框架,每个框架仅因它们所包含的文本而不同,为什么不仅仅将一个主框架对象和文本分开?我不在GUI中使用Tk,但这是我的意思(可能会起作用):
from Tkinter import *
slides = [\'Text one\',\'Text two\',\'Text three\',\'cow\']
number = 0
root = Tk()
frame = Frame(root).pack()
button = Button(frame,command = NextFrame).pack()
def NextFrame(number):
frameContent = Label(frame,text = slides[number]).pack()
number += 1
, 如果您可以复制并粘贴代码,则可以将其排除在外:
from Tkinter import *
root=Tk()
messages = [\'first window content\',\'second window content\',\'third window content\',\'fourth window content\' ]
def nextframe(current,messages):
# what happens when you click the button
def command():
current.destroy()
makeframe(messages)
return command
def makeframe(messages):
frame = Frame(root)
frame.pack()
# take the first message
next_content = Label(frame,text=messages.pop(0)).pack()
if messages: # if there are more make the button
next_button = Button(frame,command = nextframe(frame,messages)).pack()
makeframe(messages)
root.mainloop()