1

I created a Tkinter window. In one frame, I use matplotlib to just plot some text (I use matplotlib because I need greek math charakters). I do have more than 10 variables and two buttons for each variable which change the value of it. So, now I thought instead of have 28 functions to change the values, I could write only two functions, which change the desired variable using exec(). But that does not work...

Previous attempt for variable a:

def ap():
    global a
    a+=10
    plot()
    canvas.draw()

def am():
    global a
    a-=10
    plot()
    canvas.draw()

button_ap = Tk.Button(configframe, text='a+', command=ap).grid(row=0,column=0)
button_am = Tk.Button(configframe, text='a-', command=am).grid(row=0,column=1)

This works. As soon as I press one of the buttons, the plot is updated with the new value of a.

New attempt:

def parp(var):
    exec('global '+var)
    exec(var+'+=10')
    plot()
    canvas.draw()

def parm(var):
    exec('global '+var)
    exec(var+'-=10')
    plot()
    canvas.draw()

button_ap = Tk.Button(configframe, text='a+', command=lambda: parp('a')).grid(row=0,column=0)
button_am = Tk.Button(configframe, text='a-', command=lambda: parm('a')).grid(row=0,column=1)

This does not work. It actually does read the variable and executes 'var+=10', because if I print the variable afterwards, it is reduced by 10. But the plot() command does not update the plot.

Do you have any idea why? Thx.

Pythoneer
  • 165
  • 1
  • 12
  • You don't need matplotlib to use Greek characters. You can just use unicode, e.g.: `tk.Label(text = u"\N{GREEK SMALL LETTER ALPHA}") ` (or the actual char if your editor supports it, or the actual unicode number) – user812786 Jul 28 '17 at 15:17
  • rather than making lots of functions, you could make each variable show in a spinbox widget, and after an update to any spinbox re plot and draw. i believe you can set the step value of a spinbox widget (how much it changed when you press the arrow buttons) – James Kent Jul 28 '17 at 15:22
  • Thanks, but thats not the point :) I got used to using matplotlib, I have set background color, fontsize, invisible if needed and so on. I just need to solve the other thing. – Pythoneer Jul 28 '17 at 15:22

1 Answers1

0

To be able to manipulate global variables with exec in a function we need to provide global() as a 2nd argument to exec.

Here is how you would use ecec on globals():

a = 0
b = 10
c = 20

def parp(my_var):
    exec("{} -= 10".format(my_var), globals())

parp("a")
parp("b")
parp("c")

print(a)
print(b)
print(c)

One other thing. Avoid using + or % for combining strings. Instead use format() as its the current preferred method for this function.

Because myself and others would say "Globals are bad MKAY!".

Here is a good link with an explanation as to why.

Here is a class that does the same thing but with the use of class attributes so we do not need to use globals.

class ExecClass():


    def __init__(self):
        self.a = 0
        self.b = 10
        self.c = 20

        self.parp("a")
        self.parp("b")
        self.parp("c")

        print(self.a, self.b, self.c)

    def parp(self, my_var):
        exec("self.{} -= 10".format(my_var))


my_class = ExecClass()
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • Awesome. It works nicely! Thank you! Regarding format: so, is {} then the placeholder for the argument of format, just like % would be? – Pythoneer Jul 28 '17 at 15:30
  • Yes. You may also you selectors within `{}`. For example if I had this string: `print({3} and {1} and {2}.format( "first", "second", "third")` it would output: `third and first and second`. – Mike - SMT Jul 28 '17 at 15:32
  • @Pythoneer if you just leave it empty like `{}` it would replace in order from left to right. – Mike - SMT Jul 28 '17 at 15:34
  • 1
    Thank you. Learned two new things today :) – Pythoneer Jul 28 '17 at 15:35
  • @Pythoneer I have added a class example for you as well because it is preferred to avoid globals if you can. – Mike - SMT Jul 28 '17 at 15:43
  • I started a class version of the entire program, but it sucked writing so much "self." :D, because, as far as I understand it, every frame, every widget, every button has to be declared with a "self.". I actually prefer globals just because its easier. But I guess at one point I will need to get used to doing it in classes. – Pythoneer Jul 28 '17 at 15:47
  • @Pythoneer: You do not need make every variable a class attribute with `self.`. Only the ones that are needed to be interacted with inside of different methods or ones that need to be updated from time to time from outside the class or within a method of the class. – Mike - SMT Jul 28 '17 at 15:52