Как вернуть изменённую переменную?

пишу школьный проект и опыта в программировании у меня мало и вот создал я приложение на tkinter теперь мне нужно чтобы из функции btn1_click выходило pos1 = 0, для того чтобы это окно нельзя было вывести два раза, но пишет ошибку. Помогите пожалуйста или может быть есть другой способ легко ограничить количество открываемых окон одной и той же функции? ``

from tkinter import *

class Main:
    main = Tk()
    main.title("Решатель задач 2999")
    main.geometry('600x400')
    main.resizable(0, 0)
    canvas = Canvas(main, height=600, width=400)
    canvas.pack()

    frame = Frame(main, bg='white')
    frame.place(relx=0, rely=0, relwidth=1, relheight=1)

    pos = 1

    def btn1_click():
        global pos
        if pos==1:
            wnd1 = Toplevel()
            wnd1.protocol("WM_DELETE_WINDOW",)
            wnd1.title("Первый вариант")
            wnd1.geometry('600x400')
            wnd1.resizable(0, 0)
            canvas = Canvas(wnd1, height=600, width=400)
            canvas.pack()
            return (pos - 1)

    def btn2_click():
        wnd2 = Toplevel()
        wnd2.title("Первый вариант")
        wnd2.geometry('600x400')
        wnd2.resizable(0, 0)
        canvas = Canvas(wnd2, height=600, width=400)
        canvas.pack()
        return(0)

    title = Label(frame, text='Выберите вариант', bg='white')
    title.pack()
    btn1 = Button(frame, text='Встречное движение', bg='gray', font=50, command=btn1_click)
    btn1.pack(side=LEFT, padx=50)
    btn2 = Button(frame, text='Попутное движение', bg='gray', font=50, command=btn2_click)
    btn2.pack(side=RIGHT, padx=50)
    main.mainloop()``

Ответы (2 шт):

Автор решения: Captain Wine

А почему не объявить это перменную на уровне класса, со значением 1, после вызова функции значение бы менялось на ноль и соответвенно больше бы функция не отрабатывала? Только не return а просто присвоением нового значения.

→ Ссылка
Автор решения: Raven

Здраствуйте,

1. Ошибка состоит в том, что вы объявили переменную pos непосредственно в классе, вместо этого вынесите переменную из класса и объявите её сразу после from tkinter import *.

Вместо return (pos - 1) используйте pos = 1, поскольку return используется для присваивания (например: var_1 = func()), получим:

from tkinter import *
pos = 1
def btn1_click():
    global pos
    if pos == 0: return # Если pos = 0 завершаем выполнение функции, ничего не вернув
    wnd1 = Toplevel()
    wnd1.title("Первый вариант")
    wnd1.geometry('600x400')
    wnd1.resizable(0, 0)
    canvas = Canvas(wnd1, height=600, width=400)
    canvas.pack()
    pos = 0

""" Продолжение вашего кода """

2. Так или иначе выбранное вами решение проблемы не рационально, также не рекомендуется использовать глобальные переменные. Вот как вы можете улучшить код:

2.1. Не стоит оборачивать код в class Main без необходимости.

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

2.3. Вместо проверки значения переменной просто будем выключать кнопку: заменим command=btn1_click на command=lambda: [ btn1_click(), btn1.config(state=DISABLED) ], тоже самое сделаем со второй кнопкой. Здесь мы используем лямбда-функцию для того, чтобы выполнить сразу несколько команд. Заметьте, что здесь обязательно нужно вызвать функцию с помощью круглых скобок, поскольку мы обернули её в лямбда-функцию.

2.4. Начнём выполнение кода с помощью следующей конструкции. Запомните, что ваш файл должен быть назван main.py.

if __name__ == '__main__':
    main()

Вот улучшенная версия кода: надеюсь, что я вам помог.

from tkinter import *

def btn1_click():
    wnd1 = Toplevel()
    wnd1.protocol("WM_DELETE_WINDOW",)
    wnd1.title("Первый вариант")
    wnd1.geometry('600x400')
    wnd1.resizable(0, 0)
    canvas = Canvas(wnd1, height=600, width=400)
    canvas.pack()

