I have a gui with buttons to start a loop and another button to stop the loop. I use a global variable - run for the loop. 1 to loop and 0 to stop loop. Below is the code I am using.
import pygtk
pygtk require('2.0')
import gtk
import threading
import time
run = 0
mythread = ""
class daqGui:
def start_program(self):
global run
print ("start program")
i = 0
while run:
print ("Count = %s\n" % i)
i += 1
time.sleep(2)
def start_run(self, widget):
global run
global mythread
run = 1
mythread = threading.Thread(target=self.start_program, args=())
mythread.start()
def stop_run:
global run
print("Halt loop")
run = 0
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("delete_event", self.delete_event)
self.window.set_border_width(10)
self.window.set_size_request(600,400)
table = gtk.Table(10, 6, True)
self.window.add(table)
run_button = gtk.Button("Start Run")
run_button.connect("clicked", self.start_run)
run_button.set_size_request(100,30)
table.attach(run_button, 0, 1, 0, 1, False, False, xpadding=0, ypaddig=0)
run_button.show()
stop_button = gtk.Button("Halt Run")
stop_button.connect("clicked", self.stop_run)
stop_button.set_size_request(100,30)
table.attach(stop_button, 2, 3, 0, 1, False, False, xpadding=0, ypaddig=0)
stop_button.show()
def main():
gtk.main()
if __name__=="__main__":
daqGui()
main()
What happens when you run this code, click on the Start Button, you will see the program display i = 1, i = 2 and so forth. Click on the stop button, the program does not stop. Exit the gui, and the loop still continues. You have to kill the process to stop it. What am I doing wrong?? Thanks!
Global variables are sketchy with multithreaded code, and especially with a GUI.
The following code starts a "clock" thread which outputs the time every second.
After two seconds, a 2nd thread sets the stop_event flag, which is shared between both threads.
When the clock thread checks the event object, it sees that it's been flagged, so the loop exits. Generally, the event hasn't been set, so it waits for 1.0 seconds then reruns the loop.
source
import threading, time
def display_clock(event):
while not event.wait(1):
print 'now:',time.strftime('%X')
print("stop_event set")
def stop_clock(event):
time.sleep(3)
event.set()
def main():
stop_event = threading.Event()
threading.Thread(
target=display_clock, args=[stop_event],
).start()
threading.Thread(
target=stop_clock, args=[stop_event],
).start()
# wait for all threads to die
for th in threading.enumerate():
if th != threading.current_thread():
th.join()
if __name__ == "__main__":
main()
output
now: 15:17:16
now: 15:17:17
now: 15:17:18
stop_event set
Related
I am confused for showing waiting message.
For my program,
Step 1, it keep looping to show the waiting message.
Step 2, If the trigger_file exist, it stop the waiting message and run main_process()
Step 3, After finishing main_process, it show the waiting message again.
I tried to use variable waiting to stop the waiting message but it is not working
I am not sure how to use async/await function and multithreadubg for this case.
Thank you
import os
import time
import threading
waiting = True
trigger_file = r'D:\Desktop\OK'
def main_process():
print('1')
time.sleep(5)
print('2')
time.sleep(5)
print('3')
time.sleep(5)
print('4')
time.sleep(5)
print('5')
def print_waiting(): # animation of waiting
while(waiting):
for loading_symbol in ['|','/','-','\\','|','/','-','\\','|']:
print('\r[INFO] Waiting for trigger... '+loading_symbol,end="")
time.sleep(0.2)
def triggerListener(): # trigger a function if the file exist
while(True):
if os.path.exists(trigger_file):
global waiting
waiting=False
print('\n[INFO] main process start')
main_process()
waiting=True
if __name__ == "__main__":
# creating thread
t1 = threading.Thread(target=print_waiting)
t2 = threading.Thread(target=triggerListener)
# starting thread 1
t1.start()
# starting thread 2
t2.start()
# wait until thread 1 is completely executed
t1.join()
# wait until thread 2 is completely executed
t2.join()
# both threads completely executed
print("Done!")
Expected Output:
[INFO] Waiting for trigger... -
[INFO] main process start
1
2
3
4
5
[INFO] Waiting for trigger... -
Finally, I use threading.Lock to solve the problem. acquire the lock and release the lock after finishing the function.
class Print_waiting(threading.Thread):
def __init__(self,lock):
threading.Thread.__init__(self)
self.running = True
self.lock = lock
def run(self): # animation of waiting
while self.running:
for loading_symbol in ['|','/','-','\\','|','/','-','\\','|']:
self.lock.acquire()
print('\r[INFO] Waiting for trigger... '+loading_symbol,end="")
self.lock.release()
time.sleep(0.2)
def stop(self):
self.running = False
class TriggerListener(threading.Thread):
def __init__(self,lock):
threading.Thread.__init__(self)
self.running = True
self.lock = lock # for mutex
def run(self): # trigger a function if the file exist
while(self.running):
if os.path.exists(trigger_file):
self.lock.acquire()
print('\n[INFO] main process start')
Program.main()
self.lock.release()
def stop(self):
self.running = False
if __name__ == "__main__":
lock = threading.Lock()
waiting_anime = Print_waiting(lock)
Trigger_RPA = TriggerListener(lock)
Trigger_RPA.start()
waiting_anime.start()
Trigger_RPA.join()
waiting_anime.join()
This simple code example:
import threading
import time
class Monitor():
def __init__(self):
self.stop = False
self.blocked_emails = []
def start_monitor(self):
print("Run start_monitor")
rows = []
while not self.stop:
self.check_rows(rows)
print("inside while")
time.sleep(1)
def check_rows(self, rows):
print('check_rows')
def stop_monitoring(self):
print("Run stop_monitoring")
self.stop = True
if __name__ == '__main__':
monitor = Monitor()
b = threading.Thread(name='background_monitor', target=monitor.start_monitor())
b.start()
b.join()
for i in range(0, 10):
time.sleep(2)
print('Wait 2 sec.')
monitor.stop_monitoring()
How can I run background thread, in mine case background_monitor without blocking main thread?
I wanted to background_monitor thread stopped on after stop_monitoring will be called
I mine example, the for loop from main thread never called and the background is running forever.
There are two issues with your current code. Firstly, you're calling monitor.start_monitor on this line, whereas according to the docs
target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called
This means that you need to pass it as a function rather than calling it. To fix this, you should change the line
b = threading.Thread(name='background_monitor', target=monitor.start_monitor())
to
b = threading.Thread(name='background_monitor', target=monitor.start_monitor)
which passes the function as an argument.
Secondly, you use b.join() before stopping the thread, which waits for the second thread to finish before continuing. Instead, you should place that below the monitor.stop_monitoring().
The corrected code looks like this:
import threading
import time
class Monitor():
def __init__(self):
self.stop = False
self.blocked_emails = []
def start_monitor(self):
print("Run start_monitor")
rows = []
while not self.stop:
self.check_rows(rows)
print("inside while")
time.sleep(1)
def check_rows(self, rows):
print('check_rows')
def stop_monitoring(self):
print("Run stop_monitoring")
self.stop = True
if __name__ == '__main__':
monitor = Monitor()
b = threading.Thread(name='background_monitor', target=monitor.start_monitor)
b.start()
for i in range(0, 10):
time.sleep(2)
print('Wait 2 sec.')
monitor.stop_monitoring()
b.join()
The context:
I'm building a Graphical Interface with Qt creator and the "behaviour" file in python. A test version of my GUI is:
The expected behaviour:
I am running 2 different threads which are referred to the same function with different input arguments. With the SELECTOR button I can assign the value of 1 or 2 to a variable (and display it)
The button Start thread enables the correct thread to start (the first time).
The loop should be turned off by the stop button by modifying the global running variable.
This is my code
# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui, uic
import sys
import threading
import time
import Queue
running = False
first_thread = None
second_thread = None
form_class = uic.loadUiType("simple2.ui")[0]
q = Queue.Queue()
select = 0
def action(string, queue): #function called by threads
global running
while(running):
phrase = string
if queue.qsize() < 10:
queue.put(phrase)
#else:
# print queue.qsize()
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
#buttons
self.startButton.clicked.connect(self.start_clicked)
self.stopButton.clicked.connect(self.stop_clicked)
self.selector.clicked.connect(self.sel_click)
#variables
self.first = False
self.second = False
#queue
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.update_phrase)
self.timer.start(1)
def start_clicked(self): #start button callback
global select
if select > 0:
global running
running = True
print "started"
if (not self.first) & (select == 1):
first_thread.start()
self.first = True
if (not self.second) & (select == 2):
second_thread.start()
self.second = True
self.startButton.setEnabled(False)
self.startButton.setText('Starting...')
def stop_clicked(self): #stop button callback
global running
running = False
print "stopped"
self.startButton.setEnabled(True)
self.startButton.setText('Start Thread')
def sel_click(self): #selector button callback
global select
if select < 2:
select = select + 1
else:
select = 1
self.thread_counter.setText(str(select))
def update_phrase(self): #looping function
global running
if (not q.empty()) & running:
self.startButton.setText('Thread on')
abc = q.get()
print abc
def closeEvent(self, event):
global running
running = False
if __name__ == "__main__":
first_thread = threading.Thread(target=action, args = ("first", q))
second_thread = threading.Thread(target=action, args = ("second", q))
app = QtGui.QApplication(sys.argv)
w = MyWindowClass(None)
w.setWindowTitle('Multiple threads test in python')
w.show()
app.exec_()
For now, each thread should simple print on terminal their arguments ("First" or "Second").
If threads are started for the first time, my code works. But I would like to switch between threads infinite times.
Since threads cannot be stopped, is there a way to "pause" them?
I cannot find a solution, I hope someone will help me also with a piece of code. Thank you in advance
You can use Lock class to do that, a simple example would be:
import threading
lock = threading.Lock()
//here it will be lock
lock.acquire() # will block if lock is already held
...
then in other side do
//this will wake up
lock.release()
you can read more here http://effbot.org/zone/thread-synchronization.htm
I have a pyqt gui and calling a long process (ffmpeg) which I put on a separate thread to not block the gui. I then want to update a progress bar when one command of a longer list of commands finishes. The problem is, that I can't call a function in the gui thread out of the worker thread. So I let run a ticker in the worker thread, but when I update the progress bar with a while loop and reading the ticker value, the gui gets blocked again. How can I solve this. I used currently python threading and not Qthread.
Thx for any help!
import threading, pexpect
self.cmd_list = ['ffmpeg -i file outfile','and so on']
self.stop_proc = False
self.executeCMD()
def spawn_ffmpeg_cmd(self):
for cmd in self.cmd_list:
if self.stop_proc == False:
thread = pexpect.spawn(cmd)
print "\nstarted: %s" % cmd
cpl = thread.compile_pattern_list([pexpect.EOF,"frame= *\d+ fps=*\d+",'(.+)'])
while True:
i = thread.expect_list(cpl, timeout=None)
if i == 0: # EOF
print "the sub process exited"
self.pgticker += 1
break
elif i == 1:
frame_number_fps = thread.match.group(0)
print frame_number_fps
thread.close
elif i == 2:
pass
self.startButton.setEnabled(True)
def executeCMD(self):
self.startButton.setEnabled(False)
self.pgticker = 0
threading.Thread(target=self.spawn_ffmpeg_cmd, name="_proc").start()
def stopprocess(self):
self.stop_proc = True
self.cmd_list = []
os.system('pkill ffmpeg')
self.pgticker = len(self.cmd_list)
self.startButton.setEnabled(True)
def updateProgress(self):
pgfactor = 100 / len(self.cmd_list)
progress = 0.0
progress = pgfactor*int(self.pgticker)
self.progressBar.setProperty("value", progress)
In short: Move to QThread and use Qt's signals and slots, they are the preferred way to communicate between threads.
This answer provides some examples how this could look like:
https://stackoverflow.com/a/6789205/2319400
In your case, using the "SomeObject" version from the above could look like this:
class Worker(QtCore.QObject):
madeProgress = QtCore.pyqtSignal([int])
finished = QtCore.pyqtSignal()
def __init__(self, cmdlist):
self.cmdlist = cmdlist
def run(self):
for icmd, cmd in enumerate(self.cmdlist):
# execute your work
# processCommand(cmd)
# signal that we've made progress
self.madeProgress.emit(icmd)
# emit the finished signal - we're done
self.finished.emit()
Then move this worker to a QThread instance you create.
Following the pattern from the linked answer, you can then connect the madeProgress signal
to the setValue slot of a progressbar:
workerThread = QThread()
workerObject = Worker(cmdlist)
workerObject.moveToThread(workerThread)
workerThread.started.connect(workerObject.run)
workerObject.finished.connect(workerThread.quit)
# create a progressbar with min/max according to
# the length of your cmdlist
progressBar = QProgressBar()
progressBar.setRange(0, len(cmdlist))
# connect the worker's progress signal with the progressbar
workerObject.madeProgress.connect(progressBar.setValue)
# start the thread (starting your worker at the same time)
workerThread.start()
Created a gui with three buttons, one to execute a program, one to halt program, and one to clear the textbuffer . Also have a textview to display text from program. Here are the pertenant functions.
def clear_display(self, widget):
global textbuffer
start = textbuffer.get_start_iter()
end = textbuffer.get_end_iter()
textbuffer.delete(start, end)
self.print_display("")
def start_program(self):
global textbuffer
global mythread
global run
i = 0
while run:
msg = "Count = %s\n" % i
self.print_display(msg)
print ( "Count = %s\n" % i)
i += 1
mythread._Thread__stop()
def start_run(self, widget):
global run
global mythread
run = 1
mythread = Thread(target=self.start_program, args=())
mythread.start()
def stop_run(self, widget):
global run
global textbuffer
msg = "Halt Run\n"
self.print_display(msg)
run = 0
def print_display(self, msg):
global textbuffer
start = textbuffer.get_start_iter()
end = textbuffer.get_end_iter()
text = textbuffer.get_text(start, end)
text = text + msg
textbuffer.set_text(text)
The problem I am having is getting the data to display to the textview. Initial run will produce a partial display. If I press the button clear to the display then press the start button, I will receive some data and some data from the previous display interleaved which should have been deleted. Can anyone see what I am doing incorrectly?
You'll want to use a queue.Queue I'll leave your implementation to you, but the classic use is:
import threading, queue
q = queue.Queue()
def make_something(q, num_things_to_make):
for _ in num_things_to_make
new_thing = make_a_thing()
q.put(new_thing)
def use_something(q):
while True:
thing_to_use = q.get()
do_a_thing(thing_to_use)
q.task_done()
producer = threading.Thread(target=lambda: make_something(q, 8))
producer.start()
for _ in range(num_workers):
worker = threading.Thread(target=lambda: use_something(q))
worker.daemon = True
worker.start()
q.join() # block until all tasks are done