Search code examples
pythontkinterglobal-variables

Tkinter: changes made to global variable don't stay


Whenever I press on one of the buttons created, the procedure that's run does initially change the values of the global variables, but once the procedures finish execution they for some reason revert back. Here's the code:

from tkinter import *

transformEnabled,selectionEnabled,paintEnabled = False,False,False

root = Tk()

def hide(tool,toolkit):
    for i in toolkit:
        i.grid_forget()
    tool=False
def toolkitFunctions(tool1,tool2,tool3,toolkit1,toolkit2,toolkit3):
    print(tool1)
    if tool1 == False:
        print("Before other tools set to false: ",str(tool1))
        if tool2:
            hide(tool2,toolkit2)
        if tool3:
            hide(tool3,toolkit3)
        for i in range(0,len(toolkit1)):
            toolkit1[i].grid(row=i+4,column=0)
        tool1=True
        print("After other tools set: ",str(tool1))
    else:
        print("If tool1 already true: ",str(tool1))
        hide(tool1,toolkit1)

#Transform Buttons
transformNames = ["Scale Image","Rotate Image","Move Image","Flip Image","Sharpen/Soften Image","Brightness/Constrast"]
transformButtons = [Button(root,text=transformNames[i],width=20) for i in range(0,len(transformNames))]
#SelectionButtons
selectionNames = ["Regular Shape Selection","Free hand Tool","Colour Picker"]
selectionButtons = [Button(root,text=selectionNames[i],width=20) for i in range(0,len(selectionNames))]
#Paint Buttons
paintNames = ["Paintbrush","Airbrush","Pencil","Eraser","Fill","Smudge","Dodge and Burn"]
paintButtons = [Button(root,text=paintNames[i],width=20) for i in range(0,len(paintNames))]

def transformFunctions():
    toolkitFunctions(transformEnabled,selectionEnabled,paintEnabled,transformButtons,selectionButtons,paintButtons)
def selectionFunctions():
    toolkitFunctions(selectionEnabled,transformEnabled,paintEnabled,selectionButtons,transformButtons,paintButtons)
def paintFunctions():
    toolkitFunctions(paintEnabled,transformEnabled,selectionEnabled,paintButtons,transformButtons,selectionButtons)

transform = Button(root,text="Transform Tools",width=20,command=transformFunctions).grid(row=3,column=0)
selection = Button(root,text="Selection Tools",width=20,command=selectionFunctions).grid(row=2,column=0)
paint = Button(root,text="Paint tools",width=20,command=paintFunctions).grid(row=1,column=0)

root.mainloop()

Is there anything that can be done to rectify this?