def btn2_click():
    wnd2 = Toplevel()
    wnd2.title("Первый вариант")
    wnd2.geometry('600x400')
    wnd2.resizable(0, 0)
    canvas = Canvas(wnd2, height=600, width=400)
    canvas.pack()

def main():
    window = Tk()
    window.title("Решатель задач 2999")
    window.geometry('600x400')
    window.resizable(0, 0)
    canvas = Canvas(window, height=600, width=400)
    canvas.pack()

    frame = Frame(window, bg='white')
    frame.place(relx=0, rely=0, relwidth=1, relheight=1)

    title = Label(frame, text='Выберите вариант', bg='white')
    title.pack()
    btn1 = Button(frame, text='Встречное движение', bg='gray', font=50, command=lambda:[ btn1_click(), btn1.config(state=DISABLED)])
    btn1.pack(side=LEFT, padx=50)
    btn2 = Button(frame, text='Попутное движение', bg='gray', font=50, command=lambda: [ btn2_click(), btn2.config(state=DISABLED)])
    btn2.pack(side=RIGHT, padx=50)

    window.mainloop()

if __name__ == '__main__':
    main()

3. Есть наилучшее решение с использованием ООП, оно достаточно сложное с вашей точки зрения, но очень и очень удобное, рекомендую сразу перейти на ООП. Возможно, мой ответ не точен, но сухое объяснение теории вам ничего не даст: дам подсказку, аргумент self означает, что этот параметр получит объект app, так же все объекты и переменные с префиксом self можно изменять из любой точки класса (не забудьте в аргументы функции записать self).

Здесь же вы можете оптимально ограничить максимальное количество нажатий, создав переменную self.btn1_count = 0 и self.btn2_count = 0, получим:

from tkinter import *

class App(Tk):
    def __init__(self):
        super(App, self).__init__()
        self.title("Решатель задач 2999")
        self.geometry('600x400')
        self.resizable(0, 0)
        self.canvas = Canvas(self, height=600, width=400)
        self.canvas.pack()

        self.frame = Frame(self, bg='white')
        self.frame.place(relx=0, rely=0, relwidth=1, relheight=1)

        self.btn1_count = 0
        self.btn2_count = 0

        title = Label(self.frame, text='Выберите вариант', bg='white')
        title.pack()
        self.btn1 = Button(self.frame, text='Встречное движение', bg='gray', font=50, command=self.btn1_click)
        self.btn1.pack(side=LEFT, padx=50)
        self.btn2 = Button(self.frame, text='Попутное движение', bg='gray', font=50, command=self.btn2_click)
        self.btn2.pack(side=RIGHT, padx=50)

    def btn1_click(self):
        if self.btn1_count > 0: return # Завершаем выполнение функции, если мы уже нажимали на кнопку
        wnd1 = Toplevel()
        wnd1.title("Первый вариант")
        wnd1.geometry('600x400')
        wnd1.resizable(0, 0)
        self.canvas = Canvas(wnd1, height=600, width=400)
        self.canvas.pack()
        self.btn1_count += 1 # self.btn_count = self.btn_count + 1
        # Если хотите, чтобы можно было нажать на кнопку только один раз, и
        # чтобы после этого она выключалась, можно просто использовать
        # в конце этой функции self.btn1_config(state=DISABLED),
        # тогда удалите self.btn1_count = 0 и условие if self.btn1_count > 0: return,
        # то же самое можете сделать со второй кнопкой.

    def btn2_click(self):
        if self.btn2_count > 0: return
        wnd2 = Toplevel()
        wnd2.title("Второй вариант")
        wnd2.geometry('600x400')
        wnd2.resizable(0, 0)
        self.canvas = Canvas(wnd2, height=600, width=400)
        self.canvas.pack()
        self.btn2_count += 1

def main():
   app = App()
   app.mainloop()

if __name__ == '__main__':
    main()
→ Ссылка