Я новичок в 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
Что я могу попробовать дальше?
Если вам удастся набрать еще 10 баллов, нажмите на мой профиль и присоединитесь к чату tkinter, где мы немного исследуем и объясним.
если page == 0, то page = page + 1, то есть page = 1. И снова вы выполняете следующую проверку: если page == 1... получается ли page = 3. Вместо использования page = page + 1
, используйте return page + 1
Что касается наличия глобальной переменной, не используйте page
в качестве входных данных для функций, просто объявите page
как глобальную в начале каждой функции (и объявите page
глобальную при ее инициализации)
Редактор сообщений здесь использует Markdown в качестве языка для форматирования сообщений. Это означает, что если вы заключите слово в символы подчеркивания (_слово_), оно будет отображаться курсивом, например: слово. Если вы не хотите, чтобы это произошло, используйте обратные кавычки (косые апострофы), чтобы отобразить это как код: word
. (Дополнение: если у вас возникли проблемы с форматированием сообщений, пожалуйста, не упоминайте об этом в самом сообщении. Сообщения здесь хранятся вечно как полезный набор вопросов и ответов по программированию - и проблемы с редакторами не имеют отношения к этой миссии).
Я собираюсь предложить альтернативную стратегию. Вместо использования переменной, которая отслеживает текущий номер страницы, я бы предложил создать список, содержащий каждый из ваших кадров, которые вы можете перебирать, и сохранить его, а также текущий верхний кадр в словаре.
Затем, когда нажимается кнопка «назад» или «вперед», он может перебирать список, содержащий кадры, и проверять, является ли каждый из них верхним кадром. Когда он находит текущий верхний кадр, он может определить, какой кадр поднять следующим, основываясь на его позиции в списке. Это позволяет избежать использования оператора 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()
Большое спасибо, а также за очень четкое объяснение! Это очень полезно. Я должен был подумать о списках, надеюсь, что в следующий раз.
На всякий случай, если у других новичков возникнет аналогичная проблема, следующее также работает. Но чтобы прояснить этот ответ: я просто публикую его, чтобы получить отзывы от экспертов, работает ли это, но это просто костыль или действительно полезное решение.
Итак, я обнаружил, что мне пришлось добавить «глобальный» к обеим функциям. Таким образом, 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()
Я ясно вижу, что вы приложили некоторые усилия к этому вопросу и что вы собираетесь изучить основы, к сожалению, Stack Overflow не предназначен для обучения. Обычно мы занимаемся отладкой, и да, есть проблема, связанная с вашим кодом, но есть несколько вопросов, поэтому ее следует закрыть в пользу «требует большего внимания». Однако вам следует искать «пространство имен и области действия python», и вам нужно будет исследовать «понимание tkinter mainloop» в качестве отправной точки. Кроме того, в ближайшем будущем необходимо будет охватить «tkinter и lambda».