Search code examples
pythontkintercustomtkinter

Tkinter creates buttons before required arary is edited


import customtkinter
from PIL import Image
from extractData import *

ActiceSpecs = []

root = customtkinter.CTk()
root.geometry("500x350")
root.title("Badcrew raiding tool")
root.iconbitmap("badcrew.ico")
app_width = 500
app_height = 350
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
x = (screen_width / 2) - (app_width / 2)
y = (screen_height / 2) - (app_height / 2)
root.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}')


def login():
    try:
        invalidIDLable.pack_forget()
        eventID = enterEventID.get()
        get_data(eventID)
        correctIDLable.pack(pady=12, padx=10)
        frame_eventpicker.pack_forget()
        mainWindow.pack()
        root.state("zoomed")
    except:
        invalidIDLable.pack(pady=12, padx=10)
        enterEventID.delete(0)

def create_button(frame):
    try:
        my_image = customtkinter.CTkImage(dark_image=Image.open(f"D:\Badcrew\specicons\{Players[i].spec}.png"),size=(50, 50))
        buttons[i] = customtkinter.CTkButton(master=frame, image=my_image, text=Players[i].name, text_color="black", font=("Arial", 16, "bold"), anchor ="w", width=290, height=75, fg_color=("red"),hover="False", border_color="Black", border_width=2, command=lambda k=i: button_function(k))
        buttons[i].pack(pady="2", padx="10")
    except:
        pass

tankCount = 0
healerCount = 0
damageCount = 0
def button_function(count):
    global tankCount
    global healerCount
    global damageCount
    color = buttons[count].cget("fg_color")
    if color == "red":
        if Players[count].role == "Tanks":
            tankCount = tankCount + 1
            tankCountLabel.configure(text=f"{tankCount}/2")
        elif Players[count].role == "Healers":
            healerCount = healerCount + 1
            healerCountLabel.configure(text=f"{healerCount}/4")
        else:
            damageCount = damageCount + 1
            damageCountLabel.configure(text=f"{damageCount}/14")

        buttons[count].configure(fg_color=("green"))
        ActiceSpecs.append(Players[count].spec)

    elif color == "green":
        if Players[count].role == "Tanks":
            tankCount = tankCount - 1
            tankCountLabel.configure(text=f"{tankCount}/2")
        elif Players[count].role == "Healers":
            healerCount = healerCount - 1
            healerCountLabel.configure(text=f"{healerCount}/4")
        else:
            damageCount = damageCount - 1
            damageCountLabel.configure(text=f"{damageCount}/14")
        buttons[count].configure(fg_color=("yellow"))
        ActiceSpecs.remove(Players[count].spec)
    else:
        buttons[count].configure(fg_color=("red"))

frame_eventpicker = customtkinter.CTkFrame(master=root)
frame_eventpicker.pack(pady=20, padx=60, fill="both", expand=True)

EventLabel = customtkinter.CTkLabel(master=frame_eventpicker, text="Event")
EventLabel.pack(pady=12, padx=10)
enterEventID = customtkinter.CTkEntry(master=frame_eventpicker, placeholder_text="Enter Event ID", width=150, justify='center')
enterEventID.pack(pady=12, padx=10)

button = customtkinter.CTkButton(master=frame_eventpicker, text="Confirm", command=login, width=150)
button.pack(pady=12, padx=10)

correctIDLable = customtkinter.CTkLabel(master=frame_eventpicker, text="ID Accepted", text_color="green")
invalidIDLable = customtkinter.CTkLabel(master=frame_eventpicker, text="Invalid ID", text_color="red")


mainWindow = customtkinter.CTkFrame(master=root)

tankFrame = customtkinter.CTkFrame(master=mainWindow)
tankLabelFrame = customtkinter.CTkFrame(master=tankFrame, fg_color="#C69B6D")
tankLabelFrame.pack(padx="10", pady="10", anchor="n")
tankLabel = customtkinter.CTkLabel(master=tankLabelFrame, width=300, text="Tanks", text_color="Black", font=("Arial", 20, "bold"))
tankLabel.pack(padx="10", pady="10")
tankCountLabel = customtkinter.CTkLabel(master=tankLabelFrame, width=300, text="0/2", text_color="Black", font=("Arial", 20, "bold"))
tankCountLabel.pack(padx=10, pady=10, anchor="s")
tankFrame.grid(row=0, column=0, padx="2", pady="10", sticky="n")

