web-dev-qa-db-fra.com

Threading.timer Python - Répète la fonction toutes les secondes

J'ai des difficultés avec le minuteur python et j'apprécierais beaucoup un conseil ou une aide: D

Je ne connais pas très bien le fonctionnement des threads, mais je souhaite simplement activer une fonction toutes les 0,5 seconde et pouvoir démarrer et arrêter le chronomètre et le réinitialiser.

Cependant, je continue à obtenir RuntimeError: threads can only be started once lorsque j'exécute threading.timer.start() deux fois. Y at-il un travail autour de cela? J'ai essayé d'appliquer threading.timer.cancel() avant chaque départ.

Pseudo code:

t=threading.timer(0.5,function)
while True:
    t.cancel()
    t.start()
64
user1431282

Le meilleur moyen est de démarrer le thread de la minuterie une fois. Dans votre fil de la minuterie, vous coderiez ce qui suit

class MyThread(Thread):
    def __init__(self, event):
        Thread.__init__(self)
        self.stopped = event

    def run(self):
        while not self.stopped.wait(0.5):
            print("my thread")
            # call a function

Dans le code qui a lancé le minuteur, vous pouvez alors set l'événement arrêté pour l'arrêter.

stopFlag = Event()
thread = MyThread(stopFlag)
thread.start()
# this will stop the timer
stopFlag.set()
83
Hans Then

Utilisation de la minuterie

from threading import Timer,Thread,Event


class perpetualTimer():

   def __init__(self,t,hFunction):
      self.t=t
      self.hFunction = hFunction
      self.thread = Timer(self.t,self.handle_function)

   def handle_function(self):
      self.hFunction()
      self.thread = Timer(self.t,self.handle_function)
      self.thread.start()

   def start(self):
      self.thread.start()

   def cancel(self):
      self.thread.cancel()

def printer():
    print 'ipsem lorem'

t = perpetualTimer(5,printer)
t.start()

cela peut être arrêté par t.cancel()

26
swapnil jariwala

From Equivalent de setInterval en python :

import threading

def setInterval(interval):
    def decorator(function):
        def wrapper(*args, **kwargs):
            stopped = threading.Event()

            def loop(): # executed in another thread
                while not stopped.wait(interval): # until stopped
                    function(*args, **kwargs)

            t = threading.Thread(target=loop)
            t.daemon = True # stop if the program exits
            t.start()
            return stopped
        return wrapper
    return decorator

Usage:

@setInterval(.5)
def function():
    "..."

stop = function() # start timer, the first call is in .5 seconds
stop.set() # stop the loop
stop = function() # start new timer
# ...
stop.set() 

Ou voici les mêmes fonctionnalités mais en tant que fonction autonome au lieu de décorateur :

cancel_future_calls = call_repeatedly(60, print, "Hello, World")
# ...
cancel_future_calls() 

Voici comment faire sans utiliser de threads .

23
jfs

Dans l’intérêt de fournir une réponse correcte en utilisant Timer comme le PO le demande, je vais améliorer la réponse de swapnil jariwala :

from threading import Timer
import time


class InfiniteTimer():
    """A Timer class that does not stop, unless you want it to."""

    def __init__(self, seconds, target):
        self._should_continue = False
        self.is_running = False
        self.seconds = seconds
        self.target = target
        self.thread = None

    def _handle_target(self):
        self.is_running = True
        self.target()
        self.is_running = False
        self._start_timer()

    def _start_timer(self):
        if self._should_continue: # Code could have been running when cancel was called.
            self.thread = Timer(self.seconds, self._handle_target)
            self.thread.start()

    def start(self):
        if not self._should_continue and not self.is_running:
            self._should_continue = True
            self._start_timer()
        else:
            print("Timer already started or running, please wait if you're restarting.")

    def cancel(self):
        if self.thread is not None:
            self._should_continue = False # Just in case thread is running and cancel fails.
            self.thread.cancel()
        else:
            print("Timer never started or failed to initialize.")


