PySide / PyQtGraph 访问主 Qt 事件线程

Posted

技术标签:

【中文标题】PySide / PyQtGraph 访问主 Qt 事件线程【英文标题】:PySide / PyQtGraph access to main Qt event thread 【发布时间】:2018-02-12 07:08:56 【问题描述】:

附加的程序抛出这些错误:

QPixmap:在 GUI 线程之外使用像素图是不安全的

QObject::startTimer: 定时器不能从另一个线程启动

import sys
import PySide
import numpy as np
import pyqtgraph as pg
import threading

from PySide import QtGui, QtCore
from PySide.QtGui import *
from PySide.QtCore import *
from ui_mainWindow import Ui_MainWindow

#-------------------------------------------------------------------------------
# Main Window Class
# handle events and updates to the Qt user interface objects
#-------------------------------------------------------------------------------
class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.setup_actions()
        self.show()

    def setup_actions(self):
        self.startScopeButton.clicked.connect(self.start_stop_scope)

    def start_stop_scope(self):
        print("starting scope")
        self.scope_display_thread()

    def scope_display_thread(self):
        threading.Thread(target=self._scope_display_thread).start()

    def _scope_display_thread(self):
        global data_ch1      
        data_ch1  = np.random.normal(0, 10, 1000)
        self.graphicsView.plot(data_ch1,clear=True)

#-------------------------------------------------------------------------------
# Main Application Start Point
#-------------------------------------------------------------------------------

def main():
    app = QApplication(sys.argv)  
    mainWin = MainWindow()
    ret = app.exec_()
    sys.exit( ret )

data_ch1 = []
main()

这是我正在尝试做的简化版本.. 即有一个线程接收和绘制数据。通过搜索论坛等。我从根本上知道问题是我必须从“主 Qt 事件线程”更新情节..但我不知道如何做到这一点。我的代码的几个信号槽排列看起来很有希望,但只会导致程序崩溃。

请对我放轻松,我是一个改过自新的 C# 人 ;-) 在 C# 或 Java 中,通常我实际上会在绘制方法覆盖中进行绘制,然后在加载新数据时强制应用程序重新绘制中。似乎 PyQtGraph.plot() 试图立即绘画?我可以异步添加数据然后告诉主线程去重绘场景吗??

谢谢!

【问题讨论】:

【参考方案1】:

当你评论你的问题时,你只能在主线程中更新GUI,那么此类问题的策略是通过信号将辅助线程的数据发送到主线程。

信号可以携带多个数据,但必须在创建时指明,创建方式如下:

signal = QtCore.Signal(type_1, type_2, ..., type_n)

在您的特定情况下,np.random.normal (0, 10, 1000)np.ndarray,这可以通过执行以下操作轻松获得:

print(type(np.random.normal (0, 10, 1000)))

输出:

<class 'numpy.ndarray'>

或者我们可以使用object,因为所有类都继承自该基类。然后我们将该信号与绘图的函数连接起来,在这种情况下,我们将使用如下所示的 lambda 函数:

class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
    signal = QtCore.Signal(np.ndarray) # or signal = QtCore.Signal(object)

    def __init__(self):
        [..]
        self.show()

        self.signal.connect(lambda data: self.graphicsView.plot(data, clear=True))

    [...]

    def _scope_display_thread(self):
        data_ch1 = np.random.normal(0, 10, 1000)
        self.signal.emit(data_ch1)

【讨论】:

优秀。谢谢您的帮助。我有一种感觉,这也会对其他人有所帮助。尽管我理解了基本问题,但找到正确的方法对我来说有点费力。

以上是关于PySide / PyQtGraph 访问主 Qt 事件线程的主要内容,如果未能解决你的问题,请参考以下文章

从 pyqtgraph.Qt 导入 QtGui 为 *

pyqtgraph绘图Qt速成课程

pyqtgraph 主窗体内的绘图(qt 设计器)

将 Pyqtgraph 嵌入到 PySide2

带有 PySide 2 和 QtDesigner 的 pyqtgraph

在 PyQtGraph 和 PySide2 中使用 ImageView 修复文本位置