healerFrame = customtkinter.CTkFrame(master=mainWindow)
healerLabelFrame = customtkinter.CTkFrame(master=healerFrame, fg_color="#33937F")
healerLabelFrame.pack(padx="10", pady="10", anchor="n")
healerLabel = customtkinter.CTkLabel(master=healerLabelFrame, width=300, text="Healers", text_color="Black", font=("Arial", 20, "bold"))
healerLabel.pack(padx="10", pady="10")
healerCountLabel = customtkinter.CTkLabel(master=healerLabelFrame, width=300, text="0/4", text_color="Black", font=("Arial", 20, "bold"))
healerCountLabel.pack(padx=10, pady=10, anchor="s")
healerFrame.grid(row=0, column=1, padx="2", pady="10", sticky="n")

damageFrame = customtkinter.CTkFrame(master=mainWindow)
damageLabelFrame = customtkinter.CTkFrame(master=damageFrame, fg_color="#C41E3A")
damageLabelFrame.pack(pady="10", anchor="n")
damageLabel = customtkinter.CTkLabel(master=damageLabelFrame, width=900, text="Damage Dealers", text_color="Black", font=("Arial", 20, "bold"))
damageLabel.pack(padx="10", pady="10")
damageCountLabel = customtkinter.CTkLabel(master=damageLabelFrame, width=300, text="0/14", text_color="Black", font=("Arial", 20, "bold"))
damageCountLabel.pack(padx=10, pady=10, anchor="s")
damageFrame.grid(row=0, column=2, padx="2", pady="10", sticky="n")

buttons = dict()
for i in range(len(Players)):
    if Players[i].role == "Tanks":
        create_button(tankFrame)
    elif Players[i].role == "Healers":
        create_button(healerFrame)
    else:
        create_button(damageFrame)

root.mainloop()

get_data() updates the "Players" array

Buttons are created based on the information in "Players"(last section before root.mainloop). I can't find a way for "Players" to get updated before the buttons are created.

I want user to be able to put a string in enterEventID which is used by get_data() to edit the "players" to fit with the new ID.


