Как изменить текст кнопки, нажатой в другом классе? (Python - ткинтер) - PullRequest
1 голос
/ 20 апреля 2020

Введение

Я пытаюсь создать приложение GUI с tkinter в python, которое имеет 2 кадра, которые связаны друг с другом. Общая идея заключается в создании меню выбора персонажей (как в играх). Все шло хорошо, пока не дошло до изменения текста кнопок разных классов.

Голы

  • Первый кадр , обозначенный классом A , будет главной страницей приложения и должен отображать на экране 10 кнопок;

  • Второй кадр, представленный класс B , будет отображаться пользователю только при нажатии любой кнопки в классе A;

  • В классе B должен отображаться список кнопок. При нажатии любой из кнопок класса B текст этой кнопки должен быть передан в текст кнопки класса A.

Подробная информация о проекте

  • Это приложение должно работать так, как если бы это было меню выбора игрового персонажа. Воображая таким образом, мы обычно видим этот тип взаимодействия между кадрами;

  • Существует экран, который показывает количество возможных игроков для каждой игры (В случае этого приложения кнопки класса А будет представлять это), и есть экран, который показывает все символы, доступные для выбора (кнопки класса В);

  • В кадре класса А будет Параметры игрока: «Игрок 1, Игрок 2, Игрок 3 ...». Поэтому при нажатии на одну из этих кнопок (игрок 1, игрок 2, игрок 3 ...) должно отображаться окно, в котором отображаются все символы (класс B);

  • При выборе требуемый символ (нажав на одну из кнопок класса B), выбранный символ должен быть передан на главный экран и показан на кнопке, выбранной в классе A. Поскольку я еще не использую изображения, я хочу представить символы с помощью текст кнопок;

  • Так что, если я нажимаю на «Игрок 1» на главном экране, а затем выбираю «Персонаж 4» на экране выбора, текст для «Игрок» 1 "следует изменить на" Символ 4 "на главном экране и т. Д .;

Generi c Код

Я сделал обобщенное c представление о том, как я строю программу и подробно о том, как я хотел, чтобы она работала.

import tkinter as tk


# Creates the main window
class A(tk.Frame):
    """The class A frame is the main page of the application,
     when running the program, it will be the first thing shown to the user."""
    def __init__(self, master):
        tk.Frame.__init__(self, master)

        self.bt_identities_a = []  # this list will be used to save the identities of each button created in class A

        # Creates multiple buttons
        for i in range(10):
            self.bt_a = tk.Button(self, text=f"Player  A{i}", command=lambda x=i: self.open_window_of_class_b(x))
            self.bt_a.grid()

            self.bt_identities_a.append(self.bt_a)  # adds the button identity to the list

    def open_window_of_class_b(self, n):
        """This is the method responsible for executing class B
            and for recognizing which button was clicked in class A

           All actions to be performed by the buttons in class B
            from now on must be associated with exactly that one
            button that was clicked in class A.
        """
        # Run class B
        B()

        # get the button id that was clicked
        bt_id = self.bt_identities_a[n]
        ...


# Creates the secondary window
class B(tk.Toplevel):
    """The class B frame is a secondary page that will only be opened if one of the Class A buttons is clicked."""
    def __init__(self):
        tk.Toplevel.__init__(self)

        self.bt_identities_b = []  # this list will be used to save the identities of each button created in class B

        # Creates multiple buttons
        for j in range(10):
            self.bt_b = tk.Button(self, text=f"Character B{j}",
                                  command=lambda x=j: self.changes_the_text_of_a_button_in_class_a(x))
            self.bt_b.grid()

            self.bt_identities_b.append(self.bt_b)  # adds the button identity to the list

    def changes_the_text_of_a_button_in_class_a(self, n):
        """This method should recognize which of the Class B buttons that was clicked,
           take the text from this exact button and pass the text to the Class A button
           that was clicked just before."""

        # get the button id that was clicked
        bt_id = self.bt_identities_b[n]
        ...


root = tk.Tk()
root.geometry("300x300")

app = A(root)
app.pack(fill="both", expand=True)
app.mainloop()

Мой реальный код

И вот полный код, который я сделал так далеко от своего приложения, на случай, если это необходимо.

import tkinter as tk
from itertools import product


