Global variable isn't being recognized across functions in python with tkinter

41 views Asked by At

So I'm pretty new to python so this could be an easy issue that I just don't understand what's going on here. But essentially I have a dropdown that allows you to choose a character, and then display the corresponding character image. And it works fine for the first time but the issue is that I'm trying to clear the image after a new one is chosen and I keep getting an error saying "UnboundLocalError: cannot access local variable 'imageAppear' where it is not associated with a value"

This is confusing to me because I set 'imageAppear' as a global variable so I don't understand why it thinks it's a local variable.

Currently this code will just display the new image underneath the old image.

I have attached the code below and any help would be greatly appreciated

### MAIN WINDOW ###

# Using tkinter version 8.6
# Visit https://docs.python.org/3/library/tkinter.html for documentation
# Using pack as the LayoutManager, 

import tkinter as tk
import tkinter.ttk as ttk
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk
import pathlib
from pathlib import Path

def main():
    root = tk.Tk()
    root.geometry('1280x760')
    root.title("Slippi Stats")
    title = Label(root, text = "Slippi Stats", font = ('Helvetica 20 bold')).pack(pady = 20)
    
    # Create a File Explorer label
    global label_file_explorer
    label_file_explorer = Label(root, 
                                text = "File Explorer using Tkinter",
                                width = 100, height = 4)
    
    # Select Character
    characters = ["Mario", "Bowser", "Peach", "Yoshi", "Donkey Kong",
                  "Captain Falcon", "Fox", "Ness", "Ice Climbers",
                  "Kirby", "Samus", "Zelda", "Link", "Pikachu",
                  "Jigglypuff", "Dr. Mario", "Luigi", "Ganondorf",
                  "Falco", "Young Link", "Pichu", "Mewtwo",
                  "Mr. Game & Watch", "Marth", "Roy"]
    global selectedCharacter
    global label1
    label1 = Label(root, image = "")
    global imageAppear
    imageAppear = True
    selectedCharacter = tk.StringVar(master=root) #Always pass the 'master' keyword argument
    selectedCharacter.set("Mario")
    selectedCharacter.trace_add('write', characterImage)
    characterLabel = tk.Label(root, text="Select a character to train against")
    characterLabel.pack(pady=10)
    dropdown = tk.OptionMenu(root, selectedCharacter, *characters)
    dropdown.pack()
    
    button_explore = Button(root, 
                            text = "Browse Files",
                            command = browseFiles)
    label_file_explorer.pack(pady=10)
    button_explore.pack(pady=10)
    quitButton = tk.Button(text="Quit", command=root.destroy)
    quitButton.pack(pady = 100)
    root.mainloop()

def characterImage(*args):
    ##if imageAppear == True:
        ##clearImage()
    path = pathlib.Path(__file__).parent.resolve()
    path = path._str + "\\CharacterImages\\{}.jpg".format(selectedCharacter.get())
    # path = path.replace("\\\\","\\")
    path = Path(path)
    characterImage = Image.open(path)
    test = ImageTk.PhotoImage(characterImage)
    label1 = tk.Label(image=test)
    label1.image = test
    # Position image
    label1.pack()
    imageAppear = True

def clearImage():
    label1.config(image = "")

def browseFiles():
    filename = filedialog.askopenfilename(initialdir = "/",
                                          title = "Select a File",
                                          filetypes = (("Slippi Files",
                                                        "*.slp*"),
                                                       ("All files",
                                                        "*.*")))
    label_file_explorer.configure(text="File Opened: "+filename)
main()

The imageAppear check is commented out because it won't even get to display one image with the check currently

P.S. I am aware using global variables is bad coding practice, no need to tear me to shreds for using them.

2

There are 2 answers

0
Cosmow On

Maybe you should try to turn this app into an object oriented approach, so your global can be replaced by 'self'. Maybe your problem is not the global but it makes the code a little bit messy.

0
Jan_B On

To understand global better have a look at the examples here:

The basic rules for global keyword in Python are:

  • When we create a variable inside a function, it is local by default.
  • When we define a variable outside of a function, it is global by default. You don't have to use the global keyword.
  • We use the global keyword to read and write a global variable inside a function.
  • Use of the global keyword outside a function has no effect.

This means setting global imageAppear inside main works just fine but inside characterImage you have to use global aigain, because else the program thinks it is a new variable within the scope of characterImage alone:

def main():
    one()
    two()


def one():
    global one
    one = 1


def two():
    #global one  # whith this commented the code will fail with UnboundLocalError
    one += 1
    print(one)


if __name__ == '__main__':
    main()

As far as tkinter projects are concerned, I am a big fan of OOP and I think you should definitely take a look at it. Your code is quite easy to translate in this respect: Change def main() to class Main and then parenthesise the code again in def __init__(). Then you can simply declare the global variables as instance variables with self. as a prefix. If you then indent the other functions once and first pass them the argument self so that they are assigned to the class Main, you can access them with self.imageAppear, for example.