Solution

  • I think your program has a structure that makes it unnecessarily hard to maintain. The usage of global without need is also discouraged. A structure like this could be a good starting point for you.

    However, I played around with your code a little bit and it's working now. The relevant change was only to put the update of the player array inside the login function. I am also passing the player element to the create_button function, so as to not access the players array from inside the function, which makes for a better separation of concerns.

    import customtkinter
    from PIL import Image
    
    ActiceSpecs = []
    
    root = customtkinter.CTk()
    root.geometry("500x350")
    root.title("Badcrew raiding tool")
    app_width = 500
    app_height = 350
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    x = (screen_width / 2) - (app_width / 2)
    y = (screen_height / 2) - (app_height / 2)
    root.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}')
    
    players = []
    
    class Player:
        def __init__(self, role) -> None:
            self.spec = 'testspec'
            self.name ='testname'
            self.role = role
    
    def get_data(id):
        global players
        players = [
            Player("Tanks"),
            Player("Healers")
        ]
    
    
    def login():
        global players
        try:
            invalidIDLable.pack_forget()
            eventID = enterEventID.get()
            get_data(eventID)
            correctIDLable.pack(pady=12, padx=10)
            frame_eventpicker.pack_forget()
            mainWindow.pack()
            root.state("zoomed")
    
            for i in range(len(players)):
                if players[i].role == "Tanks":
                    create_button(tankFrame, players[i])
                elif players[i].role == "Healers":
                    create_button(healerFrame, players[i])
                else:
                    create_button(damageFrame, players[i])
        except Exception as e:
            print(e)
            invalidIDLable.pack(pady=12, padx=10)
            enterEventID.delete(0, 'end')
    
    def create_button(frame, player):
        try:
            my_image = customtkinter.CTkImage(dark_image=Image.open(f"D:\Badcrew\specicons\{players[i].spec}.png"),size=(50, 50))
            customtkinter.CTkButton(master=frame, text=player.name, text_color="black", font=("Arial", 16, "bold"), anchor ="w", width=290, height=75, fg_color=("red"),hover="False", border_color="Black", border_width=2, command=lambda i: button_function(i)).pack(pady="2", padx="10")
        except:
            print('failed')
            pass
    
    tankCount = 0
    healerCount = 0
    damageCount = 0
    def button_function(count):
        global tankCount
        global healerCount
        global damageCount
        color = buttons[count].cget("fg_color")
        if color == "red":
            if players[count].role == "Tanks":
                tankCount = tankCount + 1
                tankCountLabel.configure(text=f"{tankCount}/2")
            elif players[count].role == "Healers":
                healerCount = healerCount + 1
                healerCountLabel.configure(text=f"{healerCount}/4")
            else:
                damageCount = damageCount + 1
                damageCountLabel.configure(text=f"{damageCount}/14")
    
            buttons[count].configure(fg_color=("green"))
            ActiceSpecs.append(players[count].spec)
    
        elif color == "green":
            if players[count].role == "Tanks":
                tankCount = tankCount - 1
                tankCountLabel.configure(text=f"{tankCount}/2")
            elif players[count].role == "Healers":
                healerCount = healerCount - 1
                healerCountLabel.configure(text=f"{healerCount}/4")
            else:
                damageCount = damageCount - 1
                damageCountLabel.configure(text=f"{damageCount}/14")
            buttons[count].configure(fg_color=("yellow"))
            ActiceSpecs.remove(players[count].spec)
        else:
            buttons[count].configure(fg_color=("red"))
    
    frame_eventpicker = customtkinter.CTkFrame(master=root)
    frame_eventpicker.pack(pady=20, padx=60, fill="both", expand=True)
    
    EventLabel = customtkinter.CTkLabel(master=frame_eventpicker, text="Event")
    EventLabel.pack(pady=12, padx=10)
    enterEventID = customtkinter.CTkEntry(master=frame_eventpicker, placeholder_text="Enter Event ID", width=150, justify='center')
    enterEventID.pack(pady=12, padx=10)
    
    button = customtkinter.CTkButton(master=frame_eventpicker, text="Confirm", command=login, width=150)
    button.pack(pady=12, padx=10)
    
    correctIDLable = customtkinter.CTkLabel(master=frame_eventpicker, text="ID Accepted", text_color="green")
    invalidIDLable = customtkinter.CTkLabel(master=frame_eventpicker, text="Invalid ID", text_color="red")
    
    
    mainWindow = customtkinter.CTkFrame(master=root)
    
    tankFrame = customtkinter.CTkFrame(master=mainWindow)
    tankLabelFrame = customtkinter.CTkFrame(master=tankFrame, fg_color="#C69B6D")
    tankLabelFrame.pack(padx="10", pady="10", anchor="n")
    tankLabel = customtkinter.CTkLabel(master=tankLabelFrame, width=300, text="Tanks", text_color="Black", font=("Arial", 20, "bold"))
    tankLabel.pack(padx="10", pady="10")
    tankCountLabel = customtkinter.CTkLabel(master=tankLabelFrame, width=300, text="0/2", text_color="Black", font=("Arial", 20, "bold"))
    tankCountLabel.pack(padx=10, pady=10, anchor="s")
    tankFrame.grid(row=0, column=0, padx="2", pady="10", sticky="n")
    
    healerFrame = customtkinter.CTkFrame(master=mainWindow)
    healerLabelFrame = customtkinter.CTkFrame(master=healerFrame, fg_color="#33937F")
    healerLabelFrame.pack(padx="10", pady="10", anchor="n")
    healerLabel = customtkinter.CTkLabel(master=healerLabelFrame, width=300, text="Healers", text_color="Black", font=("Arial", 20, "bold"))
    healerLabel.pack(padx="10", pady="10")
    healerCountLabel = customtkinter.CTkLabel(master=healerLabelFrame, width=300, text="0/4", text_color="Black", font=("Arial", 20, "bold"))
    healerCountLabel.pack(padx=10, pady=10, anchor="s")
    healerFrame.grid(row=0, column=1, padx="2", pady="10", sticky="n")
    
    damageFrame = customtkinter.CTkFrame(master=mainWindow)
    damageLabelFrame = customtkinter.CTkFrame(master=damageFrame, fg_color="#C41E3A")
    damageLabelFrame.pack(pady="10", anchor="n")
    damageLabel = customtkinter.CTkLabel(master=damageLabelFrame, width=900, text="Damage Dealers", text_color="Black", font=("Arial", 20, "bold"))
    damageLabel.pack(padx="10", pady="10")
    damageCountLabel = customtkinter.CTkLabel(master=damageLabelFrame, width=300, text="0/14", text_color="Black", font=("Arial", 20, "bold"))
    damageCountLabel.pack(padx=10, pady=10, anchor="s")
    damageFrame.grid(row=0, column=2, padx="2", pady="10", sticky="n")
    
    root.mainloop()