Tkinter Переключение между несколькими кадрами с помощью 2 кнопок

Я новичок в Python и tk и пытаюсь учиться, создавая приложение. В этом я хочу иметь одно окно с 5 кадрами, расположенными друг над другом, и двумя кнопками «Далее» и «Назад».

При открытии окна должен отображаться кадр_1, а когда я нажимаю «Далее», отображается кадр_2. Нажатие «Далее» во второй раз поднимает кадр_3 и так далее. Нажимаю ли я "Назад" предыдущий кадр должен отображаться снова.

До сих пор у меня работало изменение между двумя кадрами вперед и назад:

from tkinter import *

test = Tk()


def but_next_click():
    frame_2.tkraise()


def but_back_click():
    frame_1.tkraise()


test.geometry("300x300")
frame_1 = LabelFrame(test, text = "frame_1", bg = "blue4")
frame_2 = LabelFrame(test, text = "frame_2", bg = "yellow4")
frame_3 = LabelFrame(test, text = "frame_3", bg = "green4")
frame_4 = LabelFrame(test, text = "frame_4", bg = "red4")

but_next = Button(test, text = "Next", width=5, height=1, pady=2,
                  command=but_next_click)
but_back = Button(test, text = "Back", width=5, height=1, pady=2,
                  command=but_back_click)

frame_1.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_2.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_3.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_4.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')

but_next.place(relx=0.7, rely=0.05, anchor='n')
but_back.place(relx=0.3, rely=0.05, anchor='n')

frame_1.tkraise()

test.mainloop()

Однако теперь я пытаюсь обезопасить «номер_страницы». Итак, что я пытался сделать, так это иметь глобальную переменную с именем «номер страницы» и хранить в ней возвращаемое значение функций but-next-click() или but-back-click(). Но на данный момент я не могу продвинуться дальше, потому что функции и переменные не работают так, как я себе это представлял (поработав немного с VBA). Я думаю, что мне не хватает базового понимания того, как функции и переменные работают друг с другом.

from tkinter import *

page_number = 0

test = Tk()

test.geometry("300x300")
frame_1 = LabelFrame(test, text = "frame_1", bg = "blue4")
frame_2 = LabelFrame(test, text = "frame_2", bg = "yellow4")
frame_3 = LabelFrame(test, text = "frame_3", bg = "green4")
frame_4 = LabelFrame(test, text = "frame_4", bg = "red4")

but_next = Button(test, text = "Next", width=5, height=1, pady=2,
                  command=lambda: but_next_click(page_number))
but_back = Button(test, text = "Back", width=5, height=1, pady=2,
                  command=lambda: but_back_click(page_number))


def but_next_click(page):
    print(f'Button Next Start - Page is {page}')
    if page == 0:
        frame_1.tkraise()
        page = page + 1
    if page == 1:
        frame_2.tkraise()
        page = page + 1
    if page == 2:
        frame_3.tkraise()
        page = page + 1
    print(f'Button Next END - Page is {page}')
    return page


def but_back_click(page):
    print(f'Button Back Start - Page is {page}')
    if page == 1:
        frame_1.tkraise()
        page = page - 1
    if page == 2:
        frame_2.tkraise()
        page = page - 1
    if page == 3:
        frame_3.tkraise()
        page = page - 1
    print(f'Button Back End - Page is {page}')
    return page


page_number = but_next_click(page_number) or but_back_click(page_number)
# thats how I would store the output of a function. But this also executes the functions.
# Which I dont want. I only want to store the page value and use with the next button click

frame_1.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_2.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_3.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_4.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')

but_next.place(relx=0.7, rely=0.05, anchor='n')
but_back.place(relx=0.3, rely=0.05, anchor='n')

frame_1.tkraise()

test.mainloop()

Я пытался понять ввод и вывод функции. Чего я не понимаю, так это запускать вывод без нажатия кнопки:

Кнопка «Далее начать» — страница равна 0
Кнопка Далее КОНЕЦ - страница 3 (*1)

Я ожидаю, что программа проверяет, какое значение имеет page_number, а затем использует первое «если». Но я не понимаю, почему он перебирает все if, что приводит к выводу 3, когда я запускаю программу. (*1)

Но, по крайней мере, значение сохраняется, чтобы с ним мог работать but_back_click. Но почему-то выходное значение не сохраняется.

Кнопка Назад Старт - Страница 3
Кнопка Back End - страница 2
Кнопка Назад Старт - Страница 3
Кнопка Back End - страница 2

Что я могу попробовать дальше?

Я ясно вижу, что вы приложили некоторые усилия к этому вопросу и что вы собираетесь изучить основы, к сожалению, Stack Overflow не предназначен для обучения. Обычно мы занимаемся отладкой, и да, есть проблема, связанная с вашим кодом, но есть несколько вопросов, поэтому ее следует закрыть в пользу «требует большего внимания». Однако вам следует искать «пространство имен и области действия python», и вам нужно будет исследовать «понимание tkinter mainloop» в качестве отправной точки. Кроме того, в ближайшем будущем необходимо будет охватить «tkinter и lambda».

Thingamabobs 04.02.2023 21:38

Если вам удастся набрать еще 10 баллов, нажмите на мой профиль и присоединитесь к чату tkinter, где мы немного исследуем и объясним.

Thingamabobs 04.02.2023 21:40

если page == 0, то page = page + 1, то есть page = 1. И снова вы выполняете следующую проверку: если page == 1... получается ли page = 3. Вместо использования page = page + 1, используйте return page + 1

mamg2909 04.02.2023 22:17

Что касается наличия глобальной переменной, не используйте page в качестве входных данных для функций, просто объявите page как глобальную в начале каждой функции (и объявите page глобальную при ее инициализации)

