2

プログラミングは私の分野ではありませんが、私は学ぼうとしています。私は次のような動作をするプログラムを書いています:

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()

現在、これは完全に機能しますが、私のプログラムが大きくなり、複雑になるにつれて、これはエレガントでも保守も容易ではないことがわかり始めています。各関数を(多かれ少なかれ)順番に記述したいのですが、プログラムがまだ定義されていない関数への参照を読み取ると、名前が表示されます(プログラムはそれまで気にしないようです)関数を実行する必要があります。その時点で、関数の定義はすでに表示されていますが、まあ)。

最初の関数定義の途中で次の関数定義を固定することなく、この機能(関数内から呼び出される関数)を使用する最も簡単な方法は何ですか?前もって感謝します!

4

3 に答える 3

2

関数のネストを解除して、エラーが何であるかを確認しました。あなたが抱えている問題は、関数が別の関数のスコープで定義された変数にアクセスしようとすることです。それはうまくいきません。スコープがオーバーラップするように関数をネストする必要があります-これは厄介です-またはグローバル変数を使用する必要があります-これはそれほど厄介ではありませんが、それでも厄介です-または関数から変数名を渡す必要があります機能する。

ただし、ここではコールバックを使用しているため、非常に高度です。--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していました。への参照を保持する場合は、上記で行ったように、最初に参照を保存してから、個別に保存する必要があります。thirdcontentpack()Labelpack()new_frame

次に、私の方法からわかるように、ラベルのテキストボタンコマンドreplaceを変更するために、実際にフレームを破棄する必要はありません。上記は、それがどのように機能するかを示すために、最初の3つのフレームをまだ破棄しています。

これで始められることを願っています!幸運を。

于 2011-06-01T03:48:04.107 に答える
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インストーラーやスライドショーのように、フレームと進むボタンを使用してアプリケーションをコーディングしているようです。

多くのフレームを持ち、それぞれに含まれるテキストだけが異なるのではなく、1つのマスターフレームオブジェクトとテキストを分離するだけではどうでしょうか。私はGUIに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
于 2011-06-01T03:10:37.870 に答える
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()
于 2011-06-01T04:17:37.287 に答える