I want to update the progress bar from my main.py using thread approach.
if __name__ == "__main__":
app = QApplication(sys.argv)
uiplot = gui.Ui_MainWindow()
Update_Progressbar_thread = QThread()
Update_Progressbar_thread.started.connect(Update_Progressbar)
def Update_Progressbar():
progressbar_value = progressbar_value + 1
while (progressbar_value < 100):
uiplot.PSprogressbar.setValue(progressbar_value)
time.sleep(0.1)
uiplot.PSStart_btn.clicked.connect(Update_Progressbar_thread.start)
The problem is this approach james my GUI and I cannot click any buttons etc.
Alternatively how can I make it work ?
Thanks
Explanation:
With your logic you are invoking "Update_Progressbar" to run when the QThread starts, but where will "Update_Progressbar" run? Well, in the main thread blocking the GUI.
Solution:
Your goal is not to run "Update_Progressbar" when the QThread starts but to run in the thread that handles QThread. So in this case you can create a Worker that lives in the thread handled by QThread
class Worker(QObject):
progressChanged = pyqtSignal(int)
def work(self):
progressbar_value = 0
while progressbar_value < 100:
self.progressChanged.emit(progressbar_value)
time.sleep(0.1)
if __name__ == "__main__":
app = QApplication(sys.argv)
uiplot = gui.Ui_MainWindow()
thread = QThread()
thread.start()
worker = Worker()
worker.moveToThread(thread)
worker.progressChanged.connect(uiplot.PSprogressbar.setValue)
uiplot.PSStart_btn.clicked.connect(worker.work)
# ...
Related
I got two functions(do_work, do_work2) how do i multithread them?
If i use one Thread it works fine if i try to add one or more it wont work.
I want to have while True loops in my functions.
I already tried to change self.continue_run to : self.continue_run2 for second function but still wont work.
I hope someone can help me
import sys
from PyQt5.QtWidgets import (QWidget,
QPushButton, QApplication, QGridLayout)
from PyQt5.QtCore import QThread, QObject, pyqtSignal
import keyboard
import time
class Worker(QObject):
finished = pyqtSignal() # give worker class a finished signal
def __init__(self, parent=None):
QObject.__init__(self, parent=parent)
self.continue_run = True # provide a bool run condition for the class
def do_work(self):
while self.continue_run: # give the loop a stoppable condition
print("Thread 1")
self.finished.emit() # emit the finished signal when the loop is done
def do_work2(self):
while self.continue_run: # give the loop a stoppable condition
print("Thread 2")
self.finished.emit() # emit the finished signal when the loop is done
def stop(self):
self.continue_run = False # set the run condition to false on stop
class Gui(QWidget):
stop_signal = pyqtSignal() # make a stop signal to communicate with the worker in another thread
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# Buttons:
self.btn_start = QPushButton('Start')
self.btn_start.resize(self.btn_start.sizeHint())
self.btn_start.move(50, 50)
self.btn_stop = QPushButton('Stop')
self.btn_stop.resize(self.btn_stop.sizeHint())
self.btn_stop.move(150, 50)
# GUI title, size, etc...
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('ThreadTest')
self.layout = QGridLayout()
self.layout.addWidget(self.btn_start, 0, 0)
self.layout.addWidget(self.btn_stop, 0, 50)
self.setLayout(self.layout)
# Thread:
self.thread = QThread()
self.worker = Worker()
self.stop_signal.connect(self.worker.stop) # connect stop signal to worker stop method
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit) # connect the workers finished signal to stop thread
self.worker.finished.connect(self.worker.deleteLater) # connect the workers finished signal to clean up worker
self.thread.finished.connect(self.thread.deleteLater) # connect threads finished signal to clean up thread
self.thread.started.connect(self.worker.do_work)
self.thread.finished.connect(self.worker.stop)
self.thread.started.connect(self.worker.do_work2)
self.thread.finished.connect(self.worker.stop)
# Start Button action:
self.btn_start.clicked.connect(self.thread.start)
# Stop Button action:
self.btn_stop.clicked.connect(self.stop_thread)
self.show()
# When stop_btn is clicked this runs. Terminates the worker and the thread.
def stop_thread(self):
self.stop_signal.emit() # emit the finished signal on stop
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = Gui()
sys.exit(app.exec_())
One of the two main issues with your logic is that you've connected the finished signal to deleteLeter, and the result is that the second function would never be run anyway because you deleted both the thread and the worker.
The first important problem is that moving two functions to a thread doesn't make them run concurrently: a thread can only do one thing at a time, no matter if it's the "main" thread or not.
When the thread is started, it starts the first function the started signal is connected to, and the second will only run as soon as the first has returned (but not in your case, as explained before, since you're deleting both objects).
Consider the following example:
class WorkerTest(QtCore.QObject):
def firstFunction(self):
print('starting first function')
sleep(2)
print('finished first function')
def secondFunction(self):
print('starting second function')
sleep(2)
print('finished second function')
class Test(QtWidgets.QPushButton):
def __init__(self):
super().__init__('start')
self.worker = WorkerTest()
self.thread = QtCore.QThread()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.firstFunction)
self.thread.started.connect(self.worker.secondFunction)
self.clicked.connect(self.thread.start)
If you run the code above, you'll see that the second function will be run only as soon as the first has finished.
If you want to have two concurrent threads, create two threads.
If you want to have another thread to run after (or alternate to) the first, connect the finished signal to a function that disconnects the started from the first function and connects to the second one.
I have some computationally heavy task that I want to run in a loop after every 5 seconds without blocking the main event-loop. For this, I intend to use a QTimer and a separate thread to run it. I have tried the following code but it has not worked so far:
#pyqtSlot()
def heavy_task_function():
# Sleep for 10 seconds to simulate heavy computation
time.sleep(10)
print "First Timer Fired"
if __name__ == "__main__":
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
threaded_timer = ModbusComThread(heavy_task_function)
threaded_timer.start()
sys.exit(app.exec_())
Where:
class ModbusComThread(QThread):
def __init__(self, slot_function):
QThread.__init__(self)
self.slot_function = slot_function
self.send_data_timer = None
def run(self):
print "Timer started on different thread"
self.send_data_timer = QTimer(self)
self.send_data_timer.timeout.connect(self.slot_function)
self.send_data_timer.start(5000)
def stop(self):
self.send_data_timer.stop()
The slot_function is never fired by the QTimer in threaded_timer. Is my threading architecture correct?
A QTimer needs a running event-loop. By default, QThread.run() will start a local event-loop for the thread, but if you completely override it in the way that you have done, that won't happen - so the timer events will never be processed.
In general, when you need a local event-loop you should create a worker object to do all the processing and then use moveToThread to put it in a separate thread. If not, it's perfectly okay to override QThread.run().
The demo below shows how to do this. Note that it's very important to create the timer after the thread has started, otherwise it would be created in the wrong thread and its timer-events wouldn't be processed by the thread's event-loop. It's also important that all communication between the worker thread and the main thread is done via signals, so as to ensure thread-safety. Never try to directly perform GUI operations outside the main thread, as Qt does not support that at all. For the purposes of the demo, a second timer in the main thread is used to stop all processing after a fixed interval. If there was a GUI, user intervention via a button would achieve the same thing.
Demo:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class ModbusComWorker(QObject):
finished = pyqtSignal()
def start(self):
self._timer = QTimer(self)
self._timer.timeout.connect(self.process)
self._timer.start(2000)
def stop(self):
self._timer.stop()
self.finished.emit()
def process(self):
print('processing (thread: %r)' % QThread.currentThread())
QThread.sleep(3)
if __name__ == "__main__":
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
thread = QThread()
worker = ModbusComWorker()
worker.moveToThread(thread)
def finish():
print('shutting down...')
thread.quit()
thread.wait()
app.quit()
print('stopped')
worker.finished.connect(finish)
thread.started.connect(worker.start)
thread.start()
timer = QTimer()
timer.setSingleShot(True)
timer.timeout.connect(worker.stop)
timer.start(15000)
print('starting (thread: %r)' % QThread.currentThread())
sys.exit(app.exec_())
Output:
starting (thread: <PyQt5.QtCore.QThread object at 0x7f980d096b98>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
shutting down...
stopped
I have obtained a widget from a QtDesigner and converted .ui file to .py file by pyside. now I want that widget to organize a database and an apart threading.Thread (in same module) to open and read database and send to UDP. the fact that I know how to deal with all these apartly but when bringing together it is hard. should I use thread as a class inside my widget class which is:
def setupUi(self, Form):
...
def retranslateUi(self, Form):
...
if __name__ == "__main__":
...
Form.show()
and also can anyone give an example for running another thread with that widget?
Thanks in advance
I give you an example for using QThreads in PySide/PyQt to achieve multithreading and communication with other Widgets. I use it myself.
from PySide import QtCore
class Master(QtCore.QObject):
command = QtCore.Signal(str)
def __init__(self):
super().__init__()
class Worker(QtCore.QObject):
def __init__(self):
super().__init__()
def do_something(self, text):
print('in thread {} message {}'.format(QtCore.QThread.currentThread(), text))
if __name__ == '__main__':
app = QtCore.QCoreApplication([])
# give us a thread and start it
thread = QtCore.QThread()
thread.start()
# create a worker and move it to our extra thread
worker = Worker()
worker.moveToThread(thread)
# create a master object and connect it to the worker
master = Master()
master.command.connect(worker.do_something)
# call a method of the worker directly (will be executed in the actual thread)
worker.do_something('in main thread')
# communicate via signals, will execute the method now in the extra thread
master.command.emit('in worker thread')
# start the application and kill it after 1 second
QtCore.QTimer.singleShot(1000, app.quit)
app.exec_()
# don't forget to terminate the extra thread
thread.quit()
I am trying to build an GUI application with wxPy that perform some long scripted action. I have put the GUI and the script in different threads to prevent blocking. this worked well except after I close the GUI, the thread containing the script remain running.
below is a simplified version of my program(sorry if the code is hard to understand or missing parts, I am terrible at shortening programs)
class Form(wx.Frame):
...
def test(self, evt):
t2 = threading.Thread(target = self.runTest)
t2.start()
def runTest(self):
result = really_long_script()
def main():
app = wx.App(False)
form = form(app)
app.MainLoop()
t1 = threading.Thread(target = main)
t1.start()
Is there any way I can just kill the thread? right now the script still runs in background when I close the window.
Any help would be greatly appreciated!
Thanks,
John
If you set the thread to be a daemon thread, it will die with the main thread.
You can do this by adding the line t2.daemon = True before you call start
Edit:
Check this example, with the t.daemon = True line the thread dies when you close the frame, if you comment out that t.daemon = True line, the thread stays alive after the frame closes
import wx
import time
from threading import Thread
def print_something_forever(something):
while True:
print something
time.sleep(1)
class Frame(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent)
self.panel= wx.Panel(self)
t= Thread(target=print_something_forever,args=("Thread alive!",))
t.daemon= True
t.start()
self.Show()
if __name__ == "__main__":
app= wx.App(False)
Frame(None)
app.MainLoop()
Python doesn't support killing/destroying threads, probably because memory leaks, resources loss, etc.
try this "Threadizing class" :D
class Run_Other_Thread(threading.Thread):
"Raises a child thread \
I'm busy dying, rather lying - _P0W !"
def __init__(self,func_name,*args): #Constructor
self._func=func_name
self._func_args=args
threading.Thread.__init__(self)
def run(self): # Start Dying
try:
print("\n** Running New Thread :"+self._func.func_name)
except:
print("\n** Running New Thread :"+self._func.__name__)
self._func(*self._func_args)
def stop(self):
print('!! Stopped')
def __del__(self):#Constructor
try:
print('\n ## Farewell :'+self._func.func_name)
except:
print('\n ## Farewell :'+self._func.__name__)
You may run GUI as:(and try closing)
def GUI():
app= wx.App(False)
Frame(None)
app.MainLoop()
if __name__ == '__main__':
"Tkinter GUI in Thread helps in De-bugging"
Run_Other_Thread(GUI).start() # Release command window control
I tried using self.terminate() in the QThread class, and also self.thread.terminate() in the GUI class. I also tried putting self.wait() in both cases. However, there are two scenarios that happen:
1) The thread does not terminate at all, and the GUI freezes waiting for the thread to finish. Once the thread finished, the GUI unfreezes and everything is back to normal.
2) The thread indeed does terminate, but at the same time it freezes the entire application.
I also tried using self.thread.exit(). No joy.
To further clarify, I am trying to implement a user-abort button in GUI which would terminate the executing of the thread at any point in time.
Thanks in advance.
EDIT:
Here is the run() method:
def run(self):
if self.create:
print "calling create f"
self.emit(SIGNAL("disableCreate(bool)"))
self.create(self.password, self.email)
self.stop()
self.emit(SIGNAL("finished(bool)"), self.completed)
def stop(self):
#Tried the following, one by one (and all together too, I was desperate):
self.terminate()
self.quit()
self.exit()
self.stopped = True
self.terminated = True
#Neither works
And here is the GUI class' method for aborting the thread:
def on_abort_clicked(self):
self.thread = threadmodule.Thread()
#Tried the following, also one by one and altogether:
self.thread.exit()
self.thread.wait()
self.thread.quit()
self.thread.terminate()
#Again, none work
From the Qt documentation for QThread::terminate:
Warning: This function is dangerous and its use is discouraged. The
thread can be terminated at any point in its code path. Threads can be
terminated while modifying data. There is no chance for the thread to
clean up after itself, unlock any held mutexes, etc. In short, use
this function only if absolutely necessary.
It's probably a much better idea to re-think your threading strategy such that you can e.g. use QThread::quit() to signal the thread to quit cleanly, rather than trying to get the thread to terminate this way. Actually calling thread.exit() from within the thread should do that depending on how you have implemented run(). If you'd like to share the code for your thread run method that might hint as to why it doesn't work.
This is what I did:
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
stop_flag = 1
...
#=========500 ms class ===================
class Timer500msThread(QThread):
signal_500ms = pyqtSignal(str)
....
def timer500msProcess(self):
if MainWindow.stop_flag == 0 :
self.timer_500ms.stop()
#==========
#=========Main Window ===================
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
MainWindow.stop_flag=0 #this sets the flag to 0 and when the next 500ms triggers the
#the thread ends
print("Program Ending")
I had a similar problem and solved it with use of pyqtSignals and pyqtSlots.
You could create a pyqtSignal in your MainWindow-class and use the aboutToQuit-function to your QApplication instance.
Then you connect this aboutToQuit-function with another which emit a signal to the slot in your separate thread.
Then you can define a stop() function in this Thread which runs if the signal is emitted.
In this case the thread would not terminate during its work.
MainWindow:
class mainSignals(QObject):
isClosed = pyqtSignal()
class mainwindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(mainwindow, self).__init__()
self.mainSignal = mainSignals()
...
...
if __name__ = "__main__":
# This function runs if the application is closed.
# (app.aboutToQuit.connect(windowClose))
def windowClose():
window.mainSignal.isClosed.emit() # Emit the signal to the slot in (sepThread)
app = QApplication(sys.argv)
app.aboutToQuit.connect(windowClose)
window = mainwindow()
window.show()
sys.exit(app.exec())
sepThread:
class sepThread(QRunnable):
def __init__(self, parent):
super(sepThread,self).__init__()
self._parent = parent
self.mainOpen = True
self._parent.mainSignal.isClosed.connect(self.stopThread)
# If the signal was emitted by the Mainapplication
# the stopThread function runs and set the mainOpen-attribute to False
def stopThread(self):
self.mainOpen = False
def run(self):
while self.mainOpen == True:
# Do something in a loop while mainOpen-attribute is True
...
...