Pyqt UI non responsive on Multithreading with python
-
I'm new to PyQt and python, i was working with a program which had a simple UI and which collects some data from a hardware
I have 2 files handling everything and a thread to collect data , but my problem is that, when thread is running, UI becomes un responsive..
All UI portion is handled by file "Analog_ui.py"
All data capture portion is handled by file "AI_DAQ.py"Code for Analog_ui.py
"___________________________Create GUI____________________________________________" from itertools import starmap import sys #import QApplications and the required widgets from PyQt.QtWidgets from PyQt5 import QtWidgets,QtCore, QtGui from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGridLayout, QLineEdit from PyQt5.QtWidgets import QVBoxLayout from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QMainWindow from PyQt5 import uic import AI_DAQ #exec(open("Analogdata.py").read()) class Configure_MainWindow(object): def Ui_Configure(self, MainWindow): MainWindow.resize(1100,600) self.centralwidget=QtWidgets.QWidget(MainWindow) #adding start and stop pushbutton self.pushButton1=QtWidgets.QPushButton(self.centralwidget) self.pushButton1.setGeometry(QtCore.QRect(1000,500,93,28)) self.pushButton2=QtWidgets.QPushButton(self.centralwidget) self.pushButton2.setGeometry(QtCore.QRect(1000,550,93,28)) self.Physicalchannel=QtWidgets.QLineEdit(self.centralwidget) self.Physicalchannel.setGeometry(QtCore.QRect(40,50,140,21)) self.Physicalchannel.setText("Dev1/ai0") #self.Physicalchannel. self.PhysicalchannelLabel=QtWidgets.QLabel(self.centralwidget) self.PhysicalchannelLabel.setGeometry(QtCore.QRect(40,25,111,21)) self.MaxVolt=QtWidgets.QSpinBox(self.centralwidget) self.MaxVolt.setGeometry(QtCore.QRect(190,120,100,21)) self.MaxVolt.setRange(0,5) self.MaxVolt.setValue(5) self.MaxVoltLabel=QtWidgets.QLabel(self.centralwidget) self.MaxVoltLabel.setGeometry(QtCore.QRect(190,90,60,28)) self.MinVolt=QtWidgets.QSpinBox(self.centralwidget) self.MinVolt.setGeometry(QtCore.QRect(40,120,100,21)) self.MinVolt.setRange(-5,0) self.MinVolt.setValue(-5) self.MinVoltLabel=QtWidgets.QLabel(self.centralwidget) self.MinVoltLabel.setGeometry(QtCore.QRect(40,90,60,28)) self.SampleRate=QtWidgets.QSpinBox(self.centralwidget) self.SampleRate.setGeometry(QtCore.QRect(220,50,70,21)) self.SampleRate.setRange(0,400) self.SampleRate.setValue(200) self.SampleRateLabel=QtWidgets.QLabel(self.centralwidget) self.SampleRateLabel.setGeometry(QtCore.QRect(220,25,60,28)) """self.graphicsView= QtWidgets.QGraphicsView(self.centralwidget) self.graphicsView.setGeometry(QtCore.QRect(265,10,321,281)) self.graphicsViewLabel=QtWidgets.QLabel(self.centralwidget) self.graphicsViewLabel.setGeometry(QtCore.QRect(100,10,60,10))""" MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def start(self): print("start button clicked") #a=self.SampleRate.get() AI_DAQ.cfg_read_task("Dev1/ai0") def stop(self): print("Stopping Tread") AI_DAQ.stop_task() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Analog Data Acquisition")) self.pushButton1.setText(_translate("MainWindow", "Start")) self.pushButton2.setText(_translate("MainWindow","Stop")) self.PhysicalchannelLabel.setText(_translate("MainWindow","Physical Channel")) self.MaxVoltLabel.setText(_translate("MainWindow","Max Voltage")) self.MinVoltLabel.setText(_translate("MainWindow", "Min Voltage")) self.SampleRateLabel.setText(_translate("MainWindow","Rate")) #self.graphicsViewLabel.setText(_translate("MainWindow","AnalogWave form")) self.pushButton1.clicked.connect(self.start) self.pushButton2.clicked.connect(self.stop) """_______________Main function_________________ """ if __name__=='__main__': #Create an instance of QApplication app=QtWidgets.QApplication(sys.argv) MainWindow=QtWidgets.QMainWindow() # Show GUI ui=Configure_MainWindow() ui.Ui_Configure(MainWindow) MainWindow.show() #Windows are hidden by default #Execute the eventloop sys.exit(app.exec_())
Code for AI_DAQ.py
import threading from turtle import delay from matplotlib.pyplot import get import nidaqmx from nidaqmx import constants import nidaqmx.system import time #variables declaration buffer_in_size=100 buffer_in_size_cfg = round(buffer_in_size * 1) task_in=nidaqmx.Task() running=True def stop_task(): running=False def ai_read(): print("Inside Thread") task_in.start() while(running): data=task_in.read(number_of_samples_per_channel=100) #print(data) time.sleep(.01) task_in.stop() #task_in.close() def cfg_read_task(chname): print(chname) running=True #configure and setup the AItask system=nidaqmx.system.System.local() device=system.devices['Dev1'] phys_chan=device.ai_physical_chans['ai0'] task_in.ai_channels.add_ai_voltage_chan(chname,min_val=-5,max_val=5) task_in.timing.cfg_samp_clk_timing (rate=100,sample_mode=constants.AcquisitionType.CONTINUOUS,samps_per_chan=buffer_in_size_cfg) print("Task configured") #configure thread starting thread_ai=threading.Thread(target=ai_read) thread_ai.start() thread_ai.join()
here when we start the thread in AI_DAQ.py the whole UI freezes
How can i resolve the same?i would also like to know, how i can pass the data from AI_DAQ.py to Analog_ui.py
-
I'm new to PyQt and python, i was working with a program which had a simple UI and which collects some data from a hardware
I have 2 files handling everything and a thread to collect data , but my problem is that, when thread is running, UI becomes un responsive..
All UI portion is handled by file "Analog_ui.py"
All data capture portion is handled by file "AI_DAQ.py"Code for Analog_ui.py
"___________________________Create GUI____________________________________________" from itertools import starmap import sys #import QApplications and the required widgets from PyQt.QtWidgets from PyQt5 import QtWidgets,QtCore, QtGui from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGridLayout, QLineEdit from PyQt5.QtWidgets import QVBoxLayout from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QMainWindow from PyQt5 import uic import AI_DAQ #exec(open("Analogdata.py").read()) class Configure_MainWindow(object): def Ui_Configure(self, MainWindow): MainWindow.resize(1100,600) self.centralwidget=QtWidgets.QWidget(MainWindow) #adding start and stop pushbutton self.pushButton1=QtWidgets.QPushButton(self.centralwidget) self.pushButton1.setGeometry(QtCore.QRect(1000,500,93,28)) self.pushButton2=QtWidgets.QPushButton(self.centralwidget) self.pushButton2.setGeometry(QtCore.QRect(1000,550,93,28)) self.Physicalchannel=QtWidgets.QLineEdit(self.centralwidget) self.Physicalchannel.setGeometry(QtCore.QRect(40,50,140,21)) self.Physicalchannel.setText("Dev1/ai0") #self.Physicalchannel. self.PhysicalchannelLabel=QtWidgets.QLabel(self.centralwidget) self.PhysicalchannelLabel.setGeometry(QtCore.QRect(40,25,111,21)) self.MaxVolt=QtWidgets.QSpinBox(self.centralwidget) self.MaxVolt.setGeometry(QtCore.QRect(190,120,100,21)) self.MaxVolt.setRange(0,5) self.MaxVolt.setValue(5) self.MaxVoltLabel=QtWidgets.QLabel(self.centralwidget) self.MaxVoltLabel.setGeometry(QtCore.QRect(190,90,60,28)) self.MinVolt=QtWidgets.QSpinBox(self.centralwidget) self.MinVolt.setGeometry(QtCore.QRect(40,120,100,21)) self.MinVolt.setRange(-5,0) self.MinVolt.setValue(-5) self.MinVoltLabel=QtWidgets.QLabel(self.centralwidget) self.MinVoltLabel.setGeometry(QtCore.QRect(40,90,60,28)) self.SampleRate=QtWidgets.QSpinBox(self.centralwidget) self.SampleRate.setGeometry(QtCore.QRect(220,50,70,21)) self.SampleRate.setRange(0,400) self.SampleRate.setValue(200) self.SampleRateLabel=QtWidgets.QLabel(self.centralwidget) self.SampleRateLabel.setGeometry(QtCore.QRect(220,25,60,28)) """self.graphicsView= QtWidgets.QGraphicsView(self.centralwidget) self.graphicsView.setGeometry(QtCore.QRect(265,10,321,281)) self.graphicsViewLabel=QtWidgets.QLabel(self.centralwidget) self.graphicsViewLabel.setGeometry(QtCore.QRect(100,10,60,10))""" MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def start(self): print("start button clicked") #a=self.SampleRate.get() AI_DAQ.cfg_read_task("Dev1/ai0") def stop(self): print("Stopping Tread") AI_DAQ.stop_task() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Analog Data Acquisition")) self.pushButton1.setText(_translate("MainWindow", "Start")) self.pushButton2.setText(_translate("MainWindow","Stop")) self.PhysicalchannelLabel.setText(_translate("MainWindow","Physical Channel")) self.MaxVoltLabel.setText(_translate("MainWindow","Max Voltage")) self.MinVoltLabel.setText(_translate("MainWindow", "Min Voltage")) self.SampleRateLabel.setText(_translate("MainWindow","Rate")) #self.graphicsViewLabel.setText(_translate("MainWindow","AnalogWave form")) self.pushButton1.clicked.connect(self.start) self.pushButton2.clicked.connect(self.stop) """_______________Main function_________________ """ if __name__=='__main__': #Create an instance of QApplication app=QtWidgets.QApplication(sys.argv) MainWindow=QtWidgets.QMainWindow() # Show GUI ui=Configure_MainWindow() ui.Ui_Configure(MainWindow) MainWindow.show() #Windows are hidden by default #Execute the eventloop sys.exit(app.exec_())
Code for AI_DAQ.py
import threading from turtle import delay from matplotlib.pyplot import get import nidaqmx from nidaqmx import constants import nidaqmx.system import time #variables declaration buffer_in_size=100 buffer_in_size_cfg = round(buffer_in_size * 1) task_in=nidaqmx.Task() running=True def stop_task(): running=False def ai_read(): print("Inside Thread") task_in.start() while(running): data=task_in.read(number_of_samples_per_channel=100) #print(data) time.sleep(.01) task_in.stop() #task_in.close() def cfg_read_task(chname): print(chname) running=True #configure and setup the AItask system=nidaqmx.system.System.local() device=system.devices['Dev1'] phys_chan=device.ai_physical_chans['ai0'] task_in.ai_channels.add_ai_voltage_chan(chname,min_val=-5,max_val=5) task_in.timing.cfg_samp_clk_timing (rate=100,sample_mode=constants.AcquisitionType.CONTINUOUS,samps_per_chan=buffer_in_size_cfg) print("Task configured") #configure thread starting thread_ai=threading.Thread(target=ai_read) thread_ai.start() thread_ai.join()
here when we start the thread in AI_DAQ.py the whole UI freezes
How can i resolve the same?i would also like to know, how i can pass the data from AI_DAQ.py to Analog_ui.py
@Albitroz
start()
callscfg_read_task()
. That callsthread_ai.start()
but then immediatelythread_ai.join()
. Which blocks waiting on the task to complete, and that in turn blocks the UI.If you need to use a thread let it run as a thread, not block till it completes.
i would also like to know, how i can pass the data from AI_DAQ.py to Analog_ui.py
Use Qt signals & slots.
-
Hi thanks for the solution, it worked.
Qt signals & slots., i was planing to use queue instead, is that a bad idea?@Albitroz said in Pyqt UI non responsive on Multi threading with python:
Hi thanks for the solution, it worked.
Qt signals & slots., i was planing to use queue instead, is that a bad idea?specifically, to transfer data which is generated by thread
-
@Albitroz said in Pyqt UI non responsive on Multi threading with python:
Hi thanks for the solution, it worked.
Qt signals & slots., i was planing to use queue instead, is that a bad idea?specifically, to transfer data which is generated by thread
@Albitroz
You can use a queue, that in itself is fine. You should also be careful to mutex, either code yourself or it must be built into the queue.The signal/slot would be needed at least to let the other side know (in your case, the main thread) that new data has been queued. How else would you intend to do that bit?
-
@Albitroz
You can use a queue, that in itself is fine. You should also be careful to mutex, either code yourself or it must be built into the queue.The signal/slot would be needed at least to let the other side know (in your case, the main thread) that new data has been queued. How else would you intend to do that bit?
-
@Albitroz
start()
callscfg_read_task()
. That callsthread_ai.start()
but then immediatelythread_ai.join()
. Which blocks waiting on the task to complete, and that in turn blocks the UI.If you need to use a thread let it run as a thread, not block till it completes.
i would also like to know, how i can pass the data from AI_DAQ.py to Analog_ui.py
Use Qt signals & slots.
@JonB in the same code, im facing issues in stopping my thread,
im calling a function from my main codedef stop_task(): running=False
which will set the running variable to False, and in my thread, im checking for that variable
def ai_read(): print("Inside Thread") task_in.start() while(running): data=task_in.read(number_of_samples_per_channel=100) #print(data) time.sleep(.01)
I have verified the value of running with printf inside the thread, its not getting updated, is there any thread safe variable?
-
@JonB i was planing to check the queue size at the other end at a pre defined interval, if queue size is greater than a pre defined size, i just dequeue my queue and get the data. thats my plan is that possible?
@Albitroz said in Pyqt UI non responsive on Multithreading with python:
@JonB i was planing to check the queue size at the other end at a pre defined interval, if queue size is greater than a pre defined size, i just dequeue my queue and get the data. thats my plan is that possible?
It will work, bit it's not as efficient as it could be.
To do that, your main thread will have to have that interval timer and check each time. Plus often(?) the queue will not be of that size, and it will be a waste of time. Wouldn't you like to know when the thread actually pushes another item to the queue, and only check then/when the count is reached? That is what having the thread send a signal (when it queues), and the main thread have a slot attached to the signal, would achieve.
The only time where "timed-polling" is (almost) just as good is if you only want the receiver to pull from the queue at those interval points, not as soon as the data arrives. All in all it's hard to see why signalling would not be a preferable solution.
Sooner or later you will have to use signals & slots in Qt programming, so why not now?
-
@JonB in the same code, im facing issues in stopping my thread,
im calling a function from my main codedef stop_task(): running=False
which will set the running variable to False, and in my thread, im checking for that variable
def ai_read(): print("Inside Thread") task_in.start() while(running): data=task_in.read(number_of_samples_per_channel=100) #print(data) time.sleep(.01)
I have verified the value of running with printf inside the thread, its not getting updated, is there any thread safe variable?
@Albitroz said in Pyqt UI non responsive on Multithreading with python:
I have verified the value of running with printf inside the thread, its not getting updated, is there any thread safe variable?
All your threading stuff is being done with Python threading. So your questions are best asked in a Python forum. I don't know when
stop_task()
gets called whileai_read()
is in a loop.Up to you, but I would use Qt signals & slots for everything like this.
-
@Albitroz said in Pyqt UI non responsive on Multithreading with python:
@JonB i was planing to check the queue size at the other end at a pre defined interval, if queue size is greater than a pre defined size, i just dequeue my queue and get the data. thats my plan is that possible?
It will work, bit it's not as efficient as it could be.
To do that, your main thread will have to have that interval timer and check each time. Plus often(?) the queue will not be of that size, and it will be a waste of time. Wouldn't you like to know when the thread actually pushes another item to the queue, and only check then/when the count is reached? That is what having the thread send a signal (when it queues), and the main thread have a slot attached to the signal, would achieve.
The only time where "timed-polling" is (almost) just as good is if you only want the receiver to pull from the queue at those interval points, not as soon as the data arrives. All in all it's hard to see why signalling would not be a preferable solution.
Sooner or later you will have to use signals & slots in Qt programming, so why not now?
-
Hi,
What issues are you having ?
Note that depending on what you do in your loop you should put break statement at regular intervals to allow for an easier way out of it.
-
Hi,
What issues are you having ?
Note that depending on what you do in your loop you should put break statement at regular intervals to allow for an easier way out of it.
@SGaist HI
Currently im facing issues, in transferring data from different threadsIm splitting my entire project to different ".py " file for easy debugging, in this case how can i transfer data from one file to other
to be pressie,
I have my main ".py file which runs the pyqt event loop, i have another file which generates some data through a thread". How can i pass data from this thread to my main python file, can i use signals and slots to transfer data to the main file which holds the UI
my main intention is to send data from different threads to the main file which runs QT event and UI so that i can display data from these threads
which is better, implementing a queue or signals and slots?
-
The files play no role here.
If you are using QThread then you can easily use signals and slots to transfert data.
Otherwise are you using Python's multithreading or multiprocessing ? If you really want parallel processing with Python, you shall use the later.
-
The files play no role here.
If you are using QThread then you can easily use signals and slots to transfert data.
Otherwise are you using Python's multithreading or multiprocessing ? If you really want parallel processing with Python, you shall use the later.
@SGaist
I will be handling around 20 threads of different functionalities, and all are time critical activities, so stack overflow suggests using Multiprocessing library which one do you prefer?if i go with multiprocessing library, how can i transfer data between files , my main action is to display these processed data on UI and thats the reason i use PyQt