I have a short and probably simple question refering to Timers:
Is it normal for the thread ID to increment every time I start a timer?
from threading import Timer
class Watchdog():
def __init__(self, timeout, user_Handler): # timeout in seconds
self.timeout = timeout
self.handler = user_Handler
self.timer = Timer(self.timeout, self.handler)
def reset(self):
self.timer.cancel() # stop the timer's action if it's still running
print("A", self.timer)
self.timer = Timer(self.timeout, self.handler) # Timer: Calls a function=handler after a specified number of seconds
print("B", self.timer)
self.timer.start()
print("C", self.timer)
def stop(self):
"""Stop the timer if it hasn't finished yet."""
self.timer.cancel()
print("D", self.timer)
When I use my class, the print statements return sth similar like this:
print(self.timer)
>>> <Timer(Thread-1, started daemon 19447202672)>
>>> <Timer(Thread-2, stopped daemon 19447202672)>
...
>>> <Timer(Thread-n, started daemon 19447202672)>
--> n (=ID) rises
Reference: How to implement a watchdog timer in Python?
UPDATE - Use of the class:
As I use this class with a button which triggers an interrupt, I'll try a textual description. I'm pretty sure, this might be enough for a python/programming expert (If not, please tell me).
A push of the button changes the view of a LC-Display from a default view to one of three special views. After the last special view it starts again with the default view (kind of round robin). After 5s without pushing the button, screen is resetted to default view.
Pushing the button calls the watchdog interrupt handler where my_watchdog.reset() is called to start the watchdog timer. By changing the view (button push) within the 5s , this happens again and so the timer is resetted by being canceled and started. If you switch from the last special view to the default view by hand, my_watchdog.stop() is called. This is to avoid the call of the watchdog_expired_handler. This handler resets the view to the default view.
In summary you could say that my_watchdog is instantiated and my_watchdog.reset() and my_watchdog.stop() are called at random times.
UPDATE_2 - Simplified use of the class in python:
import time
def watchdog_handler():
print("watchdog timer expired")
my_watchdog = Watchdog(timeout=5, user_Handler=watchdog_handler)
my_watchdog.reset() # starts a watchdog timer (no running timer has to be stopped)
time.sleep(6) # is equivalent to no action of the user
my_watchdog.reset() # user presses button again
time.sleep(2) # user presses button while timer is still running --> watchdog.reset() is called
my_watchdog.reset()
time.sleep(2) # user presses button while timer is still running
my_watchdog.reset()
time.sleep(2) # user presses button while timer is still running and reaches default view again
my_watchdog.stop()
Output of the simplified use (on Raspian OS):
A <Timer(Thread-106, initial)>
B <Timer(Thread-107, initial)>
C <Timer(Thread-107, started 14176)>
watchdog timer expired
A <Timer(Thread-107, stopped 14176)>
B <Timer(Thread-108, initial)>
C <Timer(Thread-108, started 1256)>
A <Timer(Thread-108, stopped 1256)>
B <Timer(Thread-109, initial)>
C <Timer(Thread-109, started 6796)>
A <Timer(Thread-109, stopped 6796)>
B <Timer(Thread-110, initial)>
I've written a lot and learned also sth by breaking it down here. I think the key questions are very simple:
Is there anything critical about re/starting the timer on a new thread (I assume that this is what I do). What happens to the old one (is there some kind of garbage collector)? When is the ID reset, on restart?
Related
So, I've been working on an LED strip connected to my raspberry pi 4, and I've got TouchOSC working between the Pi and my phone. The current problem I'm having is when a toggle button I've pressed is turned off, the program it's designated to run continues running. I have a global variable that's supposed to determine if the "while" loop continues, but instead of setting the button state to zero and terminating the program, it continues to run until it's interrupted by a Ctrl+C in the terminal. I was wondering if anyone would happen to know why the program doesn't stop when the button state is change.
def twinkleBtn(path, tags, args, source):
global twinkleState
twinkleState = int(args[0])
if twinkleState:
turnOff(strip)
while twinkleState:
twinkleTest(strip, False, 10)
if not twinkleState:
turnOff(strip)
This is triggered when the twinkle button is pressed, but the program continues to run when I toggle it back to zero. Below is the code to the function "twinkleTest"
def twinkleTest(strip, onlyOne, count, wait_ms=50):
setPixels(Color(0, 0, 0))
for i in range(count):
strip.setPixelColor(randint(0,LED_COUNT), Color(170, 180, 30))
strip.show()
time.sleep(wait_ms/250.0)
if (onlyOne):
setPixels(Color(0,0,0))
time.sleep(wait_ms/500.0)
I'm not sure if I'm just clueless or what exactly is being done wrong here. I'm pretty new to Python so it may not be the best. Thanks for any help!
Here is a simple threading solution. A thread waits on a threading.Event. twinkleTest is called whenever the event is set. The GUI sets the event, and twinkling will happen in the background until the button is pressed a second time to stop the twinkling.
import threading
def twinkleTest(strip, onlyOne, count, wait_ms=50):
setPixels(Color(0, 0, 0))
for i in range(count):
strip.setPixelColor(randint(0,LED_COUNT), Color(170, 180, 30))
strip.show()
time.sleep(wait_ms/250.0)
if (onlyOne):
setPixels(Color(0,0,0))
time.sleep(wait_ms/500.0)
def twinkler(twinkleEvent):
"""Call twinkleTest whenever the given twinkleEvent event is set
and keeps calling until the event is cleared. Designed to be used in
a separate thread."""
while True:
twinkleEvent.wait()
twinkleTest(strip, False, 10)
def setup_twinkle_daemon_thread():
"""Creates a daemon thread to twinkle screen whenever an event is set.
Returns event, threadhandle"""
twinkleEvent = threading.Event()
twinkleThread = threading.Thread(target=twinker, args=tuple(twinkleEvent))
twinkleThread.daemon = True
twinkleThread.start()
return twinkleEvent, twinkleThread
# controls twinkler
twinkleEvent = twinkleThread = None
def twinkleBtn(path, tags, args, source):
global twinkleEvent, twinkleThread
if twinkleEvent is None:
# start daemon thread on first use
twinkleEvent, twinkleThread = setup_twinkle_daemon_thread()
if twinkleEvent.is_set():
twinkleEvent.clear()
else:
twinkleEvent.set()
After some research on how to properly ask a thread to stop, I am stuck into an unexpected behavior.
I am working on a personal project. My aim is to run a program on a RaspberryPi dedicated to domotics.
My code is structured as below:
a first thread is dedicated to scheduling : everyday at the same hour, I send a signal on GPIO output
a second thread is dedicated to monitoring keyboard for manual events
whenever a specific key is pressed, I want to start a new thread that is dedicated to another routine just like my first thread
Here is how I proceed:
import schedule
from pynput import keyboard
import threading
first_thread = threading.Thread(target=heating, name="heating")
second_thread = threading.Thread(target=keyboard, name="keyboard")
first_thread.start()
second_thread.start()
stop_event = threading.Event()
My heating routine is defined by:
def heating():
def job():
GPIO.output(4,GPIO.HIGH)
return
schedule.every().day.at("01:00").do(job)
while True:
schedule.run_pending()
time.sleep(0.5)
My keyboard monitor is defined as follow:
def keyboard():
def on_press(key):
if key == keyboard.Key.f4:
shutter_thread = threading.Thread(name="shutter", target=shutter, args=(stop_event,))
shutter_thread.start()
if key == keyboard.Key.f5:
stop_event.set()
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()
My shutter thread target is similar to the heating one:
def shutter(stop_event):
def open():
GPIO.output(6,GPIO.HIGH)
return
t = threading.currentThread()
schedule.every().day.at("22:00").do(open)
while not stop_event.is_set():
schedule.run_pending()
time.sleep(0.5)
Problem is everytime I press the key to start my shutter thread, the shutter routine is called but:
the job within my shutter routine is executed twice
the job within the first thread is also now executed twice every time it is on schedule !
once I press the key to ask the shutter thread to stop, the heating (first) thread come back to its original (and correct) behaviour, but the shutter thread does not stop
I have no idea why starting this new thread yields such modification in the behaviour of the other thread. And why my stopping event is not working ?
What am I doing wrong ?
Since you are using the schedule framework for managing tasks a clean solution would be to use the same framework's API for canceling jobs (instead of using threading.Event). That way tasks management remains within schedule and user interaction is handled by threading.
def keyboard():
tasks = []
def on_press(key):
if key == keyboard.Key.f4:
# Shutter task.
tasks.append(
schedule.every().day.at("22:00").do(lambda: GPIO.output(6,GPIO.HIGH))
)
if key == keyboard.Key.f5:
schedule.cancel_job(tasks.pop(-1))
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()
# Heating task.
schedule.every().day.at("01:00").do(lambda: GPIO.output(4,GPIO.HIGH))
# Start keyboard listener.
ui = threading.Thread(target=keyboard)
ui.start()
while True:
schedule.run_pending()
time.sleep(0.5)
Even if a_guest solution is a clean one, I can share a second solution for those who can face a similar situation.
A working solution is to define a specific scheduler in the different threads instead of using the default one.
Illustration:
def heating():
def job():
GPIO.output(4,GPIO.HIGH)
return
heat_sched = schedule.Scheduler()
heat_sched.every().day.at("01:00").do(job)
while True:
heat_sched.run_pending()
time.sleep(1)
def shutter(stop_event):
def open():
GPIO.output(6,GPIO.HIGH)
return
shutter_sched = schedule.Scheduler()
shutter_sched.every().day.at("22:00").do(open)
while True:
if not stop_event.is_set():
shutter_sched.run_pending()
time.sleep(0.5)
else:
shutter_sched.clear()
return
I have a raspberry pi which I have hooked up with a 4 button keypad. Using the signal stuff from blinker I hooked it up to run some methods.
#sender
while True:
if buttonIsDown == True: signal.send()
#reciever
#signal.connect
def sayHI():
print("1")
time.sleep(10)
print("2")
This works fine, however when I push the button for the second time (Within 10 seconds of the previous button press) it does not fire the method as the thread is paused in the time.sleep(10).
How can I get it to fire the method again while the it is still paused(possibly in another thread)
It is an old question, but still it may be useful for someone else.
You can start a new thread every time the signal is emitted, in that way you will be able to catch all the events as soon as they happen. Remember that in your code, since you have a while True, the signal is never connected to the function, you should have defined them in the opposite order.
Here is a working example, based on your code:
import threading
from blinker import signal
from time import sleep
custom_signal = signal(name='custom')
#custom_signal.connect
def slot(sender):
def say_hello():
print("1")
sleep(10)
print("2")
threading.Thread(target=say_hello).start()
while True:
value = int(input('Press 1 to continue: '))
if value == 1:
custom_signal.send()
else:
break
I wrote an pyqt gui and used threading to run code which needs a long time to be executed, but I want to have the choice to stop the execution safely. I dont want to use the get_thread.terminate() method. I want to stop the code by a special function (maybe del()). My problem is that, I wrote the code in a own class and just want to abort the class without changing a lot of syntax.
Edit: It was mentioned that one has to pass a flag to the class, which has to be checked constantly. How do I send this flag to the class? Because the flag has to change the value, when one presses the stop button.
Edit 2: My solution so far is, to declerate a global variable with the name running_global. I changed self.get_thread.terminate() to running_global = False and I check constantly in my long_running_prog if the variable has been set False. I think this solution is ugly, so I would be pretty happy if someone has a better idea.
This is my code for the dialog where I start the thread:
class SomeDialog(QtGui.QDialog,
userinterface_status.Ui_status_window):
finished = QtCore.pyqtSignal(bool)
def __init__(self):
"""
:param raster: Coordinates which are going to be scanned.
"""
super(self.__class__, self).__init__() # old version, used in python 2.
self.setupUi(self) # It sets up layout and widgets that are defined
self.get_thread = SomeThread()
# Conencting the buttons
self.start_button.clicked.connect(self.start)
self.stop_button.clicked.connect(self.stop)
self.close_button.clicked.connect(self.return_main)
# Connecting other signals
self.connect(self.get_thread, QtCore.SIGNAL("stop()"), self.stop)
self.connect(self.get_thread, QtCore.SIGNAL("update_status_bar()"), self.update_status_bar)
def return_main(self):
"""
Function is excecuted, when close button is clicked.
"""
print("return main")
self.get_thread.terminate()
self.close()
def start(self):
"""
Starts the thread, which means that the run method of the thread is started.
"""
self.start_button.setEnabled(False)
self.get_thread.start()
def stop(self):
print("Stop programm.")
self.start_button.setEnabled(True)
self.get_thread.quit()
def end(self):
QtGui.QMessageBox.information(self, "Done!", "Programm finished")
def closeEvent(self, event):
"""
This method is called, when the window is closed and will send a signal to the main window to activaete the
window again.
:param event:
"""
self.finished.emit(True)
# close window
event.accept()
In the following class is the code for the thread:
class SomeThread(QtCore.QThread):
finished = QtCore.pyqtSignal(bool)
def __init__(self):
QtCore.QThread.__init__(self)
def __del__(self):
print("del")
self.wait()
def run(self):
self.prog = long_running_prog(self.emit) # Sending from the prog signals
self.prog.run()
self.prog.closeSystem() # Leaving the programm in a safe way.
So if one presses the stop button, the programm should instantly shut down in a save way. Is there a way to abort the class in a save way? For example can I pass a variable to the long_running_prog class which turns True, when one presses the stop button? If somethin like this is possible, could one tell me how?
Thanks for your help in advance
I hope you understand my problem.
Greetings
Hizzy
This is impossible to do unless prog.run(self) would periodically inspect a value of a flag to break out of its loop. Once you implement it, __del__(self) on the thread should set the flag and only then wait.
I have a script which has a record and stop button, the record button does an infinite loop, but it also blocks the other button (stop button). All I wanted to build is a process which starts at click of record button and stops are click of stop button. Here is the script:
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
### BEGIN LICENSE
# This file is in the public domain
### END LICENSE
from locale import gettext as _
from gi.repository import Gtk # pylint: disable=E0611
import logging
logger = logging.getLogger('recordme')
from recordme_lib import Window
from recordme.AboutRecordmeDialog import AboutRecordmeDialog
from recordme.PreferencesRecordmeDialog import PreferencesRecordmeDialog
class RecordmeWindow(Window):
__gtype_name__ = "RecordmeWindow"
record = False
def finish_initializing(self, builder): # pylint: disable=E1002
"""Set up the main window"""
super(RecordmeWindow, self).finish_initializing(builder)
self.AboutDialog = AboutRecordmeDialog
self.PreferencesDialog = PreferencesRecordmeDialog
# Code for other initialization actions should be added here.
self.button1 = self.builder.get_object('button1')
self.button2 = self.builder.get_object('button2')
def on_button1_clicked(self, widget):
while(not self.record):
print 'button1 clicked'
while gtk.events_pending():
gtk.main_iteration(False)
Any ideas about this problem ?
I encountered similar programs in WX, which is also event based. The best (and possibly only) way I found to solve the problem is to create a function that runs on a timer during the main loop. Mine ran periodically, but you could also just set it to wait and close the loop when you run your function. In GTK, you have to do this with another module, "gobject". Here is an example of a method that runs periodically in GTK.
import gobject
class gtk_object(object):
def __init__(self):
gobject.timeout_add(100, self.my_function)
def my_function(self):
#do something here, like stopping the loop or having a timer to stop the loop
return True
Assuming your record functionality is cpu-intensive and/or may block and/or needs soft realtime assurance, I would recommend moving it off to a separate "worker" thread.
Then, create a window and your buttons.
Here, when "record" is clicked, I signal the worker to start recording; when "stop" is clicked, signal worker to stop; Optionally, when stop is clicked, terminate the main loop if you want your app to exit.
Additional control logic to terminate the app when window is closed and terminate the worker thread correctly is at the very bottom.
#!/usr/bin/env python
import time
import logging
import threading
from gi.repository import Gtk
class Worker(threading.Thread):
should_record = False
quit = False
def run(self):
while not self.quit:
if self.should_record:
logging.warn("recording...")
# cpu-intensive code here
else:
time.sleep(0.1)
class MainWindow(Gtk.Window):
def __init__(self):
super(MainWindow, self).__init__()
self.worker = Worker()
self.worker.start()
hb = Gtk.Box()
self.add(hb)
record = Gtk.Button("Record")
stop = Gtk.Button("Stop")
hb.add(record)
hb.add(stop)
def command(arg):
self.worker.should_record = arg
record.connect("clicked", lambda _b: command(True))
stop.connect("clicked", lambda _b: command(False))
# optional, if you want to quit the app on stop as well
stop.connect("clicked", lambda _b: Gtk.main_quit())
if __name__ == "__main__":
main = MainWindow()
try:
# optional, if you want to support close window to quit app
main.connect("delete-event", Gtk.main_quit)
main.show_all()
Gtk.main()
finally:
main.worker.quit = True
main.worker.join()
old stuff
Ideally you wan to use Gtk.main() instead of Gtk.main_iteration() in Gtk+ 3.
In Gtk+ 2, module name was gtk rather than gi.repository.Gtk.
Then you can quit wit with:
Gtk.main_quit
def main_quit()
The Gtk.main_quit() function terminates the current main loop level
started by the most recent call to the Gtk.main() function. The
nesting level of the main loop is reduced by calling this function.
You can have several nested main loops, in which case, you'd have to quit each of those.
Alternatively you can also use gtk_dialog.run() then default action for a button is to exit the loop.
GTK+ (as most UI toolkits) is event-based. That means it runs internal "event loop" - a loop that collects and processes events, such as handling user input and redrawing windows. All event handlers are dispatched from main loop. In order to process events, loop must be "spinning".
In your example, you are blocking main loop:
def on_button1_clicked(self, widget):
while(not self.record):
print 'button1 clicked'
as long as this function does not finish, control does not return to main loop so it cannot process other events, or redraw windows.
You can add this snippet form PyGTK FAQ in order to allow main loop to process event in the meantime:
while gtk.events_pending():
gtk.main_iteration(False)