Python: реализация ряда функций, каждая из которых вызывает следующую - PullRequest
2 голосов
/ 01 июня 2011

программирование - это не моя область, но я пытаюсь учиться.Я писал программу, которая работает примерно так:

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

Теперь, это работает отлично, но по мере того, как моя программа становится больше и сложнее, я начинаю понимать, что это не элегантно и не легкоподдерживать.Я хотел бы просто написать каждую функцию в (более или менее) последовательности, но это приводит к ошибкам имен, когда программа считывает ссылку на функцию, которая еще не была определена (кажется, что программе не следует беспокоиться об этом, пока она недолжен запустить функцию, к этому времени он уже видел бы определение функции, ну да ладно).

Какой самый простой способ получить эту функцию (функции, вызываемые изнутри функций) без необходимости вставлятьследующее определение функции в середине первого определения функции?Заранее спасибо!

Ответы [ 3 ]

2 голосов
/ 01 июня 2011

Я удалил функции, чтобы увидеть, в чем была ошибка.Проблема в том, что функции пытаются получить доступ к переменным, определенным в области действия другой функции.Это не сработает.Вы должны либо вкладывать функции так, чтобы их области перекрывались, как вы это делали - что неудобно - или вам приходилось использовать глобальные переменные - что не так неудобно, но все же неудобно - или вам приходилось передавать имена переменных из функциифункционировать.

Однако, потому что вы используете обратные вызовы здесь - которые довольно продвинуты!- выполнение третьего варианта является более сложным.Если вы действительно хотите, чтобы это работало, я бы предложил объектно-ориентированный подход.Но, честно говоря, я бы посоветовал начать с чего-то более простого для начинающего программиста.

Самое важное, что вы привыкаете к определению правил.Это, по крайней мере, я могу объяснить с помощью вашего кода.Вот объяснение именных ошибок, которые вы получали.

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)

Обычно python предполагает, что переменные, определенные в функции, являются локальными переменными, поэтому вы должны сказать это иначеЭто то, что делает 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()

Вы сохраняли None в thirdcontent, потому что метод pack() не имеет возвращаемого значения.Если вы хотите сохранить ссылку на Label, сначала нужно сохранить ссылку, а затем pack() отдельно, как я делал в new_frame выше.

Во-вторых, как вы можете видеть из моего replace метода, вам на самом деле не нужно уничтожать фрейм, чтобы изменить текст метки или команда кнопки!Выше все еще разрушает первые три кадра, просто чтобы показать, как это будет работать.

Надеюсь, это поможет вам начать!Удачи.

1 голос
/ 01 июня 2011

Если вы можете скопировать и вставить код, вы можете выделить его:

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()
1 голос
/ 01 июня 2011

Вы можете добавить переменную 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 для моего 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
...