I'm trying to update the label to display the numbers being counted down when it receives the number through the queue. I am able to see it being printed in the console but the label does not change. Any help or suggestion would be helpful!
Here is my code:
import tkinter as tk
import time
import threading
import queue
class GUIApp:
def __init__(self):
self.root = tk.Tk()
self.buttonCountDown = tk.Button(text='Count Down', command=self.countDownAction)
self.buttonCountDown.pack()
self.label = tk.Label(text='default')
self.label.pack()
self.queue = queue.Queue()
self.root.mainloop()
def countDown(self, seconds):
for i in range(seconds, 0, -1):
self.queue.put(i)
time.sleep(1)
def listenToQueue(self):
while True:
try:
if self.queue.empty() == False:
print(self.queue.get(0))
self.label['text'] = self.queue.get(0)
elif self.queue.empty() == True:
pass
except queue.Empty:
pass
def countDownAction(self):
listenThread = threading.Thread(target=self.listenToQueue)
listenThread.start()
thread = threading.Thread(target=self.countDown, args=(5,))
thread.start()
thread.join()
app = GUIApp()
The first thing you need to know is Queue.get() removes the item and returns it, similar to dict.pop(). So when you do print(self.queue.get(0)), the item is already removed from the queue. You have to assign it to a variable first if you want to both print and config it:
def listenToQueue(self):
while True:
try:
if self.queue.empty() == False:
s = self.queue.get(0)
print (s)
self.label['text'] = s
elif self.queue.empty() == True:
pass
except queue.Empty:
pass
Next, calling thread.join() will wait for the thread terminates. You don't need to call this method at all in your current setup.
def countDownAction(self):
listenThread = threading.Thread(target=self.listenToQueue)
listenThread.start()
thread = threading.Thread(target=self.countDown, args=(5,))
thread.start()
#thread.join() #not required
Related
I want to stop the threaded python application with keyboard interrupt. One thread captures the images and put them in queue and the other thread saves the images to hardisk.
The code is here
import numpy as np
import threading
import time
from multiprocessing import Queue
import cv2
from datetime import datetime
import os
#import matplotlib.pyplot as plt
import sys
frames = Queue(50)
exitProgram = False
saveImages = True
class ImageGrabber(threading.Thread):
def __init__(self, ID):
threading.Thread.__init__(self)
self.ID=ID
self.cam=cv2.VideoCapture(ID)
self.fps = self.cam.get(cv2.CAP_PROP_FPS)
self.w=self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)
self.h=self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
(self.grabbed, self.frame) =self.cam.read()
cv2.imwrite("testImage.png",self.frame)
print(f"Camera Opened with fps {self.fps}, width = {self.w}, and height = {self.h}")
self.stopped = False
# self.adjustB = adjustBrightness(0.75)
def run(self):
global frames
global exitProgram
while not self.stopped:
if not self.grabbed or exitProgram is True:
print("Exit Command reached")
self.stop()
self.cam.release()
else:
(self.grabbed, self.frame) =self.cam.read()
frames.put(self.frame)
def stop(self):
self.stopped = True
class imageSaveThread(threading.Thread):
def __init__(self,grabber,filePath):
threading.Thread.__init__(self)
global saveImages
self.dateTime = self.getDateStamp()
self.imgName = filePath + 'img_' + self.dateTime + '_'
self.cntr = 0
def getDateStamp(self):
filedate = str(datetime.now())
filedate = filedate[0:-7]
filedate = filedate.replace(':', '_')
filename = filedate
return filename
def run(self):
global frames
while True:
if(not frames.empty()):
self.Currframe=frames.get()
cv2.imwrite(self.imgName + str(self.cntr).zfill(6) + '.png',self.Currframe)
self.cntr = self.cntr + 1
print(f"Queue Size in writing = {frames.qsize()} and fram number = {self.cntr}")
elif exitProgram is True:
print("Exit Command imageSaveThread reached")
print(f"Final Queue Size at exit = {frames.qsize()}")
break
def main():
if saveImages == True:
savefilePath = 'D:/111/'
grabber = ImageGrabber(0)
imageSaveThr = imageSaveThread(grabber,savefilePath)
grabber.start()
imageSaveThr.start()
e = threading.Event()
# imageSaveThread.join()
# grabber.join()
print ('Press CTRL-C to interrupt')
while grabber.isAlive():
try:
time.sleep(5) #wait 1 second, then go back and ask if thread is still alive
except KeyboardInterrupt: #if ctrl-C is pressed within that second,
#catch the KeyboardInterrupt exception
e.set() #set the flag that will kill the thread when it has finished
exitProgram=True
print ('Exiting...')
grabber.join()
imageSaveThr.join()
else:
videoCodec = 'h264'
videoExt = 'mkv'
if __name__ == '__main__':
main()
I I replace the main function and directly call the program then it works and exits the threads like the code below
import numpy as np
import threading
import time
from multiprocessing import Queue
import cv2
from datetime import datetime
import os
#import matplotlib.pyplot as plt
import sys
frames = Queue(50)
exitProgram = False
saveImages = True
class ImageGrabber(threading.Thread):
def __init__(self, ID):
threading.Thread.__init__(self)
self.ID=ID
self.cam=cv2.VideoCapture(ID)
self.fps = self.cam.get(cv2.CAP_PROP_FPS)
self.w=self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)
self.h=self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
(self.grabbed, self.frame) =self.cam.read()
cv2.imwrite("testImage.png",self.frame)
print(f"Camera Opened with fps {self.fps}, width = {self.w}, and height = {self.h}")
self.stopped = False
# self.adjustB = adjustBrightness(0.75)
def run(self):
global frames
global exitProgram
while not self.stopped:
if not self.grabbed or exitProgram is True:
print("Exit Command reached")
self.stop()
self.cam.release()
else:
(self.grabbed, self.frame) =self.cam.read()
frames.put(self.frame)
def stop(self):
self.stopped = True
class imageSaveThread(threading.Thread):
def __init__(self,grabber,filePath):
threading.Thread.__init__(self)
global saveImages
self.dateTime = self.getDateStamp()
self.imgName = filePath + 'img_' + self.dateTime + '_'
self.cntr = 0
def getDateStamp(self):
filedate = str(datetime.now())
filedate = filedate[0:-7]
filedate = filedate.replace(':', '_')
filename = filedate
return filename
def run(self):
global frames
while True:
if(not frames.empty()):
self.Currframe=frames.get()
cv2.imwrite(self.imgName + str(self.cntr).zfill(6) + '.png',self.Currframe)
self.cntr = self.cntr + 1
print(f"Queue Size in writing = {frames.qsize()} and fram number = {self.cntr}")
elif exitProgram is True:
print("Exit Command imageSaveThread reached")
print(f"Final Queue Size at exit = {frames.qsize()}")
break
savefilePath = 'D:/111/'
grabber = ImageGrabber(0)
imageSaveThr = imageSaveThread(grabber,savefilePath)
grabber.start()
imageSaveThr.start()
e = threading.Event()
# imageSaveThread.join()
# grabber.join()
print ('Press CTRL-C to interrupt')
while grabber.isAlive():
try:
time.sleep(5) #wait 1 second, then go back and ask if thread is still alive
except KeyboardInterrupt: #if ctrl-C is pressed within that second,
#catch the KeyboardInterrupt exception
e.set() #set the flag that will kill the thread when it has finished
exitProgram=True
print ('Exiting...')
grabber.join()
imageSaveThr.join()
I am not able to understand the reason. Can anyone tell me what is the reason?
PS(Would've added this as an comment, but I can't since I don't meet the requirements)
I'll leave the explanation to somebody else, since I was not able to get your code running on my computer.
But have you looked at adding an sys.exit(0) at the end of your except block? It made a difference for me whenever I tried to use KeyboarInterrupt to get out of this little code snippet:
import sys
if __name__ == "__main__":
while True:
try:
print("Hello")
except KeyboardInterrupt:
print("Exiting...")
sys.exit(0)
PS: Use Ctrl+Z and kill the process manually if you end up removing the sys.exit() part.
I have simple script for watchdog on network device. Script monitors response from PING command. If there is no answer then second thread executes and first thread is stopped. If second thread is finished then first thread is resumed (checking ping). If there is no answer then following message appears:
RuntimeError: threads can only be started once
Here is my code:
#!/usr/bin/python
import os
import time
import sqlite3
from ablib import Pin
import threading
led=Pin('W9','OUTPUT')
class threadout1(threading.Thread):
def run(self):
while True:
conn = sqlite3.connect('database/database.db')
cur = conn.cursor()
cur.execute("SELECT * FROM watchdog")
rows_output = cur.fetchall()
time.sleep(1)
if rows_output[0][1] == "ping":
response = os.system("ping -c 1 " + rows_output[0][2])
if response != 0:
print "bad"
rest.start()
rest.join()
class restart(threading.Thread):
def run(self):
led.on()
time.sleep(15)
led.off()
thr = threadout1()
rest = restart()
thr.start()
You can either create the restart thread every time you need it
if response != 0:
print "bad"
restart_thread = restart()
restart_thread.start()
restart_thread.join()
or use Events
class restart_thread(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
self.evt.wait()
# do stuff
self.evt.clear()
class threadout(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
if #other thread needs to run once
self.evt.set()
evt = threading.Event()
restart_thread = restart(evt)
restart_thread.start()
pinging_thread = threadout(evt)
pinging_thread.start()
To make the pinging_thread wait for the restart_thread to finish, you could use another Event.
I am working on a simple python script that sends serial communication command to an Arduino.
I have a main-thread which sends the commands and then another child-thread which waits for user input.
This should be simple but I want this process to be very fast.
For example if user inputs "stop", I want to stop the main thread immediately.
The way I was thinking of doing it was by throwing exceptions from the child thread and catch those in the main thread, but I soon realized that I was quite impossible since each thread runs on its own context and child thread can not send an exception to the main thread.
I am now trying to change the child class with attributes and check the attributes in the main thread but I will result on lots of confusing if-statements in the main thread and it is also not a good solution since it does not stop the execution immediately.
class User_input_thread (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.isPauseRequested = False
self.isStopRequested = False
def run(self):
while (True):
s = raw_input(">")
if s.startswith("pause"):
self.isPauseRequested = True
elif s.startswith("start"):
self.isPauseRequested = False
elif s.startswith("stop"):
self.isStopRequested = True
This is the basically main thread:
def start_demo():
user_input_thread = User_input_thread()
user_input_thread.start()
while (True):
if (not user_input_thread.isPauseRequested):
# DO SOME WORK HERE
time.sleep(10)
if (not user_input_thread.isStopRequested):
# DO SOME WORK HERE
time.sleep(10)
What is an easy way to achieve the behavior I want?
Please don't link to other pages that discuss exceptions between Threads. At least explain how I should use the code or modify my code so I can understand.
EDIT (following code has been tested)
The problem with the following code is that if event takes infinite time there is no way to stop the child process.
Run the example and write "start".... then the child starts to work and then if you send another event "such as stop" it will be ignored.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import time
class child_thread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop_request = threading.Event()
self.start_request = threading.Event()
self.pause_request = threading.Event()
def run(self):
while 1:
if self.stop_request.isSet():
print 'stop request is set!!'
self.stop_request.clear()
while 1:
print "stop running..."
time.sleep(1)
if self.pause_request.isSet():
print 'pause request is set!!'
self.pause_request.clear()
while 1:
print "pause running..."
time.sleep(1)
if self.start_request.isSet():
print 'start request is set!!!'
self.start_request.clear()
while 1:
print "start running..."
time.sleep(1)
def show(self, event='stop'):
if event == 'stop':
self.stop_request.set()
elif event == 'start':
self.start_request.set()
elif event == 'pause':
self.pause_request.set()
elif event == 'exit':
print 'enter exit'
self.join(1)
def main_thread():
t1 = child_thread()
t1.start()
while 1:
s = raw_input(">")
t1.show(event=s)
if s == 'exit':
break
print 'Done! child thread has been killed'
main_thread()
It's not a best practice to for a child thread to control its parent thread. We usually do the other way around.
In your case, you can put user input in the main thread and put your work in the child thread.
Communication between threads in Python is usually done by Event and signal. You can google this if you show interests.
Here is an example for your case. Hope it helps. I simplify the if conditions which is not the key point here. :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import time
class child_thread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop_request = threading.Event()
self.start_request = threading.Event()
self.pause_request = threading.Event()
def run(self):
while 1:
if self.stop_request.isSet():
print 'stop request is set!!'
break
if self.pause_request.isSet():
print 'pause request is set!!'
break
if self.start_request.isSet():
print 'start request is set!!!'
break
print 'no stop/pause/start is set!!'
time.sleep(1)
def join(self, timeout=None, event='stop'):
if event == 'stop':
self.stop_request.set()
elif event == 'start':
self.start_request.set()
elif event == 'pause':
self.pause_request.set()
super(child_thread, self).join(timeout)
def main_thread():
t1 = child_thread()
t1.start()
while 1:
s = raw_input(">")
t1.join(event=s)
break
print 'Done! chile thread has been killed'
main_thread()
And here is a very good article for communications between threads in Python. http://eli.thegreenplace.net/2011/12/27/python-threads-communication-and-stopping
EDIT:
Here is code for your updated request.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import time
class child_thread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop_request = threading.Event()
self.start_request = threading.Event()
self.pause_request = threading.Event()
def run(self):
while 1:
if self.stop_request.isSet():
print 'stop request is set!!'
self.stop_request.clear()
continue
if self.pause_request.isSet():
print 'pause request is set!!'
self.pause_request.clear()
continue
if self.start_request.isSet():
print 'start request is set!!!'
self.start_request.clear()
continue
def show(self, event='stop'):
if event == 'stop':
self.stop_request.set()
elif event == 'start':
self.start_request.set()
elif event == 'pause':
self.pause_request.set()
elif event == 'exit':
print 'enter exit'
self.join(1)
def main_thread():
t1 = child_thread()
t1.start()
while 1:
s = raw_input("\n>enter exit to exit:")
t1.show(event=s)
if s == 'exit':
break
print 'Done! chile thread has been killed'
main_thread()
I've been looking into a way to directly change variables in a running module.
What I want to achieve is that a load test is being run and that I can manually adjust the call pace or whatsoever.
Below some code that I just created (not-tested e.d.), just to give you an idea.
class A():
def __init__(self):
self.value = 1
def runForever(self):
while(1):
print self.value
def setValue(self, value):
self.value = value
if __name__ == '__main__':
#Some code to create the A object and directly apply the value from an human's input
a = A()
#Some parallelism or something has to be applied.
a.runForever()
a.setValue(raw_input("New value: "))
Edit #1: Yes, I know that now I will never hit the a.setValue() :-)
Here is a multi-threaded example. This code will work with the python interpreter but not with the Python Shell of IDLE, because the raw_input function is not handled the same way.
from threading import Thread
from time import sleep
class A(Thread):
def __init__(self):
Thread.__init__(self)
self.value = 1
self.stop_flag = False
def run(self):
while not self.stop_flag:
sleep(1)
print(self.value)
def set_value(self, value):
self.value = value
def stop(self):
self.stop_flag = True
if __name__ == '__main__':
a = A()
a.start()
try:
while 1:
r = raw_input()
a.set_value(int(r))
except:
a.stop()
The pseudo code you wrote is quite similar to the way Threading / Multiprocessing works in python. You will want to start a (for example) thread that "runs forever" and then instead of modifying the internal rate value directly, you will probably just send a message through a Queue that gives the new value.
Check out this question.
Here is a demonstration of doing what you asked about. I prefer to use Queues to directly making calls on threads / processes.
import Queue # !!warning. if you use multiprocessing, use multiprocessing.Queue
import threading
import time
def main():
q = Queue.Queue()
tester = Tester(q)
tester.start()
while True:
user_input = raw_input("New period in seconds or (q)uit: ")
if user_input.lower() == 'q':
break
try:
new_speed = float(user_input)
except ValueError:
new_speed = None # ignore junk
if new_speed is not None:
q.put(new_speed)
q.put(Tester.STOP_TOKEN)
class Tester(threading.Thread):
STOP_TOKEN = '<<stop>>'
def __init__(self, q):
threading.Thread.__init__(self)
self.q = q
self.speed = 1
def run(self):
while True:
# get from the queue
try:
item = self.q.get(block=False) # don't hang
except Queue.Empty:
item = None # do nothing
if item:
# stop when requested
if item == self.STOP_TOKEN:
break # stop this thread loop
# otherwise check for a new speed
try:
self.speed = float(item)
except ValueError:
pass # whatever you like with unknown input
# do your thing
self.main_code()
def main_code(self):
time.sleep(self.speed) # or whatever you want to do
if __name__ == '__main__':
main()
The following code executes two threads (multithread), each with different time delays so that each thread will finish at a different time.
Once both threads are finished module display1.py issues a print statement saying they are BOTH finished.
I would like module display1.py to issue a 'finished' statement for EACH thread AS EACH thread finishes
How can i do this ... amendments to my working code appreciated! I'd like to change as little of the current code as possible so a better form of variable transfer between the two modules might be what I'm after
display1.py
from threads1 import *
manager = ThreadManager()
manager.start(False)
print (manager.GetResults())
threads1.py
from threading import Thread
import time
class ThreadManager:
def __init__(self):
pass
def start(self, answer):
self.answer = answer
thread_refs = []
t1 = MyThread(70, 'Not finished')
t1.daemon = True
t1.start()
t2 = MyThread(2, 'Not finished')
t2.daemon = True
t2.start()
while True:
if t1.AskFinished == 'Finished' and t2.AskFinished == 'Finished': #If I break the loop after EACH site, Only the first to finish will be sent via GetResults to display1.py
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
def GetResults(self):
global results
return(results)
class MyThread(Thread):
def __init__(self, SleepWait, AskFinished):
Thread.__init__(self)
self.SleepWait = SleepWait
self.AskFinished = AskFinished
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
What you have here (entering a very tight check loop in the main thread) is a very naive approach to threading in many languages, but especially in python where GIL contention will just slow the threads down a great bit.
What is a better idea is instead using queue.Queue to push info when a thread is completed. This allows the main thread to block on the queue instead, which is less CPU intensive as well as allowing you to know (out of order) which one is finished.
The changes you would need to make:
at the top of the module threads1.py:
import queue
finished_queue = queue.Queue()
in your start():
num_finished = 0
while True:
info = finished_queue.get()
num_finished += 1
if info is t1:
print("t1 finished")
else:
print("t2 finished")
if num_finished == 2:
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
and finally in run():
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
finished_queue.put(self)
Some more fundamental modifications I'd make is actually pushing the result into the queue and then fetching the results out, skipping the extra step before GetResults. Furthermore, if GetResults had to stay, I'd pass them through a field on self e.g. self.results = [t1.AskFinished, t2.AskFinished]
Update:
Ok, so you want to know more about how to have display1.py print the results. It would be helpful if you could explain why it matters, because that might make a difference in how you should do this, but here's a first approach:
# threads1.py
from threading import Thread
import time
class ThreadManager:
def __init__(self):
self.threads = {}
def start(self):
t1 = MyThread(4)
t1.daemon = True
t1.start()
self.threads[1] = t1
t2 = MyThread(1)
t2.daemon = True
t2.start()
self.threads[2] = t2
def is_alive(self, thread_id):
return self.threads[thread_id].is_alive()
def GetResults(self): # or you could just access results directly
return self.results
class MyThread(Thread):
def __init__(self, SleepWait):
Thread.__init__(self)
self.SleepWait = SleepWait
def run(self):
time.sleep(self.SleepWait)
And then...
# display1.py
from threads1 import *
manager = ThreadManager()
manager.start()
t1_state = t2_state = True
while manager.is_alive(1) or manager.is_alive(2):
time.sleep(1)
if manager.is_alive(1) != t1_state:
print("t1 finished")
t1_state = manager.is_alive(1)
if manager.is_alive(2) != t2_state:
print("t2 finished")
t2_state = manager.is_alive(2)
if not manager.is_alive(1) and not manager.is_alive(2):
print("Both Finished")
break
You should eventually consider using a Queue as suggested by Crast; but let's focus on getting this right first.
Original Post:
There are a number of problems with this code.
First, you should use t1.is_alive() to check if a thread is finished. There's no need to reimplement it with AskFinished.
Second, the while True: loop in threads1.py is doing nothing at a furious rate while it waits for your threads to terminate. Take a look at the cpu usage while this is running if you don't believe me. You should throw a time.sleep(1) statement in there.
Third, why are you using a global var to return your results? That's a really strange thing to do. Just store it in self!
And finally, why does display1.py have to print the messages? Why can't thread1.py do that?
With these four points in mind, here's a thread1.py that works more sensibly:
from threading import Thread
import time
class ThreadManager:
def __init__(self):
self.results = None
def start(self, answer): # why is "answer" here?
self.answer = answer
thread_refs = []
t1 = MyThread(4, 'Not finished')
t1.daemon = True
t1.start()
t2 = MyThread(1, 'Not finished')
t2.daemon = True
t2.start()
t1_state = t2_state = True
while t1.is_alive() or t2.is_alive():
time.sleep(1)
if t1.is_alive() != t1_state:
print("t1 finished")
t1_state = t1.is_alive()
if t2.is_alive() != t2_state:
print("t2 finished")
t2_state = t2.is_alive()
if not t1.is_alive() and not t2.is_alive():
self.results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
def GetResults(self): # or you could just access results directly
return self.results
class MyThread(Thread):
def __init__(self, SleepWait, AskFinished):
Thread.__init__(self)
self.SleepWait = SleepWait
self.AskFinished = AskFinished
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
Now, this still doesn't do exactly what you wanted, because you asked for display.py to do the displaying. To make that work, you'd have to put your while True loop in display.py and add an ThreadManager.is_alive() method that display.py could use to check whether a thread is alive or not. If you want to see how to do that let me know.
Im not familiar with threading but since no answers yet ill give it a shot.
In this:
Cant you just add two if statements before hand?
while True:
if t1.askFinished == 'Finished':
print("t1 Finished")
if t2.askFinished == 'Finished':
print("t2 Finished")
if t1.AskFinished == 'Finished' and t2.AskFinished == 'Finished': #If I break the loop after EACH site, Only the first to finish will be sent via GetResults to display1.py
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
edit: I tried changing your code as little as possible... it's not very well written though tbh. (No offense)