[英]Python: Implementing a series of functions with each one calling the next
編程不是我的領域,但我正在努力學習。 我一直在編寫一個像這樣工作的程序:
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()
現在,這可以完美運行,但是隨着我的程序變得更大更復雜,我開始發現這既不優雅也不易於維護。 我想簡單地按(或多或少)順序編寫每個 function,但是當程序讀取對尚未定義的 function 的引用時,這會導致名稱錯誤(似乎程序不應該擔心它,直到它必須運行 function,到那時它已經看到了 function 定義,但是哦,好吧)。
擁有此功能(從函數內部調用的函數)而不必將下一個 function 定義粘貼在第一個 function 定義中間的最簡單方法是什么? 提前致謝!
我取消嵌套函數以查看錯誤是什么。 您遇到的問題是函數嘗試訪問另一個 function 的 scope 中定義的變量。 那是行不通的。 您要么必須嵌套函數以使其范圍重疊,就像您所做的那樣——這很尷尬——或者你必須使用全局變量——這不那么尷尬,但仍然很尷尬——或者你必須從 function 傳遞變量名至 function。
但是,因為您在這里使用了回調——這是相當先進的。 -- 執行第三個選項更復雜,如果你真的想讓它工作的話。 我會建議一種面向對象的方法。 但坦率地說,對於初學者來說,我建議從比這更簡單的東西開始。
最重要的是你習慣了范圍規則。 至少,我可以用你的代碼來解釋。 這是您得到的 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
,它在全局 scope 中定義(即在程序的“最低級別”。這意味着它可以從任何地方訪問。所以你在這里沒問題。
secondframe = Frame(root)
secondframe.pack()
secondcontent = Label(secondframe, text = 'second window content').pack()
secondbutton = Button(secondframe, text = 'Next ->', command = Thirdwindow).pack()
這些變量都在 Secondwindow 的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 的ThirdWindow
中定義。
現在,我無法解釋完成這項工作所需的所有知識,但這里有一個基本提示。 您可以通過說在函數的命名空間中創建全局變量
global secondframe
secondframe = Frame(root)
通常 python 假定 function 中定義的變量是局部變量,因此您必須另外說明。 這就是global secondframe
所做的。 現在你真的不應該經常這樣做,因為隨着全局 scope 充滿越來越多的變量,使用它們變得越來越難。 函數創建更小的范圍(或在某些上下文中稱為“命名空間”),因此您不必跟蹤所有名稱(以確保您不會在兩個地方使用相同的名稱,或者其他更災難性的錯誤)。
通常,為了避免創建全局變量,您將讓每個 function 通過調用return secondframe
返回它定義的幀。 然后,您可以在包含前一幀的每個 function 中添加一個 function 參數,如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()
您將None
存儲在thirdcontent
中,因為pack()
方法沒有返回值。 如果你想保留對Label
的引用,你必須先保存引用,然后單獨pack()
它,就像我在上面的new_frame
中所做的那樣。
其次,從我的replace
方法中可以看出,實際上不必破壞框架來更改 label 的文本或按鈕命令。 上面仍然破壞了前三幀,只是為了展示它是如何工作的。
希望這能讓你開始。 祝你好運。
您可以為每個 function 添加一個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 安裝程序或幻燈片。
與其擁有許多框架,每個框架的區別僅在於它們包含的文本,為什么不只有一個主框架 object 和文本分開? 我不將 Tk 用於我的 GUI,但這就是我的意思(可能有效):
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
如果您可以復制和粘貼代碼,則可以將其分解:
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()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.