2

I would like to change the function and text of a button based on which radio-button is selected. Right now what's happening is that both commands are run at the same time as soon as the application is launched rather than it being based upon which radio-button is selected.

import tkinter as tk
import time

## Time variables for the Pomodoro
pomo = 60 * 25 ## 60 seconds times number of minutes
btime = 60 * 5 ## 60

class ExampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)        
        self.label = tk.Label(self, text="25:00", width=10, font="Helvetica 20")
        self.label.pack()
        self.remaining = 0
        self.button = tk.Button(self)
        self.button.pack()

        pomocommand = self.button.configure(text="Pomodoro", state=tk.NORMAL, command= lambda: self.pomodoro(pomo)) #Switch back to the pomodoro timer
        breakcommand = self.button.configure(text="Break", state=tk.NORMAL, command= lambda: self.breaktime(btime)) #Switch to the break timer

        countercommand = self.button.configure(text="Counter", state=tk.NORMAL, command= print('cheese'))

        self.radvar = tk.IntVar()
        self.radvar.set('1')
        self.radio = tk.Radiobutton(self, text="Pomodoro", variable = self.radvar, value=1, indicatoron=0, command = pomocommand)
        self.radio.pack(anchor=tk.W)
        self.radio = tk.Radiobutton(self, text="Counter", variable = self.radvar, value=2, indicatoron=0, command = countercommand)
        self.radio.pack(side=tk.LEFT)

    def pomodoro(self, remaining = None):
        self.button.configure(state=tk.DISABLED)
        if remaining is not None:
            self.remaining = remaining

        if self.remaining <= 0:
            self.label.configure(text="Time's up!")
            breakcommand
        else:
            self.label.configure(text= time.strftime('%M:%S', time.gmtime(self.remaining))) #Integer to 'Minutes' and 'Seconds'
            self.remaining = self.remaining - 1
            self.after(1000, self.pomodoro)


    def breaktime(self, remaining = None):
        self.button.configure(state=tk.DISABLED)
        if remaining is not None:
            self.remaining = remaining

        if self.remaining <= 0:
            self.label.configure(text="Time's up!")
            pomocommand
        else:
            self.label.configure(text= time.strftime('%M:%S', time.gmtime(self.remaining))) #Integer to 'Minutes' and 'Seconds'
            self.remaining = self.remaining - 1
            self.after(1000, self.breaktime)

if __name__ == "__main__":
    app = ExampleApp()
    app.mainloop()
Ryan Hasse
  • 253
  • 1
  • 4
  • 15
  • Notice how the code says `command= lambda: self.pomodoro(pomo)`, and **not** `command= self.pomodoro(pomo)`? The same logic applies to `pomocommand = self.button.configure(...`, because `pomocommand` is itself being used as a `command` argument. – Karl Knechtel Aug 20 '22 at 02:06

1 Answers1

4

What you are doing is calling the function self.button.configure(...) instead of passing the function itself. Here is a small example:

def test(a):
    return a+4

callback_1 = test(2) # callback_1 will be (2+4) = 6, because you called the function. Notice the parens ()
callback_2 = test    # callback_2 will be the function test, you did not call it
# So, you can do:
callback_2(some_value) # will return (some_value + 4), here you called it

Basically what is happening is that you are using the first example, so the function is called in __init__, and what it is supposed to do is done there. What you want is something similar to the second, because Tkinter wants something to call (a function or any callable). So pomocommand and break should be functions not the result of calling a function. (You can try to do print pomocommand and see that it is not a function.)

The solution is either to create a new function, or use lambdas. Here:

def pomocommand(self):
    self.button.configure(text="Pomodoro", state=tk.NORMAL, command= lambda: self.pomodoro(pomo)) #Switch back to the pomodoro timer

# and in your __init__ method:
def __init__(self):
    # ...
    self.radio = tk.Radiobutton(self, text="Pomodoro", variable = self.radvar, value=1, indicatoron=0, command = self.pomocommand)
    # ...

And you do the same for the other button. Using a lambda here is not recommended because it is a long statement (the self.button.configure part) so your code will not be readable. But I see you are using lambdas, so you might get the idea.

As a side note, be careful when using lambda like you do. Here pomo is a global variable, but if it is not, you might get into trouble. See this question. (Right now it is alright, so you can just ignore this :D).

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
jadkik94
  • 7,000
  • 2
  • 30
  • 39
  • That worked like a charm. I don't fully understand why the `pomocommand` as a variable within `def pomocommand` works though. Thank you for the suggested reading. :) – Ryan Hasse May 16 '12 at 08:57
  • Yes, it is useless. try doing `print pomocommand` inside this function. You'll see it's an integer or None or something similar. This is what the function `configure` of the Button class returns. You most probably don't need to store it in a variable, if you don't want to do anything with it. I just copy pasted what you wrote... :P You can remove it. – jadkik94 May 16 '12 at 09:05