0
import sys
from tkinter import *

def run_GUI():
    # create the window
    root = Tk()
    frame = Frame(root)
    frame.pack()

    #modify root window
    root.title("Simple GUI")
    root.geometry("700x300") # w x h        
    def SP2T():     # Edit occurs here where I forgot to pit that the button was created in a called funtction
        #Creates Row
        frameSP2T = Frame(root)
        frameSP2T.pack(side = TOP)

        #Creating Buttons First Row
        button1 = Button(frameSP2T, padx=13, pady = 6, bd=4, text="SW02",fg = "black", command = SW02)
        button1.pack(side = LEFT)

    def SW02():
        print("SW02 is on")
        button1["fg"] = "green"

    #Sets up initial boot screen
    #Creates Row
    topframe = Frame(root)
    topframe.pack(side = TOP)

    #Creating Buttons First Row
    buttonA = Button(topframe, padx=13, pady = 6, bd=4, text="SP2T",fg = "black", command = SP2T)
    buttonA.pack(side = LEFT)

    buttonB = Button(topframe, padx=12, pady = 6, bd=4, text="SP4T",fg = "black")
    buttonB.pack(side = LEFT)

    buttonC = Button(topframe, padx=12, pady = 6, bd=4, text="SP12T",fg = "black")
    buttonC.pack(side = LEFT)

    buttonD = Button(topframe, padx=12, pady = 6, bd=4, text="QUIT", fg="red",command=frame.quit)
    buttonD.pack(side = LEFT)

    #Kick off event loop
    root.mainloop()
    return

run_GUI()

And I got the error:

Traceback (most recent call last):

  File "C:\Python34\lib\tkinter\__init__.py", line 1487, in __call__

   return self.func(*args)

   File "C:\Python34\gui_attempt.py", line 25, in SW02

   button1["fg"] = "green"

NameError: name 'button1' is not defined

Updated version which hopefully shows the whole picture

There's more to this program so that's why it says line 60, but this is the problem area. I'm trying to change the button's color when pressed.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
lherron8
  • 13
  • 1
  • 3
  • 2
    Please provide a [minimal example](http://stackoverflow.com/help/mcve) that actually recreates your error - even after adding `from tkinter import *` (which you should avoid doing) I get `NameError: name 'SW02' is not defined`, because the function is defined *after* it is referred to in the `Button` definition, and if I switch them around it runs without error. – jonrsharpe Sep 11 '14 at 15:49
  • With two minor changes (added import, moved function definition before the creation of the `Button`) the code you have posted works for me. So, again, **please provide a minimal example** so that others can see exactly what your problem is. See also http://sscce.org. – jonrsharpe Sep 11 '14 at 16:24
  • I'm sorry. This is my first time posting, so I appreciate patience. – lherron8 Sep 11 '14 at 16:38

2 Answers2

2

The simplest fix is to add global button to the start of the two functions that need access to that object:

def SP2T():
    global button1
    ...

def SW02():
    global button1
    ...

However, the use of global is generally a bad sign - instead, you could take a class-based approach, as in this simple example:

import tkinter as tk

class MyGui(tk.Tk):

    def __init__(self):
        super(MyGui, self).__init__()
        self.create_widgets()


    def create_widgets(self):
        self.frame = tk.Frame(self)
        self.frame.pack()
        self.buttonA = tk.Button(self.frame, padx=13, pady=6, bd=4, text="SP2T",
                                 fg="black", command=self.SP2T)
        self.buttonA.pack(side=tk.LEFT)

    def SW02(self):
        print("SW02 is on")
        self.button1["fg"] = "green"

    def SP2T(self):
        self.button1 = tk.Button(self.frame, padx=13, pady=6, bd=4, text="SW02",
                                 fg="black", command=self.SW02)
        self.button1.pack(side=tk.LEFT)

if __name__ == "__main__":
    root = MyGui()
    root.mainloop()
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 1
    Since this is Python 3.x, he doesn't need `global` here, he could use `nonlocal` and store them in the closure. Technically this is equivalent to creating objects. But of course practically, it's a lot more pythonic to use OO for cases like this, exactly as you show. – abarnert Sep 12 '14 at 06:28
  • This is probably an amazingly stupid question, but I believe I have fixed my code to have a class, but now everything is defined in a function and nothing runs if I have it set up like you do. is the if statement at the bottom suppose run the program? I tried calling create_widgets(self) after defining all my functions, but only my GUI box comes up, no buttons. I'll update my question with what I have now. – lherron8 Sep 15 '14 at 12:40
  • I'm not sure what to do because none of those examples are a class with a def inside a def trying to be run. Unless you are suggesting doing it as a seperate py file and importing it, but I'd rather not go that route. – lherron8 Sep 15 '14 at 13:03
  • @lherron8 don't alter the question, it invalidates the answers. Note, however, that my code does not include functions nested inside the instance methods. You also have numerous `NameError`s. – jonrsharpe Sep 15 '14 at 13:11
0

You're dealing with scope issues. The exception is occurring in the function SW02() because you're trying to modify an object that is not in the function's scope. You need to pass it in. Change SW02() to the following:

def SW02(button1):
    print("SW02 is on")
    button1["fg"] = "green"

Since you're using the function as an action for a command, you have a few options to actually pass it in.

Use a lambda:

button1 = Button(frameSP2T, padx=13, pady = 6, bd=4, text="SW02",fg = "black", command = lambda: SW02(button1))

Use functools.partial:

from functools import partial
# other stuff
_SW02 = partial(SW02, button1)
button = Button(frameSP2T, padx=13, pady = 6, bd=4, text="SW02",fg="black", command=_SW02)

Alternatively, as suggested in jonrsharpe's answer, you could refactor your whole GUI function into a class, and have all of your widgets be class members, then associate your widget actions with class methods instead of plain functions. That way, all of your functions that modify GUI components would have access to them.

Community
  • 1
  • 1
skrrgwasme
  • 9,358
  • 11
  • 54
  • 84
  • Global didn't make a difference. – lherron8 Sep 11 '14 at 16:24
  • I'm sorry about that. I'm also fairly new to python so the combination of new to posting questions and new to python kinda made my original post suck. So: button1 = Button(frameSP2T, padx=13, pady = 6, bd=4, text="SW02",fg = "black", command = SW02(button1)) button1.pack(side = LEFT) def SW02(input_button): print("SW02 is on") button1["fg"] = "green" – lherron8 Sep 11 '14 at 16:47
  • With def SW02(input_button) on a new line of course – lherron8 Sep 11 '14 at 16:47
  • is # other stuff the entirety of my code? or the other imports and def run_GUI():? – lherron8 Sep 11 '14 at 16:50
  • "# other stuff" is everything between the import and the function definition (your existing code that I didn't want to copy into my answer). – skrrgwasme Sep 11 '14 at 16:52
  • @lherron8 I had a typo in the functools.partial example. See edit if you're interested in using that method. – skrrgwasme Sep 11 '14 at 17:38