Solution

  • You send value from global variable transformEnabled to function but for string/int/float/boolean Python doesn't keep reference to original variable but it copies values to new variable.

    You could use global transformEnabled in function and change directly transformEnabled = True to assign value to global variable. But your complex is too code for this simple method.

    In your code it is better to keep values in global dictionary and send strings "transform", "selection", "paint" and use them to access data in this global dictionary.

    It is big change in code but you need it. Eventually you could try to use classes to create widget which keep buttons and values for one type of functions. You could also use Frame to group buttons and show/hide this frame.


    I changed other things in code - based on PEP 8 -- Style Guide for Python Code

    import tkinter as tk
    
    # --- functions --- (lower_case_names)
    
    def hide(toolkit):
        for item in toolkit:
            item.grid_forget()
    
    def show(toolkit):
        for i, item in enumerate(toolkit):
            item.grid(row=i+4, column=0) # maybe len(settings) instead of `4` so it will be works when you add more data in dictionary.
    
    def on_click(name):
    
        print('--- before ---')
        for key, value in settings.items():
            print('{} enabled: {}'.format(key, value['enabled']))
    
        print('--- changes ---')
        if not settings[name]['enabled']:
            # hide other buttons
            for key, value in settings.items():
                print("Check:", key, key != name, value['enabled'])
                if key != name and value['enabled']:
                    hide(value['buttons'])
                    value['enabled'] = False
    
            # show buttons for `name`
            print("Show:", name)
            show(settings[name]['buttons'])
            settings[name]['enabled'] = True
        else:
            # hide buttons for `name`
            print("Hide:", name)
            hide(settings[name]['buttons'])
            settings[name]['enabled'] = False
    
    # --- main --- (lower_case_names)
    
    settings = {
        'transform': {
            'enabled': False,
            'names': ["Scale Image", "Rotate Image", "Move Image", "Flip Image", "Sharpen/Soften Image", "Brightness/Constrast"],
            'buttons': [],
        },
        'selection': {
            'enabled': False,
            'names': ["Regular Shape Selection", "Free hand Tool", "Colour Picker"],
            'buttons': [],
        }, 
        'paint': {
            'enabled': False,
            'names': ["Paintbrush", "Airbrush", "Pencil", "Eraser", "Fill", "Smudge", "Dodge and Burn"],
            'buttons': [],
        },
    }    
    
    root = tk.Tk()
    
    settings['transform']['buttons'] = [tk.Button(root, text=item, width=20) for item in settings['transform']['names']]
    
    # Selection Buttons
    settings['selection']['buttons'] = [tk.Button(root, text=item, width=20) for item in settings['selection']['names']]
    
    # Paint Buttons
    settings['paint']['buttons'] = [tk.Button(root, text=item, width=20) for item in settings['paint']['names']]
    
    transform = tk.Button(root, text="Transform Tools", width=20, command=lambda:on_click('transform'))
    transform.grid(row=3, column=0)
    
    selection = tk.Button(root, text="Selection Tools", width=20, command=lambda:on_click('selection'))
    selection.grid(row=2, column=0)
    
    paint = tk.Button(root, text="Paint Tools", width=20, command=lambda:on_click('paint'))
    paint.grid(row=1, column=0)
    
    root.mainloop()
    

    EDIT: The same code but buttons are create with loop. And I use len(settings) instead 4 to remove buttons - this way I could add Help menu to dictionary without changing other code.

    import tkinter as tk
    
    # --- functions --- (lower_case_names)
    
    def hide(toolkit):
        for item in toolkit:
            item.grid_forget()
    
    def show(toolkit):
        for i, item in enumerate(toolkit):
            item.grid(row=i+len(settings), column=0)
    
    def on_click(name):
    
        print('--- before ---')
        for key, value in settings.items():
            print('{} enabled: {}'.format(key, value['enabled']))
    
        print('--- changes ---')
        if not settings[name]['enabled']:
            # hide other buttons
            for key, value in settings.items():
                print("Check:", key, key != name, value['enabled'])
                if key != name and value['enabled']:
                    hide(value['buttons'])
                    value['enabled'] = False
    
            # show buttons for `name`
            print("Show:", name)
            show(settings[name]['buttons'])
            settings[name]['enabled'] = True
        else:
            # hide buttons for `name`
            print("Hide:", name)
            hide(settings[name]['buttons'])
            settings[name]['enabled'] = False
    
    # --- main --- (lower_case_names)
    
    settings = {
        'transform': {
            'title': "Transform Tools",
            'enabled': False,
            'names': ["Scale Image", "Rotate Image", "Move Image", "Flip Image", "Sharpen/Soften Image", "Brightness/Constrast"],
            'buttons': [],
        },
        'selection': {
            'title': "Selection Tools",
            'enabled': False,
            'names': ["Regular Shape Selection", "Free hand Tool", "Colour Picker"],
            'buttons': [],
        }, 
        'paint': {
            'title': "Paint Tools",
            'enabled': False,
            'names': ["Paintbrush", "Airbrush", "Pencil", "Eraser", "Fill", "Smudge", "Dodge and Burn"],
            'buttons': [],
        },
        'help': {
            'title': "Help",
            'enabled': False,
            'names': ["Help ...", "Home Page", "About"],
            'buttons': [],
        },
    }    
    
    root = tk.Tk()
    
    for i, name in enumerate(['paint', 'selection', 'transform', 'help']): # it could be `enumerate(settings.keys())` but sometimes it may not keep order.
        settings[name]['buttons'] = [tk.Button(root, text=item, width=20) for item in settings[name]['names']]
        button = tk.Button(root, text=settings[name]['title'], width=20, command=lambda arg=name:on_click(arg))
        button.grid(row=i, column=0)
    
    root.mainloop()