0

I have a Tkinter gui that opens a separate gui with checkboxes. In this separate window, I need to reference the states of the checkboxes, but I get the following error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1470, in __call__
    return self.func(*args)
  File "C:\@ Batch\LMS Python Script\tester3.pyw", line 23, in var_states
    print("FX: %d, FY: %d, FZ: %d, MX: %d, MY: %d, MZ: %d" % (FX_1.get(), FY_1.get(), FZ_1.get(),MX_1.get(), MY_1.get(), MZ_1.get()))
NameError: global name 'FX_1' is not defined

Is it possible to define and reference a global variable from a sub function?

Here is my code:

from Tkinter import *
import Tkinter
import tempfile

root = Tkinter.Tk()
root.title("Generate APDL")
root.geometry("200x225")

Lbl1 = Label(root, text="How many interface points?")
Lbl1.pack(side=TOP,padx=5,pady=5)
Entry1 = Entry(root, bd =1)
Entry1.pack(side=TOP,padx=5,pady=5)

def NewWindow():

    IPoints=int(Entry1.get())
    NumPoints=IPoints+1

    master = Tkinter.Toplevel()
    master.title("Select Unit Load Components")

    def var_states():
        print("FX: %d, FY: %d, FZ: %d, MX: %d, MY: %d, MZ: %d" % (FX_1.get(), FY_1.get(), FZ_1.get(),MX_1.get(), MY_1.get(), MZ_1.get()))

    def CloseWindow():
        master.destroy()

    for i in xrange(1,NumPoints):

        Label(master, text="PT_%d: " % i).grid(row=i,column=0, sticky=W)
        exec('global FX_' + str(i)) in globals(), locals()
        Checkbutton(master, text="FX", variable='FX_%d' % i).grid(row=i,column=2, sticky=W)
        exec('global FY_' + str(i)) in globals(), locals()
        Checkbutton(master, text="FY", variable='FY_%d' % i).grid(row=i,column=3, sticky=W)
        exec('global FZ_' + str(i)) in globals(), locals()
        Checkbutton(master, text="FZ", variable='FZ_%d' % i).grid(row=i,column=4, sticky=W)
        exec('global MX_' + str(i)) in globals(), locals()
        Checkbutton(master, text="MX", variable='MX_%d' % i).grid(row=i,column=5, sticky=W)
        exec('global MY_' + str(i)) in globals(), locals()
        Checkbutton(master, text="MY", variable='MY_%d' % i).grid(row=i,column=6, sticky=W)
        exec('global MZ_' + str(i)) in globals(), locals()
        Checkbutton(master, text="MZ", variable='MZ_%d' % i).grid(row=i,column=7, sticky=W)


    Label(master, text=" ").grid(row=99,column=0, sticky=W)
    Bttn1=Button(master, text='Generate APDL Snippet', command=var_states).grid(row=100,column=0, sticky=W)
    Label(master, text=" ").grid(row=101,column=0, sticky=W)
    Bttn2=Button(master, text='Close Window', command=CloseWindow)
    Bttn2.grid(row=102,column=0, sticky=W)

Bttn1 = Tkinter.Button(root, text ="Open New Window", command = NewWindow)
Bttn1.pack(side = TOP,padx=10,pady=5)

root.mainloop()
7
  • Please post the full traceback of your error, it provides a lot more detail that we can use to help answer. Commented Jul 14, 2015 at 16:09
  • 1
    Also why do you from Tkinter import * and import Tkinter, this seems like an obvious redundancy Commented Jul 14, 2015 at 16:10
  • 1
    What are you doing with exec? Commented Jul 14, 2015 at 16:12
  • I am trying to define global variables based on the index of the for loop, so if I loop through i=1 to 10, I should have global variables FX_1, FX_2, FX_3... etc. The exec command allows me to embed the current index number of the for loop into the variable name. Commented Jul 14, 2015 at 16:25
  • 2
    You should not be using variables named FX_1 et al, you should be using a list. Commented Jul 14, 2015 at 16:33

2 Answers 2

1

