2011-06-01 65 views
2

编程不是我的领域,但我试图学习。 我一直在写一个程序,它的工作原理是这样的:Python:实现一系列功能,每个函数调用下一个

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, text = 'Next ->', command = Thirdwindow).pack() 
firstframe = Frame(root) 
firstframe.pack() 
firstcontent = Label(firstframe, text = 'first window content').pack() 
firstbutton = Button(firstframe, text = 'Next ->', command = Secondwindow).pack() 

root.mainloop() 

现在,这个完美的作品,但我的程序变得更大,更复杂,我开始看到这既不优雅,也不便于维护。我想简单地按照(或多或少)的顺序编写每个函数,但是当程序读取尚未定义的函数的引用时会导致名称错误(程序似乎不应该担心,直到它必须运行该功能,到时候它已经看到了功能定义,但是哦)。

有什么最简单的方法来实现此功能(函数内调用的函数),而不必在第一个函数定义的中间粘贴下一个函数定义?提前致谢!

+1

你不应该使用“从X导入*”的指令,因为它拉该模块的所有符号为全局命名空间。这可能导致冲突(并且模糊错误)。相反,只需'导入Tkinter'并使用完全限定的名称(如果必须缩短一些,可以'将Tkinter导入为tk'并参考tk.whatever]。 – user505255 2011-06-01 03:03:31

+0

感谢您的意见,我只是复制了我在其他示例中看到的内容。 – Verdigriss 2011-06-01 03:42:44

回答

2

我未嵌套的功能,看看是什么错误。您遇到的问题是函数尝试访问另一个函数范围中定义的变量。这是行不通的。您必须嵌套函数以便它们的作用域重叠,正如您所做的那样 - 这很尴尬 - 或者您必须使用全局变量 - 这样做不那么笨拙,但仍然很尴尬 - 或者必须从函数传递变量名起作用。

但是,因为您在这里使用回调 - 这是相当先进的! - 执行第三个选项比较复杂。如果你真的想得到这个工作,我会建议一个面向对象的方法。但坦率地说,我会建议从一开始的程序员开始比这更简单。

最重要的是您习惯了范围规则。至少,我可以用你的代码解释。以下是你正在获取的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, text = 'Next ->', command = Fourthwindow).pack() 

这两个功能看起来像他们几乎相同的事情。但他们不!这里的原因:

def Secondwindow(): 
    firstframe.destroy() 

这条线是指firstframe,这是在全球范围内(即在程序的“最低水平”定义这意味着它可以从任何地方访问所以你确定这里。 。

secondframe = Frame(root) 
    secondframe.pack() 
    secondcontent = Label(secondframe, text = 'second window content').pack() 
    secondbutton = Button(secondframe, text = 'Next ->', command = Thirdwindow).pack() 

这些变量是所有的Secondwindow范围内定义,这意味着他们只存在内Secondwindow。一旦你离开Secondwindow,它们就不再存在了。有很好的理由!

def Thirdwindow(): 
    secondframe.destroy() 

现在您遇到了您的问题。这试图访问secondframe,但secondframe仅在Secondwindow中定义。所以你得到一个NameError

thirdframe = Frame(root) 
    thirdframe.pack() 
    thirdcontent = Label(thirdframe, text = 'third window content').pack() 
    thirdbutton = Button(thirdframe, text = 'Next ->', command = Fourthwindow).pack() 

再次,这些都只在ThirdWindow范围内定义。

现在,我无法解释所有你需要知道做这项工作,但这里有一个基本的提示。你可以这样

global secondframe 
secondframe = Frame(root) 

通常蟒蛇假定在函数中定义的变量是局部变量创建一个函数的命名空间中的全局变量,所以你要明确的告诉它。这就是global secondframe所做的。现在你不应该经常这样做,因为随着全球范围内变得越来越多,与它们一起工作变得越来越困难。函数创建更小的作用域(或称为“命名空间”,因为它们在某些情况下被调用),以便不必跟踪所有的名称(以确保在两个地方不使用相同的名称,或使其他更加灾难性的错误)。

通常,为了避免创建全局变量,每个函数都会通过调用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, text = 'second window content').pack() 
    secondbutton = Button(secondframe, text = 'Next ->', command = lambda: Thirdwindow(secondframe)).pack() 
def Thirdwindow(secondframe): 
    secondframe.destroy() 
    thirdframe = Frame(root) 
    thirdframe.pack() 
    thirdcontent = Label(thirdframe, text = 'third window content').pack() 
    thirdbutton = Button(thirdframe, text = 'Next ->', 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, text = 'Next ->', 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, text = 'Next ->', 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() 

你被存储在thirdcontentNone,因为pack()方法没有返回值。如果您想保留对Label的引用,则必须先单独保存该引用,然后再保存pack(),就像我在上面的new_frame中那样。

其次,从我的replace方法中可以看到,你实际上并不需要销毁该框架来改变标签文本的按钮命令!上面仍然破坏前三个框架,只是为了说明它如何工作。

希望这会让你开始!祝你好运。

+0

非常感谢!你知道,当我遇到这个问题时,我确实认为'啊哈,这是OOP的用处。'但是当我试图实现它时,我无法实现它。 lambdas的这个修复很简单,我可以理解它。谢谢你的帮助! – Verdigriss 2011-06-01 04:16:56

+0

@Verdigriss,完全没问题!顺便说一下,请参阅我的底部编辑OO解决方案。 – senderle 2011-06-01 06:12:11

1

您可以将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安装程序或幻灯片。

为什么不只有一个主框架对象和文本是分开的,而不是有许多框架,每个框架只有它们包含的文本不同。我不使用Tk的我的图形用户界面,但这里是我的意思是(可能工作):

from Tkinter import * 

slides = ['Text one', 'Text two', 'Text three', 'cow'] 
number = 0 

root = Tk() 
frame = Frame(root).pack() 
button = Button(frame, text = 'Next ->', command = NextFrame).pack() 


def NextFrame(number): 
    frameContent = Label(frame, text = slides[number]).pack() 
    number += 1 
+0

非常抱歉,这看起来像一个很好的答案,但我太无知,无法理解这里发生了什么。 定义要传递给RecursiveWindow的父变量在哪里? if语句底部发生了什么? – Verdigriss 2011-06-01 03:52:01

+0

递归是当你使用你自己定义的函数时,就像你正在使用窗口一样。如果你不退出循环,那么窗口将会继续生成更多的窗口(当然,如果你想要一个动态数字的话)。 – Blender 2011-06-01 03:55:03

+0

父代变量在您的代码中定义。它被认为是“self”,它是当前窗口对象。你在开始时将它定义为'root',但随后每次递归都会将其自身更改为第一个窗口,第二个等。 – Blender 2011-06-01 03:56:23

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, text = 'Next ->', command = nextframe(frame, messages)).pack() 

makeframe(messages) 
root.mainloop() 
+0

阴森恐怖。我正要发布几乎完全像这样的东西! – Blender 2011-06-01 04:19:26

相关问题