How to make Tkinter buttons commands work

ghz 10hours ago ⋅ 3 views

I have a class with a button, it runs the command automatically when the gui is constructed (which I don't want it to do) but then doesn't work again after. What am I doing wrong? Built-in commands such as endcommand work as they should.

relevant excerpts (ignore the indent problem at the very beginning)

class GuiPart(object):    
def __init__(self, master, queue, endCommand):
    self.queue = queue
    # Set up the GUI
    #tkinter.Button(master, text='Done', command=endCommand).grid(row=6,column=6)
    
    tkinter.Button(text='Update Variables', command=self.updateValues()).grid(row=3)

    Lp_pacingState = tkinter.Label(text="p_pacingState")
    Lp_pacingState.grid(row=1, column=3)
    Tp_pacingState = tkinter.Label(bg="white", relief="ridge",justify="center",width=9)
    Tp_pacingState.grid(row=1, column=4)
    ....
    
    self.textBoxes = {"p_pacingState" : Tp_pacingState, "p_pacingMode" : Tp_pacingMode, 
                 "p_hysteresis" : Tp_hysteresis, "p_hysteresisInterval" : Tp_hysteresisInterval,
                 "p_lowrateInterval" : Tp_lowrateInterval, "p_vPaceAmp" : Tp_vPaceAmp,
                 "p_vPaceWidth" : Tp_vPaceWidth, "p_VRP" : Tp_VRP}

#def updateValues(self,input):
def updateValues(self):
    testInput = ["p_pacingState=3", "garbage=poop", "p_VRP=5"]
    for updates in testInput:
        print("zzzz")
        var = updates.split("=")
        try:
            self.textBoxes[var[0]].config(text = var[1])
        except:
            pass

So I get "zzzz" printed 3 times at construction of gui (labels don't update their text though) and the button doesn't work after that. Also if there's a better way to update boxes please tell me. I get input from a stream in no particular order or relevance.

Answer

The issue you're experiencing arises because the command argument in the Button widget is being set to the result of self.updateValues() (i.e., the function is being called immediately during the GUI construction), instead of being set to a reference to the method itself, which is what you want. When you add the parentheses self.updateValues(), it calls the method immediately and assigns its result (which is None) to the command, causing the button to fail to work after that.

Fixing the Issue:

To fix this, you should remove the parentheses from self.updateValues() in the Button constructor. This will pass the method reference to the button, so it will only be called when the button is clicked.

Here's the corrected part of your code:

class GuiPart(object):
    def __init__(self, master, queue, endCommand):
        self.queue = queue
        # Set up the GUI
        tkinter.Button(text='Update Variables', command=self.updateValues).grid(row=3)

        Lp_pacingState = tkinter.Label(text="p_pacingState")
        Lp_pacingState.grid(row=1, column=3)
        Tp_pacingState = tkinter.Label(bg="white", relief="ridge", justify="center", width=9)
        Tp_pacingState.grid(row=1, column=4)
        # ...

        self.textBoxes = {"p_pacingState": Tp_pacingState, "p_pacingMode": Tp_pacingMode,
                          "p_hysteresis": Tp_hysteresis, "p_hysteresisInterval": Tp_hysteresisInterval,
                          "p_lowrateInterval": Tp_lowrateInterval, "p_vPaceAmp": Tp_vPaceAmp,
                          "p_vPaceWidth": Tp_vPaceWidth, "p_VRP": Tp_VRP}

    def updateValues(self):
        testInput = ["p_pacingState=3", "garbage=poop", "p_VRP=5"]
        for updates in testInput:
            print("zzzz")
            var = updates.split("=")
            try:
                self.textBoxes[var[0]].config(text=var[1])
            except:
                pass

Explanation:

  • The problem was that command=self.updateValues() in the button creation was immediately calling self.updateValues() and passing the result (which is None) as the command for the button. What you want instead is passing the function reference self.updateValues (without the parentheses), so that it only gets executed when the button is clicked.

Additional Notes:

  • Make sure that the updateValues method updates the labels correctly by ensuring that the keys in self.textBoxes (like "p_pacingState", "p_VRP", etc.) match the keys you're using in testInput.

  • You might want to handle the exceptions more carefully instead of using a general except: pass. For example, you can log or print a message when an invalid key is encountered so that you can debug it easily.

def updateValues(self):
    testInput = ["p_pacingState=3", "garbage=poop", "p_VRP=5"]
    for updates in testInput:
        print("zzzz")
        var = updates.split("=")
        try:
            if var[0] in self.textBoxes:  # Ensure the key exists
                self.textBoxes[var[0]].config(text=var[1])
            else:
                print(f"Invalid key: {var[0]}")  # Log invalid keys
        except Exception as e:
            print(f"Error updating {var[0]}: {e}")

This should now behave as expected: The updateValues method will not run at GUI construction time but will be triggered when the button is clicked.