PyQt5“定时器不能从另一个线程启动"更改 QLabel 的大小时出错
问题描述
我在 Python 3.5 中遇到了一个奇怪的 PyQt5 问题.我有两个类,FrontEnd(QWidget)
和 TimerThread(Thread)
.我在FrontEnd
的init函数中定义了很多QLabel
,都可以正常工作.
I am having a strange issue with PyQt5 in Python 3.5.
I have two classes, FrontEnd(QWidget)
and TimerThread(Thread)
. I have a number of QLabel
s defined in the init function of FrontEnd
, all of which work properly.
显示的是FrontEnd
的相关几个函数:
Shown are the relevant few functions of 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 类:
And the entire TimerThread class:
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)
(TimerThread
的 front_end
元素在 FrontEnd
的 init 上设置)
(The front_end
element of TimerThread
is set on init of FrontEnd
)
问题出在 update_bar_graph
函数中.当 setFixedSize
调用被注释掉时,程序运行良好,尽管在我的应用程序中没有正确显示条形图的条(它们是 QLabels
).move
函数似乎运行正常.但是,setFixedSize
调用会导致此错误:
The problem is in the update_bar_graph
function. When the setFixedSize
call is commented out, the program runs fine, although without properly displaying the bars of the bar graph in my application (which are QLabels
). The move
function seems to run properly. However, the setFixedSize
call causes this error:
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
我完全不知道为什么会发生这种情况,也不知道为什么 move
函数在本质上看起来很相似,但运行良好.任何帮助将非常感激.(如果我应该使用不同类型的计时器类或不同的方法在 PyQt 中绘制大矩形,我愿意接受任何此类建议).
I have absolutely no idea why this occurs, and why the move
function, seemingly similar in nature, works fine. Any help would be much appreciated.
(If I should be using a different sort of timer class or different methods for drawing large rectangles in PyQt, I am open to any such suggestions).
这是一些奇怪的东西.第二天我运行了两次,没有更改代码.(我认为...)有一次条形图没有显示,但没有引发错误.另一次我得到了这个:
This is some weird stuff. I ran it twice the next day, with no changes to the code. (I think...) One time the bar graphs did not display but no errors were thrown. The other time I got this:
7fdfaf931000-7fdfaf932000 r--p 0007a000 08:07 655633 /usr/lib/x86_64-linux-gnu/libQt5DBus.so.5.5.1
7fdfaf932000-7fdfaf933000 rw-p 0007b000 08:07 655633 /usr/lib/x86_64-linux-gnu/libQt5DBus.so.5.5.1
7fdfaf933000-7fdfaf934000 rw-p 00000000 00:00 0
7fdfaf934000-7fdfaf971000 r-xp 00000000 08:07 667112 /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0
7fdfaf971000-7fdfafb70000 ---p 0003d000 08:07 667112 /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0
7fdfafb70000-7fdfafb72000 r--p 0003c000 08:07 667112 /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0
7fdfafb72000-7fdfafb73000 rw-p 0003e000 08:07 667112 /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0
7fdfafb73000-7fdfafb7a000 r-xp 00000000 08:07 667110 /usr/lib/x86_64-linux-gnu/libxkbcommon-x11.so.0.0.0
7fdfafb7a000-7fdfafd79000 ---p 00007000 08:07 667110 /usr/lib/x86_64-linux-gnu/libxkbcommon-x11.so.0.0.0
我想我可能在 PyQt5 中发现了一个错误.
I think I may have found a bug in PyQt5.
解决方案
正如@mata 所述,您的代码不是线程安全.这可能是错误行为的来源,当然应该在进一步调试之前修复(this 是相关的).
As mentioned by @mata your code is not thread safe. This is likely where the errant behaviour is coming from, and should certainly be fixed before debugging further (this is related).
它是线程不安全的原因是因为您直接从辅助线程与 GUI 对象交互.相反,您应该从线程向主线程中的插槽发出信号,您可以在其中安全地更新 GUI.但是,这需要您使用 QThread
,无论如何根据这篇文章推荐.
The reason it is thread unsafe is because you are interacting with GUI objects from the secondary thread directly. You should instead emit a signal from your thread to a slot in the main thread where you can safely update your GUI. This however requires you use a QThread
which is recommended anyway as per this post.
这需要进行以下更改:
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
对象中,这样线程就不会被垃圾回收.
I've also modified your code so that it stores a reference to the TimerThread
in the FrontEnd
object so the thread is not garbage collected.
我还要补充一点,这是每 0.02 秒触发一次更新的一种过于复杂的方式.您可以使用 QTimer
来调用 update_ui
方法并完全放弃线程,但我采用的方法是您以后可能希望对线程执行更复杂的操作,所以已经演示了如何安全地做到这一点!
I would also add that this is an overly complex way of triggering an update every 0.02 seconds. You could use a QTimer
to just call the update_ui
method and ditch threads entirely but I'm taking the approach that you will likely want to do something more complex with your threads later, so have demonstrated how to do it safely!
相关文章