0

Using tkinter, I wish to create a Button, when pressed it delays its switching in x seconds.

using time.sleep(x), pauses entire program, which is not my intention.

how can it be done ?

followed- class of the "button"( has a checkbutton widget, a labelwidget showing on/off label, and an entry widget to enter amount of seconds to delay )

class dev_buttons2(object):
  def __init__(self,master,buts_list):
    self.status=[]
    self.buts=[]
    self.leds=[]
    for i in range(len(buts_list)):
        var = StringVar()
        c = Checkbutton(master,text=buts_list[i], variable=var,
        indicatoron=0,command=lambda arg=[buts_list[i],var]: 
        self.cb(arg),width=10,height=2,onvalue="on",offvalue="off")
        c.grid(column=i, padx=30,pady=5,row = 1)
        var.set("off")

        var1=IntVar()
        ent=Entry(master,textvariable=var1,width=4)
        ent.grid(column=i,row=2)

        var2=StringVar()
        led=Label(master,textvariable=var2,width=4,bg="red",fg="white",
        relief="ridge")
        var2.set("off")
        led.grid(row=0,column=i)

        self.status.append([var,var2,var1])           
        self.buts.append(c)
        self.leds.append(led)

  def cb(self,but):
    indx=devices_headers.index(but[0])
    if but[1].get()=="on":
        self.status[indx][1].set(but[1].get())
        self.leds[indx].config(bg="green")
        if self.status[indx][2].get() !=0:
            print(self.status[indx][2].get() )

    if but[1].get()=="off":
        self.status[indx][1].set(but[1].get())
        self.leds[indx].config(bg="red")

a try to update cb function - gets the delay, but doen'st delay:

def cb(self,but):
    print(but[2].get())   ###(but[2] contains var1.get() -- timeout for opretion
    indx=devices_headers.index(but[0])
    device_chage_state(indx,but[1].get())
    if but[2].get() >0 :
        print ("switch off in %d seconds"%self.status[indx][2].get())
        root.after(but[2].get(),self.cb,but)

Pics of relevant part in GUI shows each button have a timeout Entry, when entered (greater than 0 ) will turn off after amount of seconds entered Manual switch pannel description

guyd
  • 693
  • 2
  • 14
  • 32

1 Answers1

2

Since you are using tkinter, the best way to do that is using the after() method. You can for example add a method like this:

def cb_delayed(self, master, but, delay_ms_var):
    master.after(delay_ms_var.get(), self.cb, but)

And then in the button creation change:

# ...
c = Checkbutton(
    master, text=buts_list[i], variable=var, indicatoron=0,
    command=lambda arg=[buts_list[i],var]: self.cb_delayed(master, arg, delay_ms_var),
    width=10, height=2, onvalue="on", offvalue="off")
# ...

Where delay_ms_var is the tkinter variable object containing the delay in milliseconds that you want to have before the change happens.

jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • thanks for your answer- I have some questions : 1) CheckButton's command can transfer only one variable ( so I used lambda and arg=[but_list,var]) - so how `DELAY_MS` is transfered to `cd_delayed` function ? 2)`cb_delayed` has `master` in its definition. can you explain what is the purpose of using it ? – guyd Jul 19 '17 at 09:39
  • 1
    1) I thought the delay was meant to be a fixed amount of time. I've changed the answer to use a variable object instead. This can be passed just like that or added to `arg` if you prefer; since tkinter variables are prone to cause problems when they are [garbage collected](https://stackoverflow.com/a/37351021/1782792), it could be better in your case. 2) `master` is needed in `cb_delayed` in order to call `.after`; however, if that object is accessible from a global variable or from `self`, you may not need to pass it as a parameter. – jdehesa Jul 19 '17 at 10:01
  • thanks for your update- you can see a pic of GUI attached to Question. 1) `delay_ms_var` is `var1` in my code. when pressing any button- the value of `var1` passed to `cb_delayed` belongs to last button (#4) - that is why i constructed `arg` since there I didn't find any alternative way to pass values. can you explain ? – guyd Jul 19 '17 at 12:48
  • 1
    @Guy.D The parameter `command` is a function (or lambda, or callable in general) that is called by Tk when the button is pressed. If you use a lambda, you can specify a call with arguments from the local variables of the function (or global variables). See [this question](https://stackoverflow.com/questions/6920302/how-to-pass-arguments-to-a-button-command-in-tkinter), for example. – jdehesa Jul 19 '17 at 12:55
  • I agree, but it passes ( as far as i know...) only one argument to `cb` or `cb_delayed` --> and that happed again using the code you suggested. – guyd Jul 19 '17 at 13:00
  • 1
    @Guy.D The lambda function receives no parameters from tkinter; in your case your lambda is receiving `arg` because it has a default value. However, the lambda itself is free to call any function with however many parameters; it is not tkinter the one that calls `cb` / `cb_delayed`, but your lambda, so you have full control over the parameters these functions receive. [Here](http://effbot.org/zone/tkinter-callbacks.htm) are more examples of callbacks. – jdehesa Jul 19 '17 at 13:45
  • Interesting. Can you please write an example, since I thought otherwise – guyd Jul 19 '17 at 16:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149640/discussion-between-jdehesa-and-guy-d). – jdehesa Jul 19 '17 at 17:41