0

I am creating a GUI using a class object for the window. This window has quite a few buttons to turn on and off different panels of settings. A simple example of my class is something like:

import tkinter as tk 

class MyWindow(object):
    def __init__(self):
        # flags which are used to draw settings panels if True, and not draw if False
        self.settings1 = True
        self.settings2 = True 

        # create frames for each panel of settings 
        self.frame1 = tk.LabelFrame(window, text = "First panel")
        self.frame2 = tk.LabelFrame(window, text = "Second panel")
        self.frame1.grid()
        self.frame2.grid() 

        # add button widget to each frame 
        self.button1 = tk.Button(self.frame1, text = "ON", bg = "green", command = lambda: self.changeButtonState(self.button1, self.settings1))
        self.button2 = tk.Button(self.frame2, text = "ON", bg = "green", command = lambda: self.changeButtonState(self.button2, self.settings2))
        self.button1.grid()
        self.button2.grid()

    def changeButtonState(self, button, flag):
        if button["text"] == "ON":
            button["text"] = "OFF"
            button["bg"] = "red"
            
            # change the state of the given flag
            flag = False 
        else: 
            button["text"] = "ON"
            button["bg"] = "green"
            
            # change state of flag
            flag = True 

        print("Settings 1: ", self.settings1, "\nSettings 2: ", self.settings2)

# create the window 
window = tk.Tk()
x = MyWindow()
window.mainloop()

The button colours and text changes fine, but the value of self.settings1 and self.settings2 remains unchanged when you click the buttons. I do not understand why, since flag in the function should be one of the class attributes, but it treats it like a new variable!

How do I create a general function that can alter the state of class attributes that are passed to it?

shadowbiscuit
  • 218
  • 1
  • 8
  • https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference Here is a post explaining why your implementation doesn't work – Challe Aug 20 '20 at 11:36

2 Answers2

3

Changing the value of a parameter doesn't change the value used to set the parameter. But there's no real reason to pass self.settings1 to the function; as a method, it already has access to self, and therefore any setting. First, store the settings in a container, rather than individually named attributes:

def __init__(self):
    self.settings = {'setting1': True, 'setting2': True}
    ...

Then, pass the name of the setting to the call to self.changeButtonState:

    ...
    self.button1 = tk.Button(self.frame1, text = "ON", bg = "green", command = lambda: self.changeButtonState(self.button1, "settings1"))
    ...

Finally, use the name to index your dict of settings:

def changeButtonState(self, button, flag):
    if button["text"] == "ON":
        button["text"] = "OFF"
        button["bg"] = "red"
        
        # change the state of the given flag
        self.settings[flag] = False 
    else: 
        button["text"] = "ON"
        button["bg"] = "green"
        
        # change state of flag
        self.settings[flag] = True 

    print("Settings 1: ", self.settings['setting1'], "\nSettings 2: ", self.settings['setting2'])
chepner
  • 497,756
  • 71
  • 530
  • 681
1

The reason that self.settings1 and self.settings2 isn't changing is because you are not changing them during the changeButtonState function call. Instead, what you do is pass the values to the function. I assume you think the following line:

flag = True

Should change the self.settings1 variable. However, this is not the case because of how variables are handled in Python. Here is an article that goes into some detail about the topic.

loganasherjones
  • 1,012
  • 2
  • 11
  • 19