I'm trying to make my button change on and off whenever the user clicks on it. But I keep getting a name error. I've tried changing the order of the function and button creation. If I place the button creation above the function then it won't be able to call the function.
Here is my Code:
#import all of tkinter and also updated widgets
from tkinter import *
from tkinter import ttk
import main
#sets up main application window
root = Tk()
root.title("Winstreak Counter GUI")
#creating a content frame (frame widget, hold content of user interface)
#we do this because it'll allow us to control the background color and allow for more themed widgets
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=10, row=10, sticky=(N, W, E, S))
root.columnconfigure(10, weight=1)
root.rowconfigure(10, weight=1)
f = open('/Users/ripkoye/Desktop/WinstreakProject/winstreak.txt', 'r')
#store the information into a variable
winstreak = StringVar(mainframe)
winstreak.set(f.read())
#creates a winstreaklabel
winstreaklabel = ttk.Label(mainframe,width=20, padding='5 5 5 5')
winstreaklabel.grid()
winstreaklabel['textvariable'] = winstreak
#assigns the text to the variable named winstreak
f.close()
is_on = True
def switch():
global is_on
if is_on:
buttonOnOff.config(image = off)
is_on = False
else:
buttonOnOff.config(image = on)
is_on = True
on = PhotoImage(file='on.png')
off = PhotoImage(file='off.png')
buttonOnOff = ttk.Button(mainframe, image=on, width=0.5, padding='5 5 5 5', command=switch())
buttonOnOff.grid()
root.mainloop()
This is the error:
Traceback (most recent call last):
File "/Users/ripkoye/Desktop/WinstreakProject/gui.py", line 42, in <module>
buttonOnOff = ttk.Button(mainframe, image=on, width=0.5, padding='5 5 5 5', command=switch())
File "/Users/ripkoye/Desktop/WinstreakProject/gui.py", line 34, in switch
buttonOnOff.config(image = off)
NameError: name 'buttonOnOff' is not defined
CodePudding user response:
You calling the switch
function directly, but you need to give callable object to command
parameter.
buttonOnOff
will be created when ttk.Button
return. But before the ttk.Button
return, switch
func will be called because you do command=switch()
.
You need to modify it like ;
buttonOnOff = ttk.Button(mainframe, image=on, width=0.5, padding='5 5 5 5', command=switch)
CodePudding user response:
@VeyselOlgun is partly correct, you'll run into an issue with buttonOnOff
's command
parameter - you should change it from switch()
to switch
However, the issue you're having is caused by the fact that buttonOnOff
is outside the scope of your switch()
function.
A clean solution is to wrap your button and it's associated command callback in a simple class like so
#import all of tkinter and also updated widgets
import tkinter as tk # star imports are not your friend!
from tkinter import ttk
import main
#sets up main application window
root = tk.Tk() # properly namespaced w/ 'tk.' prefix
root.title("Winstreak Counter GUI")
#creating a content frame (frame widget, hold content of user interface)
#we do this because it'll allow us to control the background color and allow for more themed widgets
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=10, row=10, sticky=(N, W, E, S))
root.columnconfigure(10, weight=1)
root.rowconfigure(10, weight=1)
wsp_path = '/Users/ripkoye/Desktop/WinstreakProject/winstreak.txt'
#store the information into a variable
winstreak_var = tk.StringVar(mainframe)
with open(wsp_path, 'r') as wsp:
winstreak_var.set(wsp.read())
#creates a winstreaklabel
winstreaklabel = ttk.Label(
mainframe,
textvariable=winstreak_var, # now you can set textvariable here
width=20,
padding='5 5 5 5'
)
winstreaklabel.grid()
ButtonOnOff(mainframe) # call your button class with mainframe as parent
# I like to inherit from tk.Frame as my generic base class
class ButtonOnOff(tt.Frame):
def __init__(self, parent):
super().__init__(parent) # init the container frame
# 'parent' is set to 'mainframe' when you init the class
self.is_on = True
self.on = tk.PhotoImage(file='on.png')
self.off = tk.PhotoImage(file='off.png')
# create your button
self.buttonOnOff = ttk.Button(
self, # the button is now a child of the ttk.Frame container
image=self.on,
width=0.5,
padding='5 5 5 5',
command=self.switch # bind to the 'switch' method below
)
button.grid() # populate the button
def switch(self):
if self.is_on:
self.buttonOnOff.config(image=self.off)
self.is_on = False
else:
self.buttonOnOff.config(image=self.on)
self.is_on = True
Edit - I took the liberty of cleaning things up a bit using a context manager to handle the reading of winstreak.txt
. This handles opening, reading, and closing for you in one fell swoop!
Another Edit - I've gotten rid of the star import in favor of import tkinter as tk
and properly namespaced the necessary classes with tk.
. See here for more info on why star imports can be a headache