# Creating main page
class MainApplication(tk.Frame):
    def __init__(self, master, *args, **kwargs):
        tk.Frame.__init__(self, master, *args, **kwargs)

        # produce the set of coordinates of the main page buttons
        self.row_amount = 2
        self.column_amount = 5
        self.main_positions = product(range(self.row_amount), range(self.column_amount))
        self.main_buttons_identities = []

        # Creating main page header
        self.lb = tk.Label(self, width=111, height=4, bg="#2c3e50", text="Champions", fg="white", font=50,
                           justify=tk.CENTER)
        self.lb.grid(row=0, column=0, columnspan=5, pady=(0, 50), sticky="snew")

        # Creating Done button
        self.button = tk.Button(self, width=30, height=3, bg="#2c3e50", relief=tk.RIDGE, text="Done",
                                fg="white", font=20, command=root.destroy)
        self.button.grid(row=3, columnspan=5, pady=(0, 150))

        # Creating multiple buttons
        for i, item in enumerate(self.main_positions):
            self.button_main = tk.Button(self, width=16, height=8, bg="#2c3e50", relief=tk.RIDGE, fg="white",
                                         justify=tk.CENTER, text=f"Champion {i +1}",
                                         command=lambda c=i: [ChampionWindow(), self.clicked_main(c)])
            self.button_main.grid(row=item[0] + 1, column=item[1], pady=(0, 50))

            self.main_buttons_identities.append(self.button_main)

    def clicked_main(self, current_index):
        current = self.main_buttons_identities[current_index]

        print(current["text"])


# Creating champion select window
class ChampionWindow(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)

        # produce the set of coordinates of the char selection page buttons
        self.row_amount = 30
        self.column_amount = 5
        self.champion_position = product(range(self.row_amount), range(self.column_amount))
        self.champions_buttons_identities = []

        # scroll bar
        self.ch_canvas = tk.Canvas(self, bg="blue", width=470, height=500)
        self.ch_frame = tk.Frame(self.ch_canvas, bg="#273c75")

        self.vscrollbar = tk.Scrollbar(self, orient="vertical", command=self.ch_canvas.yview)

        self.ch_canvas.configure(yscrollcommand=self.vscrollbar.set)
        self.ch_canvas.grid(sticky="snew")

        self.vscrollbar.grid(row=0, column=3, sticky="sn")

        self.ch_canvas.create_window((0, 0), window=self.ch_frame, anchor="nw")
        self.ch_frame.bind("<Configure>", self.scroll)

        # Creating multiple buttons
        for i, itm in enumerate(self.champion_position):
            self.button_champion = tk.Button(self.ch_frame, width=12, height=6, bg="#2c3e50",
                                             relief=tk.RIDGE, fg="white", justify=tk.CENTER,
                                             command=lambda c=i: [self.clicked_champion(c), self.destroy()],
                                             text=f"Pick champ {i+1}")
            self.button_champion.grid(row=itm[0], column=itm[1])

            self.champions_buttons_identities.append(self.button_champion)

    def scroll(self, ch_event):
        self.ch_canvas.configure(scrollregion=self.ch_canvas.bbox("all"))

    def clicked_champion(self, champ_index):
        champ = self.champions_buttons_identities[champ_index]
        print(champ["text"])


if __name__ == "__main__":
    root = tk.Tk()
    root.title("Champion")
    root.geometry("1000x570+450+200")
    root.resizable(False, False)

    app = MainApplication(root)
    app.configure(background="#34495e")
    app.pack(fill="both", expand=True)
    app.mainloop()

GUI images

Чтобы было проще понять, что я пытаюсь сделать, я свяжу изображения из главного окна и выбор персонажа окно.

Главное окно (Отображает игроков)

Окно выбора персонажа (Отображает доступные символы)

Ответы [ 2 ]

1 голос
/ 20 апреля 2020

Вы можете просто передать экземпляр нажатой кнопки классу ChampionWindow:

class MainApplication(tk.Frame):
    def __init__(self, master, *args, **kwargs):

        ...

        # Creating multiple buttons
        for i, item in enumerate(self.main_positions):
            button_main = tk.Button(self, width=16, height=8, bg="#2c3e50", relief=tk.RIDGE, fg="white",
                                    justify=tk.CENTER, text=f"Champion {i +1}",
                                    command=lambda c=i: self.clicked_main(c))
            button_main.grid(row=item[0] + 1, column=item[1], pady=(0, 50))
            self.main_buttons_identities.append(button_main)

    def clicked_main(self, current_index):
        current = self.main_buttons_identities[current_index]
        print(current["text"])
        ChampionWindow(current) # pass clicked button to ChampionWindow

Затем обновить текст переданной кнопки в ChampionWindow при нажатии одной из ее кнопок:

# Creating champion select window
class ChampionWindow(tk.Toplevel):
    def __init__(self, button, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)
        self.button = button   # save the button for later use

        ...    

    def clicked_champion(self, champ_index):
        champ = self.champions_buttons_identities[champ_index]
        print(champ["text"])
        self.button["text"] = champ["text"]  # update passed button
0 голосов
/ 20 апреля 2020

вы можете попробовать что-то вроде этого. Вы можете применить изменения к переменным класса извне после инициализации класса.

Вот простой пример.

class A():
    def __init__(self):
        self.champ = None

    def get_name(self):
        B()
    def test(self):
        print(self.champ)
class B():
    def __init__(self):
        print("Assingin name")
        f.champ = "Hero1"
        return
if __name__ == "__main__":
    f = A()
    f.test()
    f.get_name()
    f.test()

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

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

...