2

I have to stop a loop and resume after many seconds. I tried to use after(), but the loop don't freeze. And when I use time.sleep() (works out tkinter), tkinter freeze. Have another way or a function equivalent time.sleep() without freeze.

code:

for message in listOfMessages:
    time.sleep(message.time)
    #manipulate message
    #change text widget

This will freeze my tkinter app.

I tried to use after() but message.time is a float: 0.5054564 to 1.5234244. It's random. And after supports only int. I can't convert to int, because low numbers make differents. And if i convert 0.52342342 in int: 523... i lost the others 42342

and this dont work too:

def changes(#values):
    #manipulate message
    #change text widget

for message in listOfMessages:
    app.after(message.time, lambda: changes(#values))

Have another function equivalent time.sleep thats not freeze tkinter and is different that after()? If not, have another way to do this? Thanks for attention.

Radagast
  • 509
  • 10
  • 23

1 Answers1

3

To create an analog of:

for message in listOfMessages:
    time.sleep(message.time)
    change(message)

in tkinter:

def update_widget(app, messages):
    message = next(messages, None)
    if message is None: # end of the loop
        return
    delay = int(message.time * 1000) # milliseconds
    app.after(delay, change, message) # execute body
    app.after(delay, update_widget, app, messages) # next iteration

update_widget(app, iter(listOfMessages))

If you want to wait until change(message) finishes before continuing the loop:

def iterate(message, app, messages):
    change(message)
    update_widget(app, messages)

def update_widget(app, messages):
    message = next(messages, None)
    if message is None: # end of the loop
        return
    delay = int(message.time * 1000) # milliseconds
    app.after(delay, iterate, message, app, messages)

update_widget(app, iter(listOfMessages))
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • this works, but when message have a time != 0, error appears: tkinter.TclError: bad argument "507.478125": must be cancel, idle, info, or an integer – Radagast Oct 16 '15 at 18:21
  • @ArnaldoBadin: if `message.time` is a float then coerce the delay into integer. I've updated the answer. – jfs Oct 16 '15 at 18:24
  • Bug appears, not a error, but I have delay in lasts messages. Have a way to self.after read float? like 507.478125? – Radagast Oct 16 '15 at 18:44
  • @ArnaldoBadin: delay is in *milliseconds* i.e., the scheduling precision is probably worse than that and therefore accepting a float wouldn't help anyway. Make sure you don't call functions that may block in `change(message)`. Use threads, separate processes to offload blocking calls. btw., are you sure you want to wait 500 seconds (almost 10 minutes)? – jfs Oct 16 '15 at 18:54
  • the decimals make difference, because the messages is song notes and lyrics, then i need to put in ;/ ... so, 500 is milisseconds, then 500 = 0.5 s in self.after, but in time.sleep is 500s – Radagast Oct 16 '15 at 19:01
  • @ArnaldoBadin: have you measured whether there is a difference between `app.after(500,..)` and `app.after(501,..)` in your environment? If you need a metronome precision (to avoid drift over multiple iterations); see [this answer](http://stackoverflow.com/a/26609843/4279). You should probably ask it as a separate question (with a specific focus on your application). – jfs Oct 16 '15 at 19:18
  • Thanks for help. I fix the problem using threading.Timer(delay, func) – Radagast Oct 16 '15 at 20:49
  • 1
    @ArnaldoBadin: It indicates that the issue is that you call a blocking function in your code. `Timer()` creates a new thread that allows you to avoid unnecessary delay due to the blocking call. Otherwise, there is no reason to expect a better precision from `Timer()` than from `app.after()`. – jfs Oct 16 '15 at 21:04
  • Both has bad precision, if you think in milimlimliseconds, i tried a lot of ways, but the best is use app.update with time.time, and thanks for help man <3..........: for msg in messages: start = time.time() app.update() while message.time >= time.time() - start: pass – Radagast Oct 18 '15 at 02:27