This little hack that you're trying to pull with global is something I've never seen before -- Kudos for originality :-), but I wouldn't suggest that you keep going down that road because it's going to be a major pain to get it to work (if it's even possible) and in the long-run maintenance will be even more painful.

Never fear though. There is a better way! Generally in these circumstances, I recommend using a class. With a class, you can carry all of the state that you need along with self. Here's an abbreviated version of your code:

class NewWindowFactory(object):
    def var_states(self):
        print 'FX_1', self.FX_1.get()
    def NewWindow(self):
        for i in xrange(1,NumPoints):
            label = Label(master, text="PT_%d: " % i)
            label.grid(row=i,column=0, sticky=W)
            setattr(self, 'FX_' + str(i), label)
        Button(master, text='Generate APDL Snippet', command=self.var_states).grid(row=100,column=0, sticky=W)

Usage would look something like this:

factory = NewWindowFactory()
Bttn1 = Tkinter.Button(root, text="Open New Window", command = factory.NewWindow)
Bttn1.pack(side = TOP,padx=10,pady=5)

Also note that whenever you have a series of variables Foo1, Foo2, Foo3 ..., you generally want to start thinking about using a different data structure (in this case, you probably actually want a list of checkboxes rather than 10 checkbox variables). For a more in-depth explanation, see Keep data out of your variable names, an execellent article by Ned Batchelder.

An astute reader might notice that there are benefits to be gained by turning the entire application into a class (you can set up the initial UI in __init__). I would definitely recommend that approach, but I didn't write it out to keep this suggestion as simple as possible.

Sign up to request clarification or add additional context in comments.

1 Comment

I cant see how to define the FX_1 variable based on a check box state in your example. Could your clarify this?
0

I was able to get the original code to work. I had to global declarations in the wrong spot... I also had to define the variable first in the main loop. I know there is a more elegant way to do this, but it works.

    for i in xrange(1,100):
        exec('var1_' + str(i) + ' = Tkinter.IntVar()')
        exec('var2_' + str(i) + ' = Tkinter.IntVar()')
        exec('var3_' + str(i) + ' = Tkinter.IntVar()')
        exec('var4_' + str(i) + ' = Tkinter.IntVar()')
        exec('var5_' + str(i) + ' = Tkinter.IntVar()')
        exec('var6_' + str(i) + ' = Tkinter.IntVar()')

    def NewWindow():
        IPoints=int(Entry1.get())
        NumPoints=IPoints+1
        MaxComp=IPoints*6

        master = Tkinter.Toplevel()
        master.title("Select Unit Load Components")

        Label(master, text="Interface Points        ").grid(row=0,column=0, sticky=W)
        Label(master, text="FX").grid(row=0,column=1, sticky=W)
        Label(master, text="FY").grid(row=0,column=2, sticky=W)
        Label(master, text="FZ").grid(row=0,column=3, sticky=W)
        Label(master, text="MX").grid(row=0,column=4, sticky=W)
        Label(master, text="MY").grid(row=0,column=5, sticky=W)
        Label(master, text="MZ").grid(row=0,column=6, sticky=W)

        def var_states():
            exec('global var1_' + str(i)) in globals(), locals()
            exec('global var2_' + str(i)) in globals(), locals()
            exec('global var3_' + str(i)) in globals(), locals()
            exec('global var4_' + str(i)) in globals(), locals()
            exec('global var5_' + str(i)) in globals(), locals()
            exec('global var6_' + str(i)) in globals(), locals()

            print("FX: %d, FY: %d, FZ: %d, MX: %d, MY: %d, MZ: %d" % (var1_15.get(), var2_15.get(), var3_15.get(),var4_15.get(), var5_15.get(), var6_15.get()))

        for i in xrange(1,NumPoints):
            Label(master, text="PT_%d: " % i).grid(row=i,column=0, sticky=W)
            exec('Checkbutton(master, text="  ", variable=var1_' + str(i) + ').grid(row=i,column=1, sticky=W)') in globals(), locals()
            exec('Checkbutton(master, text="  ", variable=var2_' + str(i) + ').grid(row=i,column=2, sticky=W)') in globals(), locals()
            exec('Checkbutton(master, text="  ", variable=var3_' + str(i) + ').grid(row=i,column=3, sticky=W)') in globals(), locals()
            exec('Checkbutton(master, text="  ", variable=var4_' + str(i) + ').grid(row=i,column=4, sticky=W)') in globals(), locals()
            exec('Checkbutton(master, text="  ", variable=var5_' + str(i) + ').grid(row=i,column=5, sticky=W)') in globals(), locals()
            exec('Checkbutton(master, text="  ", variable=var6_' + str(i) + ').grid(row=i,column=6, sticky=W)') in globals(), locals()


        Button(master, text='Show', command=var_states).grid(row=100, sticky=W, pady=4)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.