def tick():
    print('ipsem lorem')

# Example Usage
t = InfiniteTimer(0.5, tick)
t.start()

L'heure d'importation est facultative sans l'utilisation de l'exemple.

4
Bill Schumacher

J'ai changé du code swapnil-jariwala pour créer une petite horloge de console.

from threading import Timer, Thread, Event
from datetime import datetime

class PT():

    def __init__(self, t, hFunction):
        self.t = t
        self.hFunction = hFunction
        self.thread = Timer(self.t, self.handle_function)

    def handle_function(self):
        self.hFunction()
        self.thread = Timer(self.t, self.handle_function)
        self.thread.start()

    def start(self):
        self.thread.start()

def printer():
    tempo = datetime.today()
    h,m,s = tempo.hour, tempo.minute, tempo.second
    print(f"{h}:{m}:{s}")


t = PT(1, printer)
t.start()

SORTIE

>>> 11:39:11
11:39:12
11:39:13
11:39:14
11:39:15
11:39:16
...

Minuterie avec une interface graphique tkinter

Ce code met la minuterie dans une petite fenêtre avec tkinter

from threading import Timer, Thread, Event
from datetime import datetime
import tkinter as tk

app = tk.Tk()
lab = tk.Label(app, text="Timer will start in a sec")
lab.pack()


class perpetualTimer():

    def __init__(self, t, hFunction):
        self.t = t
        self.hFunction = hFunction
        self.thread = Timer(self.t, self.handle_function)

    def handle_function(self):
        self.hFunction()
        self.thread = Timer(self.t, self.handle_function)
        self.thread.start()

    def start(self):
        self.thread.start()

    def cancel(self):
        self.thread.cancel()


def printer():
    tempo = datetime.today()
    clock = "{}:{}:{}".format(tempo.hour, tempo.minute, tempo.second)
    try:
        lab['text'] = clock
    except RuntimeError:
        exit()


t = perpetualTimer(1, printer)
t.start()
app.mainloop()

Un exemple de jeu de cartes mémoire (en quelque sorte)

from threading import Timer, Thread, Event
from datetime import datetime


class perpetualTimer():

    def __init__(self, t, hFunction):
        self.t = t
        self.hFunction = hFunction
        self.thread = Timer(self.t, self.handle_function)

    def handle_function(self):
        self.hFunction()
        self.thread = Timer(self.t, self.handle_function)
        self.thread.start()

    def start(self):
        self.thread.start()

    def cancel(self):
        self.thread.cancel()


x = datetime.today()
start = x.second


def printer():
    global questions, counter, start
    x = datetime.today()
    tempo = x.second
    if tempo - 3 > start:
        show_ans()
    #print("\n{}:{}:{}".format(tempo.hour, tempo.minute, tempo.second), end="")
    print()
    print("-" + questions[counter])
    counter += 1
    if counter == len(answers):
        counter = 0


def show_ans():
    global answers, c2
    print("It is {}".format(answers[c2]))
    c2 += 1
    if c2 == len(answers):
        c2 = 0


questions = ["What is the capital of Italy?",
             "What is the capital of France?",
             "What is the capital of England?",
             "What is the capital of Spain?"]

answers = "Rome", "Paris", "London", "Madrid"

counter = 0
c2 = 0
print("Get ready to answer")
t = perpetualTimer(3, printer)
t.start()

sortie:

Get ready to answer
>>> 
-What is the capital of Italy?
It is Rome

-What is the capital of France?
It is Paris

-What is the capital of England?
...
3
Giovanni Gianni

Améliorer un peu sur La réponse de Hans Then , nous pouvons simplement sous-classer la fonction Minuterie. Ce qui suit devient notre code entier "Repeat timer", et il peut être utilisé comme remplacement instantané de threading.Timer avec les mêmes arguments:

from threading import Timer

class RepeatTimer(Timer):
    def run(self):
        while not self.finished.wait(self.interval):
            self.function(*self.args, **self.kwargs)

