Qt中快速发射的信号会导致段错误(Rapidly emitting signals in Qt causes a segfault)

编程入门 行业动态 更新时间:2024-10-26 03:25:40
Qt中快速发射的信号会导致段错误(Rapidly emitting signals in Qt causes a segfault)

我正在使用PyQt5和QML(Qt 5.7.1)构建一个应用程序,该应用程序控制许多硬件,并遇到一个接一个地发出10个以上信号的问题,导致应用程序发生段错误。 我附加了一个MWE来演示这个问题。

MWE创建后台线程,然后使用信号每毫秒更新主线程中的两个标签。 Windows 7和Linux中的段错误是随机的,但通常在一秒之内。 我在Linux中安装了Qt调试符号,发现它在QV4中的随机位置中进行了分割,尽管每个分割的调用看起来都与内存管理器有关。

我在这一点上做了一个死胡同,唯一阻止segfaulting的事情是在每个信号发射之间放置QThread.msleep()调用,随着应用程序的增长,这已经变得QThread.msleep() 。

这是我第一次使用Qt / QML,所以如果这不是使用信号的正确方式,我很抱歉,但我找不到任何说不用这种方式的信号。

StartPage.qml

import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 ApplicationWindow { visible: true id: mainWindow ColumnLayout { Label { id: runLabel property int timesRun: 0 text: "Number of times run: " + timesRun Connections { target: worker onDoSomethingDone: { runLabel.timesRun = runLabel.timesRun + 1; } } } Label { id: dataLabel property real value: 0.0 text: "Data: " + value Connections { target: worker onDataChanged: { dataLabel.value = data } } } } }

thread_test.py

#!/usr/bin/env python3 import sys import os import signal from PyQt5.QtCore import QObject, QThread, pyqtSlot, pyqtSignal from PyQt5.QtQml import QQmlApplicationEngine from PyQt5.QtWidgets import QApplication class Worker(QObject): doSomethingDone = pyqtSignal() dataChanged = pyqtSignal(float, arguments=['data']) runWorkSignal = pyqtSignal() _count = 0.0 def __init__(self): super().__init__() self.runWorkSignal.connect(self.do_something) @pyqtSlot() def do_something(self): while (True): self._count += 0.5 self.doSomethingDone.emit() self.dataChanged.emit(self._count) QThread.msleep(1) if __name__ == '__main__': signal.signal(signal.SIGINT, signal.SIG_DFL) # Switch to the script directory so relative paths work correctly. os.chdir(os.path.dirname(os.path.abspath(__file__))) app = QApplication(sys.argv) engine = QQmlApplicationEngine() workObject = Worker() workThread = QThread() engine.rootContext().setContextProperty('worker', workObject) engine.load('qml/StartPage.qml') workThread.started.connect(workObject.do_something) workObject.moveToThread(workThread) workThread.start() sys.exit(app.exec_())

I am building an application using PyQt5 and QML (Qt 5.7.1) that controls a number of pieces of hardware and have run into a problem where emitting 10+ signals one after another causes the application to segfault. I attached a MWE to demonstrate the problem.

The MWE creates a background thread which then updates two labels in the main thread every millisecond using signals. The example segfaults in both Windows 7 and linux randomly, but generally in under a second. I installed the Qt debugging symbols in linux and found that it was segfaulting in random locations in QV4, though each call that segfaulted appeared to have something to do with the memory manager.

I've hit a dead end on what to do at this point, the only thing that stopped the segfaulting was to place QThread.msleep() calls between each signal emit, which has become untenable as the application grows.

This is my first time using Qt/QML so if this is not the correct way to use signals I apologize, but I can't find anything that says don't use them this way.

StartPage.qml

import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 ApplicationWindow { visible: true id: mainWindow ColumnLayout { Label { id: runLabel property int timesRun: 0 text: "Number of times run: " + timesRun Connections { target: worker onDoSomethingDone: { runLabel.timesRun = runLabel.timesRun + 1; } } } Label { id: dataLabel property real value: 0.0 text: "Data: " + value Connections { target: worker onDataChanged: { dataLabel.value = data } } } } }

thread_test.py

#!/usr/bin/env python3 import sys import os import signal from PyQt5.QtCore import QObject, QThread, pyqtSlot, pyqtSignal from PyQt5.QtQml import QQmlApplicationEngine from PyQt5.QtWidgets import QApplication class Worker(QObject): doSomethingDone = pyqtSignal() dataChanged = pyqtSignal(float, arguments=['data']) runWorkSignal = pyqtSignal() _count = 0.0 def __init__(self): super().__init__() self.runWorkSignal.connect(self.do_something) @pyqtSlot() def do_something(self): while (True): self._count += 0.5 self.doSomethingDone.emit() self.dataChanged.emit(self._count) QThread.msleep(1) if __name__ == '__main__': signal.signal(signal.SIGINT, signal.SIG_DFL) # Switch to the script directory so relative paths work correctly. os.chdir(os.path.dirname(os.path.abspath(__file__))) app = QApplication(sys.argv) engine = QQmlApplicationEngine() workObject = Worker() workThread = QThread() engine.rootContext().setContextProperty('worker', workObject) engine.load('qml/StartPage.qml') workThread.started.connect(workObject.do_something) workObject.moveToThread(workThread) workThread.start() sys.exit(app.exec_())

最满意答案

QML无法直接访问另一个线程,因此如果将某个项目连接到另一个线程中的某个对象,则会导致您观察到的问题。 如果你想访问另一个线程,你必须从Python端执行它,那就是:

Worker(another thread) --> Proxy(main Thread) --> QML

在你的情况下,解决方案是:

class Worker(QObject): [...] class Proxy(QObject): doSomethingDone = pyqtSignal() dataChanged = pyqtSignal(float, arguments=['data']) if __name__ == '__main__': signal.signal(signal.SIGINT, signal.SIG_DFL) # Switch to the script directory so relative paths work correctly. os.chdir(os.path.dirname(os.path.abspath(__file__))) app = QApplication(sys.argv) engine = QQmlApplicationEngine() workObject = Worker() workThread = QThread() proxy = Proxy() workObject.doSomethingDone.connect(proxy.doSomethingDone) workObject.dataChanged.connect(proxy.dataChanged) engine.rootContext().setContextProperty('worker', proxy) engine.load('qml/StartPage.qml') workThread.started.connect(workObject.do_something) workObject.moveToThread(workThread) workThread.start() sys.exit(app.exec_())

QML can not access another thread directly so if you connect an item to an object that is in another thread it causes problems like the one you observe. If you want to access another thread you must do it from the Python side, that is:

Worker(another thread) --> Proxy(main Thread) --> QML

In your case the solution is:

class Worker(QObject): [...] class Proxy(QObject): doSomethingDone = pyqtSignal() dataChanged = pyqtSignal(float, arguments=['data']) if __name__ == '__main__': signal.signal(signal.SIGINT, signal.SIG_DFL) # Switch to the script directory so relative paths work correctly. os.chdir(os.path.dirname(os.path.abspath(__file__))) app = QApplication(sys.argv) engine = QQmlApplicationEngine() workObject = Worker() workThread = QThread() proxy = Proxy() workObject.doSomethingDone.connect(proxy.doSomethingDone) workObject.dataChanged.connect(proxy.dataChanged) engine.rootContext().setContextProperty('worker', proxy) engine.load('qml/StartPage.qml') workThread.started.connect(workObject.do_something) workObject.moveToThread(workThread) workThread.start() sys.exit(app.exec_())

更多推荐

本文发布于:2023-08-06 11:53:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1448628.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:信号   错误   快速   Qt   segfault

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!