我在Python 3.5中遇到PyQt5的奇怪问题。
我有两个班级,FrontEnd(QWidget)
和TimerThread(Thread)
。我在QLabel
的init函数中定义了一些FrontEnd
,所有这些都正常工作。
显示FrontEnd
的相关少数功能:
def update_ui(self):
ret, frame = self.cam_capture.read()
if self.results_pending:
if not path.isfile('output.jpg'):
self.results_pending = False
with open('.out') as content_file:
content = content_file.readlines()[2:-2]
system('rm .out')
self.handle_image_classification(content)
if self.take_picture:
cv2.imwrite('output.jpg', frame)
self.user_prompt.setText('Please wait...')
system('./classifyimage.py --mean mean.binaryproto --nogpu --labels labels.txt model.caffemodel deploy.prototxt output.jpg > .out && rm output.jpg')
self.take_picture = False
self.results_pending = True
image = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888).rgbSwapped()
pix = QPixmap.fromImage(image)
self.video_frame.setPixmap(pix)
def update_bar_graph(self, data):
palette = QPalette()
palette.setColor(QPalette.Background, Qt.white)
for i in range(0, 8):
self.bar_graph_labels[i].setText(str(data[i]) + "%")
height = int(data[i] * 5)
self.bar_graph[i].setFixedSize(self.bar_width, height)
self.bar_graph[i].move(1280 + (i * (self.bar_width + self.bar_spacing)), 640 - height)
def handle_image_classification(self, raw_output):
data = [None] * 8
for i in range(0, len(raw_output)):
raw_output[i] = raw_output[i].strip()
data[int(raw_output[i][-2]) - 1] = float(raw_output[i][:-10])
self.update_bar_graph(data)
整个TimerThread类:
class TimerThread(Thread):
front_end = None
def __init__(self, event):
Thread.__init__(self)
self.stopped = event
def run(self):
while not self.stopped.wait(0.02):
FrontEnd.update_ui(self.front_end)
(front_end
的{{1}}元素设置为TimerThread
的初始值
问题出在FrontEnd
函数中。当update_bar_graph
调用被注释掉时,程序运行正常,但没有在我的应用程序中正确显示条形图的条形(setFixedSize
)。 QLabels
函数似乎运行正常。但是,move
调用会导致此错误:
setFixedSize
我完全不知道为什么会发生这种情况,以及为什么QObject::startTimer: Timers cannot be started from another thread
QObject::startTimer: Timers cannot be started from another thread
QObject::killTimer: Timers cannot be stopped from another thread
QObject::startTimer: Timers cannot be started from another thread
函数在性质上看似相似,效果很好。任何帮助将非常感激。
(如果我应该使用不同类型的计时器类或不同的方法在PyQt中绘制大矩形,我愿意接受任何此类建议。)
编辑:
这是一些奇怪的东西。我第二天跑了两次,代码没有变化。 (我想......)有一次条形图没有显示但没有抛出任何错误。另一次我得到了这个:
move
我想我可能在PyQt5中发现了一个错误。
答案 0 :(得分:4)
正如@ mata所提到的,您的代码不是线程安全的。这可能是错误行为的来源,并且应该在进一步调试之前确定(this是相关的)。
它是线程不安全的原因是因为您直接从辅助线程与GUI对象进行交互。您应该从线程向主线程中的插槽发出信号,您可以安全地更新GUI。但是,这需要您根据this post使用QThread
建议。
这需要进行以下更改:
class TimerThread(QThread):
update = pyqtSignal()
def __init__(self, event):
QThread.__init__(self)
self.stopped = event
def run(self):
while not self.stopped.wait(0.02):
self.update.emit()
class FrontEnd(QWidget):
def __init__(self):
super().__init__()
... # code as in your original
stop_flag = Event()
self.timer_thread = TimerThread(stop_flag)
self.timer_thread.update.connect(self.update_ui)
self.timer_thread.start()
我还修改了你的代码,以便它在TimerThread
对象中存储对FrontEnd
的引用,这样就不会对线程进行垃圾回收。
我还要补充一点,这是一种每0.02秒触发更新的过于复杂的方式。您可以使用QTimer
来调用update_ui
方法并完全抛弃线程,但我采取的方法是您可能希望稍后对您的线程执行更复杂的操作,因此已经演示了如何安全地做到这一点!