Exemple d'utilisation:

def dummyfn(msg="foo"):
    print(msg)

timer = RepeatTimer(1, dummyfn)
timer.start()
time.sleep(5)
timer.cancel()

produit la sortie suivante:

foo
foo
foo
foo

et

timer = RepeatTimer(1, dummyfn, args=("bar",))
timer.start()
time.sleep(5)
timer.cancel()

produit

bar
bar
bar
bar
3
right2clicky

Je devais le faire pour un projet. Ce que j'ai fini par faire, c'est de lancer un thread séparé pour la fonction

t = threading.Thread(target =heartbeat, args=(worker,))
t.start()

**** le battement de coeur est ma fonction, le travailleur est l'un de mes arguments ****

à l'intérieur de ma fonction de battement de coeur:

def heartbeat(worker):

    while True:
        time.sleep(5)
        #all of my code

Ainsi, lorsque je démarre le thread, la fonction attend 5 secondes à plusieurs reprises, exécute tout mon code et le fait indéfiniment. Si vous voulez tuer le processus, tuez simplement le fil.

1
Andrew Wilkins

J'ai implémenté une classe qui fonctionne comme une minuterie.

Je laisse le lien ici au cas où quelqu'un en aurait besoin: https://github.com/ivanhalencp/python/tree/master/xTimer

1
Ivan Coppes

J'aime la réponse de right2clicky, notamment parce qu'elle n'exige pas qu'un fil soit déchiré et qu'un nouveau soit créé chaque fois que le minuteur se déclenche. En outre, il est facile de créer une classe avec un rappel de minuterie appelé périodiquement. C'est mon cas d'utilisation normal:

class MyClass(RepeatTimer):
    def __init__(self, period):
        super().__init__(period, self.on_timer)

    def on_timer(self):
        print("Tick")


if __== "__main__":
    mc = MyClass(1)
    mc.start()
    time.sleep(5)
    mc.cancel()
0
Frank P
from threading import Timer
def TaskManager():
    #do stuff
    t = Timer( 1, TaskManager )
    t.start()

TaskManager()

Voici un petit exemple, il aidera à mieux comprendre comment il s'exécute . Function taskManager () à la fin de créer un appel de fonction retardé.

Essayez de changer la variable "dalay" et vous pourrez voir la différence

from threading import Timer, _sleep

# ------------------------------------------
DATA = []
dalay = 0.25 # sec
counter = 0
allow_run = True
FIFO = True

def taskManager():

    global counter, DATA, delay, allow_run
    counter += 1

    if len(DATA) > 0:
        if FIFO:
            print("["+str(counter)+"] new data: ["+str(DATA.pop(0))+"]")
        else:
            print("["+str(counter)+"] new data: ["+str(DATA.pop())+"]")

    else:
        print("["+str(counter)+"] no data")

    if allow_run:
        #delayed method/function call to it self
        t = Timer( dalay, taskManager )
        t.start()

    else:
        print(" END task-manager: disabled")

# ------------------------------------------
def main():

    DATA.append("data from main(): 0")
    _sleep(2)
    DATA.append("data from main(): 1")
    _sleep(2)


# ------------------------------------------
print(" START task-manager:")
taskManager()

_sleep(2)
DATA.append("first data")

_sleep(2)
DATA.append("second data")

print(" START main():")
main()
print(" END main():")

_sleep(2)
DATA.append("last data")

allow_run = False
0
ch3ll0v3k

Ceci est une autre implémentation utilisant function au lieu de class. Inspiré par @ Andrew Wilkins ci-dessus.

Parce que wait est plus précis que dormir (cela prend en compte l'exécution de la fonction):

import threading

PING_ON = threading.Event()

def ping():
  while not PING_ON.wait(1):
    print("my thread %s" % str(threading.current_thread().ident))

t = threading.Thread(target=ping)
t.start()

sleep(5)
PING_ON.set()
0
Paul Kenjora