Reputation: 66
I have been looking for solutions in the stackoverflow and other pyqt tutorials on how to overcome the GUI freeze problem in pyqt4. There are similar topics that suggest the following methods to rectify it:
app.processEvents()
in your loop. This gives Qt the chance to process events and redraw the GUI.I have tried the above methods but still my GUI is stuck. I have given below the structure of code that is causing the problem.
# a lot of headers
from PyQt4 import QtCore, QtGui
import time
import serial
from time import sleep
from PyQt4.QtCore import QThread, SIGNAL
getcontext().prec = 6
getcontext().rounding = ROUND_CEILING
adbPacNo = 0
sdbPacNo =0
tmPacNo = 0
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
#ADB Widget
class Ui_ADB(object):
def setupUi(self, ADB):
ADB.setObjectName(_fromUtf8("ADB"))
ADB.resize(1080, 212)
self.gridLayout_2 = QtGui.QGridLayout(ADB)
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.label_20 = QtGui.QLabel(ADB)
font = QtGui.QFont()
font.setBold(True)
font.setUnderline(True)
font.setWeight(75)
self.label_20.setFont(font)
self.label_20.setAlignment(QtCore.Qt.AlignCenter)
self.label_20.setObjectName(_fromUtf8("label_20"))
.
# Rate X
self.rateX = QtGui.QLineEdit(ADB)
self.rateX.setReadOnly(True)
self.rateX.setObjectName(_fromUtf8("rateX"))
self.gridLayout.addWidget(self.rateX, 1, 6, 1, 1)
# Rate Z
self.rateZ = QtGui.QLineEdit(ADB)
self.rateZ.setReadOnly(True)
self.rateZ.setObjectName(_fromUtf8("rateZ"))
self.gridLayout.addWidget(self.rateZ, 1, 10, 1, 1)
# Rate Y
self.rateY = QtGui.QLineEdit(ADB)
self.rateY.setReadOnly(True)
self.rateY.setObjectName(_fromUtf8("rateY"))
self.gridLayout.addWidget(self.rateY, 1, 8, 1, 1)
# qv2
# qv1
# rateValid
# qv3
# qs
# and a lot more....
def retranslateUi(self, ADB):
# this contains the label definintions
# SDB Widget
class Ui_SDB(object):
def setupUi(self, SDB):
# again lot of fields to be displayed
def retranslateUi(self, SDB):
# this contains the label definintions
def sdbReader(self, sdbData):
#--- CRC Checking -------------------------------------------------#
global sdbPacNo
sdbPacNo+=1
tmCRC = sdbData[0:4];
data = sdbData[4:];
tmCRCResult = TM_CRCChecker(data,tmCRC)
if (tmCRCResult == 1):
print 'SDB Packet verification : SUCCESS!'
else:
print 'SDB packet verification : FAILED!'
quit()
#--- Type ID and Length -------------------------------------------#
# code to check the ID and length of the packet
#--- Reading out SDB into its respective variables ----------------#
# the code that performs the calculations and updates the parameters for GUI
## make thread for displaying ADB and SDB separately
# ADB Thread
class adbThread(QThread):
def __init__(self,Ui_ADB, adbData):
QThread.__init__(self)
self.adbData = adbData
self.Ui_ADB = Ui_ADB
def adbReader(self,adbData):
global adbPacNo
adbPacNo+=1;
#--- CRC Checking -------------------------------------------------#
tmCRC = self.adbData[0:4];
data = self.adbData[4:];
tmCRCResult = TM_CRCChecker(data,tmCRC)
if (tmCRCResult == 1):
print 'ADB Packet verification : SUCCESS!'
else:
print 'ADB packet verification : FAILED!'
#--- Type ID and Length -------------------------------------------#
# code to check the ID and length
#--- Reading out ADB into respective variables --------------------#
qvUnit = decimal.Decimal(pow(2,-30))
qv1 = qvUnit*decimal.Decimal(int(ADBlock[0:8],16))
qv1 = qv1.to_eng_string()
print 'qv1 = '+ qv1
self.Ui_ADB.qv1.setText(qv1)
# similar to above code there are many such variables that have to
# be calculated and printed on the respective fields.
def __del__(self):
self.wait()
def run(self):
self.adbReader(self.adbData)
myMessage = "ITS F** DONE!"
self.emit(SIGNAL('done(QString)'), myMessage)
print "I am in ADB RUN"
# SDB Thread
class sdbThread(QThread):
#similar type as of adbThread
# Global Variable to set the number of packets
packets=0
class mainwindow(QtGui.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(1153, 125)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.formLayout = QtGui.QFormLayout(self.centralwidget)
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label = QtGui.QLabel(self.centralwidget)
self.label.setObjectName(_fromUtf8("label"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label)
self.serialStatus = QtGui.QLineEdit(self.centralwidget)
self.serialStatus.setReadOnly(True)
self.serialStatus.setObjectName(_fromUtf8("serialStatus"))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.serialStatus)
self.label_2 = QtGui.QLabel(self.centralwidget)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_2)
self.lineEdit = QtGui.QLineEdit(self.centralwidget)
self.lineEdit.setReadOnly(True)
self.lineEdit.setObjectName(_fromUtf8("lineEdit"))
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1153, 25))
self.menubar.setObjectName(_fromUtf8("menubar"))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName(_fromUtf8("statusbar"))
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
################################################################
#Setting up ADB
self.Ui_ADB = Ui_ADB()
self.myADB = QtGui.QWidget()
self.Ui_ADB.setupUi(self.myADB)
self.myADB.show()
# Setting up SDB
self.Ui_SDB = Ui_SDB()
self.mySDB = QtGui.QWidget()
self.Ui_SDB.setupUi(self.mySDB)
# Setting up the serial communication
self.tmSerial = serial.Serial('/dev/ttyACM0',9600)
self.sdb_Thread = sdbThread(self.Ui_SDB, self.mySDB)
buff = ''
tempByte= ''
counter =1
while counter<10:
# this reads the header of the SP
# Simulating the RTT signal trigger
self.tmSerial.write('y')
print "serial opened to read header"
tmSerialData = self.tmSerial.read(8*8)
print "tmSerialData="+str(tmSerialData)
littleEndian = tmSerialData[0:8*8]
# Converts the bitstream of SP header after converting to bigEndian
bufferData = bitstream_to_hex(littleEndian)
print "bufferData="+str(bufferData)
# Reads the header info : First 8 bytes
headerINFO = readHeader(bufferData)
# checking the packets in the headerINFO
# ADB & SDB present
global tmPacNo
if (headerINFO['adbINFO'] == 1 and headerINFO['sdbINFO'] == 1):
print 'Both ADB and SDB info are present'
tmPacNo+=1;
# Need to call both ADB and SDB
# Statements for reading the ADB
bufferData = tmSerial.read(42*8) # ADB packet bitstream
self.adbPacket = bitstream_to_hex(bufferData)
# Calling ADB thread
self.adb_Thread = adbThread(self.Ui_ADB, self.adbPacket)
self.adb_Thread.start()
#self.connect(self.adb_Thread, SIGNAL("finished()"),self.done)
self.connect(self.adb_Thread, SIGNAL("done(QString)"), self.done)
QtGui.QApplication.processEvents()
# IGNORED FOR NOW...
## Statements for reading the SDB
#bufferData = self.tmSerial.read(46*8) # SDB packet bitstream
#self.sdbPacket = bitstream_to_hex(bufferData)
## Calling SDB thread
#self.sdb_Thread.run(self.sdbPacket)
elif (headerINFO['adbINFO'] == 1 and headerINFO['sdbINFO'] == 0):
print 'ADB INFO only present'
tmPacNo+=1;
# Statements for reading the ADB
bufferData = self.tmSerial.read(42*8) # ADB packet bitstream
self.adbPacket = bitstream_to_hex(bufferData)
# Calling ADB thread
self.adb_Thread = adbThread(self.Ui_ADB, self.adbPacket)
self.adb_Thread.start()
#self.connect(self.adb_Thread, SIGNAL("finished()"),self.done)
self.connect(self.adb_Thread, SIGNAL("done(QString)"), self.done)
QtGui.QApplication.processEvents()
# IGNORED FOR NOW...
#elif (headerINFO['adbINFO'] == 0 and headerINFO['sdbINFO'] == 1):
#print 'SDB INFO only present'
#tmPacNo+=1;
## Statements for reading the SDB
#bufferData = self.tmSerial.read(46*8) # SDB packet bitstream
#self.sdbPacket = bitstream_to_hex(bufferData)
## Calling SDB thread
#self.sdb_Thread.run(sdbPacket)
#while (self.adb_Thread.isFinished() or self.sdb_Thread.isFinished() is False):
#print "waiting to complete adb Thread"
counter+=1
################################################################
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.label.setText(_translate("MainWindow", "Serial Communication Status", None))
self.label_2.setText(_translate("MainWindow", "No. of SP_Packets Received", None))
####################################################################
def done(self,someText):
print someText + "the value has been updated"
self.myADB.show()
# This program converts the little endian bitstream -> BigEndian -> hex
def bitstream_to_hex(bitStream):
#global littleEndian
# small code for conversion
if __name__== "__main__":
import sys
# setting up the GUI
app = QtGui.QApplication(sys.argv)
main = mainwindow()
main.show()
sys.exit(app.exec_())
In the above code it can be noticed that threads have been implemented but I am not sure what am I doing wrong? I have put the long running loop adbreader()
in the thread but the values are not updated in GUI responsively. I could only view the output only after the while loop has run 10 times.
Also, I have tried using QtGui.QApplication.processEvents()
and this somehow manages to print the values in GUI, but I am not happy with that approach.(Not happy because, it sometimes skips printing while on iteration 5 and it prints the values in iteration 7 next) Some guidance on how to use threads in this purpose would be greatly appreciated.
Upvotes: 1
Views: 1351
Reputation: 66
As suggested by three_pinapples , I tried to offload the program to by creating more thread. Further I was calling the thread
that performed the whole serial writing and reading in while
loop. This caused the problem of calling the thread only once no matter the loop. I am not sure why, but I guess it could be because of the same object has been called again and again in the loop? Not sure.
I figured out a way around this issue by using a signal/slot mechanism acting as recursive function that keeps the thread in infinite running mode irrespective of the while loop. I have posted the modified structure of the code below:
# a lot of headers
from PyQt4 import QtCore, QtGui
import time
import serial
from time import sleep
from PyQt4.QtCore import QThread, SIGNAL
getcontext().prec = 6
getcontext().rounding = ROUND_CEILING
adbPacNo = 0
sdbPacNo =0
tmPacNo = 0
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
#ADB Widget
class Ui_ADB(object):
def setupUi(self, ADB):
# Rate X
# Rate Z
# Rate Y
# qv2
# qv1
# rateValid
# qv3
# qs
# and a lot more....
def retranslateUi(self, ADB):
# this contains the label definintions
## make thread for displaying ADB and SDB separately
# ADB Thread
class adbThread(QThread):
def __init__(self,Ui_ADB, adbData):
def adbReader(self,adbData):
global adbPacNo
adbPacNo+=1;
#--- CRC Checking -------------------------------------------------#
#--- Type ID and Length -------------------------------------------#
# code to check the ID and length
#--- Reading out ADB into respective variables --------------------#
# similar to above code there are many such variables that have to
# be calculated and printed on the respective fields.
def __del__(self):
self.wait()
def run(self):
self.adbReader(self.adbData)
myMessage = "ITS F** DONE!"
self.emit(SIGNAL('done(QString)'), myMessage)
print "I am in ADB RUN"
# SDB Thread
class sdbThread(QThread):
#similar type as of adbThread
# Global Variable to set the number of packets
packets=0
# WorkerThread : This runs individually in the loop & call the respective threads to print.
class workerThread(QThread):
readComplete = QtCore.pyqtSignal(object)
def __init__(self, tmSerial, Ui_ADB, myADB, Ui_SDB, mySDB):
QThread.__init__(self)
self.tmSerial = tmSerial
self.Ui_ADB = Ui_ADB
self.myADB = myADB
self.Ui_SDB = Ui_SDB
self.mySDB = mySDB
def __del__(self):
self.wait()
def run(self):
print "worker = "+str(self.temp)
buff = ''
tempByte= ''
# Simulating the RTT signal trigger
self.tmSerial.write('y')
# Reading SP Header
tmSerialData = self.tmSerial.read(8*8)
# Converts the bitstream of SP header after converting to bigEndian
bufferData = bitstream_to_hex(littleEndian)
# Reads the header info : First 8 bytes
headerINFO = readHeader(bufferData)
# checking the packets in the headerINFO
global tmPacNo
if (headerINFO['adbINFO'] == 1 and headerINFO['sdbINFO'] == 1):
print 'Both ADB and SDB info are present'
tmPacNo+=1;
# Need to call both ADB and SDB
# Statements for reading the ADB
bufferData = tmSerial.read(42*8) # ADB packet bitstream
self.adbPacket = bitstream_to_hex(bufferData)
# Calling ADB thread
self.adb_Thread = adbThread(self.Ui_ADB, self.myADB, self.adbPacket)
self.adb_Thread.start()
self.adb_Thread.adbReadComplete.connect(self.adbdone)
# IGNORED -- Statements for reading the SDB
# Calling SDB thread
#self.sdb_Thread.run(self.sdbPacket)
elif (headerINFO['adbINFO'] == 1 and headerINFO['sdbINFO'] == 0):
print 'ADB INFO only present'
tmPacNo+=1;
# Statements for reading the ADB
bufferData = self.tmSerial.read(42*8) # ADB packet bitstream
self.adbPacket = bitstream_to_hex(bufferData)
# Calling ADB thread
self.adb_Thread = adbReadThread(self.Ui_ADB, self.myADB , self.adbPacket)
self.adb_Thread.start()
self.adb_Thread.adbReadComplete.connect(self.adbDone)
# IGNORED FOR NOW
#elif (headerINFO['adbINFO'] == 0 and headerINFO['sdbINFO'] == 1):
#print 'SDB INFO only present'
#tmPacNo+=1;
## Statements for reading the SDB
#bufferData = self.tmSerial.read(46*8) # SDB packet bitstream
#self.sdbPacket = bitstream_to_hex(bufferData)
## Calling SDB thread
#self.sdb_Thread.run(sdbPacket)
mess = "Worker Reading complete"
self.readComplete.emit(mess)
def adbDone(self,text):
print text
#self.myADB.show()
# Global Variable to set the number of packets
packets=0
class mainwindow(QtGui.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(1153, 125)
# ..... codes for main window GUI
################################################################
#Setting up ADB
self.Ui_ADB = Ui_ADB()
self.myADB = QtGui.QWidget()
self.Ui_ADB.setupUi(self.myADB)
#self.myADB.show()
# IGONRED FOR NOW -- Setting up SDB
self.Ui_SDB = Ui_SDB()
self.mySDB = QtGui.QWidget()
self.Ui_SDB.setupUi(self.mySDB)
# Setting up the serial communication
self.tmSerial = serial.Serial('/dev/ttyACM0',9600)
# IGONRED FOR NOW -- setting up the SDB read thread
#self.sdb_Thread = sdbReadThread(self.Ui_SDB, self.SDBPacket)
# *** MODIFIED ***
# Setting up the Worker thread
self.tmWorker = workerThread(self.tmSerial, self.Ui_ADB, self.myADB, Ui_SDB, self.mySDB)
# Code to call the thread that checks the serial data and print accordingly
self.tmWorker.start()
self.tmWorker.readComplete.connect(self.done) # This will act as a recursive function
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.label.setText(_translate("MainWindow", "Serial Communication Status", None))
self.label_2.setText(_translate("MainWindow", "No. of SP_Packets Received", None))
####################################################################
def done(self):
print "worker reading done"
self.myADB.show()
self.tmWorker.start() #Modified
#sleep(01)
# This program converts the little endian bitstream -> BigEndian -> hex
def bitstream_to_hex(bitStream):
# Code for conversion
if __name__== "__main__":
import sys
# setting up the GUI
app = QtGui.QApplication(sys.argv)
main = mainwindow()
main.show()
sys.exit(app.exec_())
This program now works fine and the GUI seems responsive. But I find a glitch in GUI as am not sure whether it is because the program runs much faster than the time required to refresh the frames. I find it so because the counter placed in the GUI skips one or two counts while updating the value. But the GUI is responsive and there is no force-close during execution of the program.
Hope this helps someone who is in search of similar problem. More insight on the glitches and good programming techniques are welcome. Thank you.
Upvotes: 1