関数のネストを解除して、エラーが何であるかを確認しました。あなたが抱えている問題は、関数が別の関数のスコープで定義された変数にアクセスしようとすることです。それはうまくいきません。スコープがオーバーラップするように関数をネストする必要があります-これは厄介です-またはグローバル変数を使用する必要があります-これはそれほど厄介ではありませんが、それでも厄介です-または関数から変数名を渡す必要があります機能する。
ただし、ここではコールバックを使用しているため、非常に高度です。--3番目のオプションの実行はより複雑です。これを本当に機能させたいのであれば、オブジェクト指向のアプローチをお勧めします。しかし率直に言って、初心者のプログラマーには、これよりも単純なものから始めることをお勧めします。
最も重要なことは、スコープルールに慣れることです。それは、少なくとも、あなたのコードで説明することができます。取得した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()
これらの2つの関数は、ほとんど同じことをしているように見えます。しかし、そうではありません!理由は次のとおりです。
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)
通常、Pythonは関数で定義された変数がローカル変数であると想定しているため、そうでない場合は指定する必要があります。それが何をするかglobal secondframe
です。グローバルスコープがますます多くの変数でいっぱいになるにつれて、それらを操作することがますます難しくなるため、これを頻繁に行うべきではありません。関数はより小さなスコープ(または一部のコンテキストで呼び出される「名前空間」)を作成するため、すべての名前を追跡する必要はありません(2つの場所で同じ名前を使用しないようにするため、または他のさらに悲惨な間違い)。
通常、グローバル変数の作成を回避するには、各関数に、を呼び出して定義したフレームを返すようにします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()
メソッドには戻り値がないため、に格納None
していました。への参照を保持する場合は、上記で行ったように、最初に参照を保存してから、個別に保存する必要があります。thirdcontent
pack()
Label
pack()
new_frame
次に、私の方法からわかるように、ラベルのテキストやボタンコマンドreplace
を変更するために、実際にフレームを破棄する必要はありません。上記は、それがどのように機能するかを示すために、最初の3つのフレームをまだ破棄しています。
これで始められることを願っています!幸運を。