mamg2909 04.02.2023 22:36

Редактор сообщений здесь использует Markdown в качестве языка для форматирования сообщений. Это означает, что если вы заключите слово в символы подчеркивания (_слово_), оно будет отображаться курсивом, например: слово. Если вы не хотите, чтобы это произошло, используйте обратные кавычки (косые апострофы), чтобы отобразить это как код: word. (Дополнение: если у вас возникли проблемы с форматированием сообщений, пожалуйста, не упоминайте об этом в самом сообщении. Сообщения здесь хранятся вечно как полезный набор вопросов и ответов по программированию - и проблемы с редакторами не имеют отношения к этой миссии).

halfer 06.02.2023 11:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
55
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

Затем, когда нажимается кнопка «назад» или «вперед», он может перебирать список, содержащий кадры, и проверять, является ли каждый из них верхним кадром. Когда он находит текущий верхний кадр, он может определить, какой кадр поднять следующим, основываясь на его позиции в списке. Это позволяет избежать использования оператора if для каждого кадра, и он будет работать одинаково для любого количества кадров, которые вам в конечном итоге потребуется создать.

Это также имеет то преимущество, что не нужно ничего передавать обратным вызовам кнопок или использовать лямбда-выражение в параметрах кнопок command.

Например:

from tkinter import *

test = Tk()

test.geometry("300x300")
frame_1 = LabelFrame(test, text = "frame_1", bg = "blue4")
frame_2 = LabelFrame(test, text = "frame_2", bg = "yellow4")
frame_3 = LabelFrame(test, text = "frame_3", bg = "green4")
frame_4 = LabelFrame(test, text = "frame_4", bg = "red4")

frame_info = {
    "top": frame_1,  # start with frame_1 on top
    "frames": [frame_1, frame_2, frame_3, frame_4]
}


def get_frame_index():
    """Iterate list of frames to find which one is on top."""
    for i, frame in enumerate(frame_info["frames"]):
        if frame == frame_info["top"]:
            return i

def but_next_click():
    """Determine next frame based on it's position in the list."""
    frames = frame_info["frames"]
    index = get_frame_index()
    if index == len(frames) - 1:
        next_frame = frames[0]
    else:
        next_frame = frames[index+1]
    next_frame.tkraise()    # raise the next frame
    frame_info["top"] = next_frame   # assign the next frame to the "top" frame in dictionary.

def but_back_click():
    frames = frame_info["frames"]
    index = get_frame_index()
    if index == 0:
        next_frame = frames[-1]
    else:
        next_frame = frames[index-1]
    next_frame.tkraise()
    frame_info["top"] = next_frame


but_next = Button(test, text = "Next", width=5, height=1, pady=2,
                  command=but_next_click)
but_back = Button(test, text = "Back", width=5, height=1, pady=2,
                  command=but_back_click)
frame_1.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_2.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_3.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')
frame_4.place(relx=0.5, rely=0.2, relwidth=0.9, relheight=0.9, anchor='n')

but_next.place(relx=0.7, rely=0.05, anchor='n')
but_back.place(relx=0.3, rely=0.05, anchor='n')

frame_1.tkraise()

test.mainloop()

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

Zitter 05.02.2023 07:02

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

Итак, я обнаружил, что мне пришлось добавить «глобальный» к обеим функциям. Таким образом, but_back_click() и but_next_click() могут читать и записывать его.

def but_next_click():
    global page_number
    print(f'Button Next Start - Page is {page_number}')
    if page_number <= 2:       #this just prevents overcounting, when the button is clicked too many times
        if page_number == 0:
            frame_2.tkraise()
        elif page_number == 1:
            frame_3.tkraise()
        elif page_number == 2:
            frame_4.tkraise()
        page_number = page_number + 1
    print(f'Button Next END - Page is {page_number}')
    return

По-другому, работа с классами

Создайте объект пользовательского интерфейса и выполните все действия в этом объекте. Создавайте экраны и добавляйте их в список. Позже вы можете изменить текущую позицию индекса. Метод управления отслеживает текущий индекс и упаковывает кадр в окно.

Может быть, это поможет вам.

import tkinter as tk 
from tkinter import ttk 

class Ui:
def __init__(self):        
    self.frame_obj_array=[]
    self.create_screens(5)
    self.activescreen_index=0
    self.last_activescreen_index=1
    root.after(12,self.update)
    
def create_screens(self,n):
    for i in range(n):
        frame=tk.Frame(root)
        btn_back=ttk.Button(frame,text='back',command=self.back)
        btn_next=ttk.Button(frame,text='next',command=self.next)
        lbl=tk.Label(frame,text=f'Frame: {str(i)}!',width=100,font=('Railway',20))
        btn_back.pack(side='left',anchor='sw',pady=40,padx=40)
        btn_next.pack(side='right',anchor='se',pady=40,padx=40)
        lbl.pack(side='top')
        self.frame_obj_array.append(frame)

def back(self):
    if not self.activescreen_index <= 0:
        self.activescreen_index-=1

def next(self):
    if self.activescreen_index < len(self.frame_obj_array)-1:
        self.activescreen_index+=1

def manage_screens(self):
    if self.activescreen_index != self.last_activescreen_index:
        self.frame_obj_array[self.last_activescreen_index].pack_forget()
        self.frame_obj_array[self.activescreen_index].pack(expand=True,fill='both')
        self.last_activescreen_index=self.activescreen_index
   
def update(self):
    self.manage_screens()
    root.after(12,self.update)

if __name__ == 'main':
    root=tk.Tk()
    root.geometry('%dx%d+%d+%d' % (900,600, 0, 0))
    app=Ui()
    root.mainloop()

Другие вопросы по теме