1

I am working on a simple Python program, dubbed "Ilmiont DDNS Alerter". As the title may indicate, the aim is to let you know when your dynamic IP address changes.

It does this by consulting with an external server. In the Tkinter GUI, the user can select a time frequency to update it on. When they start the updater, I want to schedule the update function to run every x minutes, as specified by the user. I have seen the sched module but it seems to schedule only once. How can I make it check for the new IP repeatedly on the interval until stopped?

I am having trouble working out how to schedule this. Additionally, when the updater runs, the GUI goes unresponsive while the process completes. This can take a couple of seconds, especially on slow connections. Presumably threading will overcome this?

nbro
  • 15,395
  • 32
  • 113
  • 196
Ilmiont
  • 2,032
  • 4
  • 27
  • 44
  • Can you list and show what you have tried so far? – Mo H. May 07 '15 at 17:35
  • Well I saw another person who has used the sched module to schedule it for x minutes as I mentioned. Then, within the called function, they schedule a new run as the very last thing but this seems rather crude and I'm not sure how to cancel it on a button. And it keeps the GUI unresponsive issue. – Ilmiont May 07 '15 at 17:40
  • To avoid blocking your GUI, you could use threads or `createfilrhandler()` to perform I/O asynchronously. See [links with code examples in my comments to the related question](http://stackoverflow.com/q/15362372/4279) – jfs May 07 '15 at 18:22

3 Answers3

2

I think this is similar to what you're looking for, the answer given might work for your issue as well. Loop socket request every 5 seconds (Python)

The answer indicates you should you a module of tkinter called "after_idle" that schedules a function the be called once the GUI isn't busy anymore. You can also use .after(time, function) to delay for a specific amount of time before calling the function again.

Good luck !

Community
  • 1
  • 1
Meghdeep Ray
  • 5,262
  • 4
  • 34
  • 58
  • It could work but it seems as though it only does it once again – Ilmiont May 07 '15 at 17:40
  • @Ilmiont: wrong. It runs repeatedly. `read_the_socket()` schedules itself using `gui.after(5000, read_the_socket)`. `widget.after()` is the preferable option if the request code doesn't block for long. – jfs Feb 22 '16 at 21:21
1

You have really two options:

  1. A loop that runs once per 5 (or x) minutes to add to the schedule

  2. Or scheduling the function to run again x minutes after it last finished.

In either case, your cancel Button would just set a flag somewhere that the running function(s) should check as their first operation.

The biggest downside with the first case is that it will lock up the execution of your other code. The simpler route is definitely to make the function schedule itself after it runs. If your other code is taking a long time, threading is definitely an option you could investigate; I've not had to use it myself, but something similar to the code below should work:

import threading
import tkinter
import time


class RepeatExecute(object):
    def __init__(self,master,t=2000):
        self.master=master
        self.time=t

    def go(self):
        self.GO = True
        self.call_self()

    def call_self(self):
        if self.GO is True:
            t = self.time
            print(time.time())
            thrd = threading.Thread(target=self.slow_execute)
            thrd.start()
            self.master.after(t,self.call_self)

    def set_time(self,t):
        self.time=t

    def stop(self):
        self.GO = False

    def slow_execute(self):
        time.sleep(1.0)
        if self.GO is True:
            print('Halfway')

    def active(self):
        print('Active')


if __name__=='__main__':
    root =tkinter.Tk()
    RE = RepeatExecute(root,t=2000)
    btn1 = tkinter.Button(root,text='Start',command=RE.go)
    btn1.grid()
    btn2 = tkinter.Button(root,text='Stop',command=RE.stop)
    btn2.grid()
    btn3 = tkinter.Button(root,text='ACTIVE!',command=RE.active)
    btn3.grid()
    root.mainloop()

So hit 'Start', and it will print the time every two seconds, plus the other function will run in between. You can hit 'ACTIVE' at any time to prove the interface is responsive. I hope this is helpful? If you're really against the function calling itself, you could probably start a thread that just runs an infinite loop adding events to the queue, but this seems an unnecessary complication.

I do note that threading won't be the best choice if the slow_execute function is doing a lot of number crunching, threading is mainly for slow I/O due to the action of the global interpreter lock. Should be fine if you're waiting for results/communications.

rpatel
  • 576
  • 1
  • 6
  • 20
Phren
  • 36
  • 4
0

You can try Threading.Timer

See this Example

from threading import Timer

def job_function():
    Timer(60, job_function).start ()
    print("Running job_funtion")
job_function()

It will print "Running job_function" every Minute

sKhan
  • 9,694
  • 16
  • 55
  • 53
Anshuman
  • 421
  • 4
  • 11