我一直在寻找stackoverflow和其他pyqt教程中有关如何克服pyqt4中的GUI冻结问题的解决方案。有类似的主题建议采用以下方法来纠正它:
app.processEvents()
。这使Qt有机会处理事件并重绘GUI。我已尝试过上述方法,但我的GUI仍然卡住了。我在下面给出了导致问题的代码结构。
# 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_())
在上面的代码中可以注意到线程已经实现但是我不确定我做错了什么?我已经在线程中放置了长时间运行的循环adbreader()
,但是在GUI中没有相应地更新值。我只能在while循环运行10次后查看输出。
另外,我尝试过使用QtGui.QApplication.processEvents()
并以某种方式设法在GUI中打印值,但我对这种方法不满意。(不高兴,因为它有时会在迭代5时跳过打印并打印出来迭代7中的值next)非常感谢有关如何在此目的中使用线程的一些指导。
答案 0 :(得分:1)
正如three_pinapples所建议的,我试图通过创建更多线程来卸载程序。我进一步调用thread
执行整个序列写入和读取while
循环。这导致无论循环如何都只调用一次线程的问题。我不知道为什么,但我想这可能是因为在循环中一次又一次地调用了同一个对象?不确定。
我通过使用信号/插槽机制作为递归函数找到了解决此问题的方法,该函数使线程处于无限运行模式,而与while循环无关。我已经发布了以下代码的修改结构:
# 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_())
此程序现在运行正常,GUI似乎响应。但是我发现GUI中存在一个小故障,因为我不确定是否因为程序运行速度比刷新帧所需的时间快得多。我发现它是因为放置在GUI中的计数器在更新值时跳过一个或两个计数。但是GUI 响应并且在程序执行期间有没有强制关闭。
希望这有助于寻找类似问题的人。欢迎更多关于毛刺和良好编程技术的见解。谢谢。