在pyqt小部件中实现pyqtgraph多重处理 [英] Implementing pyqtgraph multiprocessing into a pyqt widget

查看:127
本文介绍了在pyqt小部件中实现pyqtgraph多重处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在用Python设计的GUI上绘制图像.完整的程序将从相机收集图像数据,然后在GUI上显示图像.我已经探索过使用matplotlib,但是对于我的应用程序来说太慢了.我需要相当快地更新情节(最好是尽可能快地从相机获取,每半秒或第二次). pyqt面临挑战,因为即使使用QThread时,绘图也只能在主线程上更新,这会导致我的程序停止并且用户无法访问GUI.

I am trying to plot images on a GUI that I am designing in Python. The full program will be collecting image data from a camera and then displaying the images on the GUI. I have explored using matplotlib, but it was too slow for my application. I need the plot to update rather quickly (preferably as fast as I can acquisition from the camera, once ever half second or second). This is challenging with pyqt because it seems that even when using a QThread, the plot can only update on the main thread, which causes my program to stop and the GUI to be inaccessible to the user.

我读到pyqtgraph应该比matplotlib快得多.因此,我尝试了一下并喜欢它,但是在显示图像时,它似乎与matplotlib有相同的问题.它将停止整个GUI.我做了一些研究,发现了这个问题:在没有paintEvent的情况下绘画,答案之一是建议使用QtProcess().因此,我的问题是是否有可能(如果可以,请提供一些示例代码)将QtProcess()实施到GUI中,即,如何在单独的进程中运行的GUI上绘制图形? (此外,如果可以使用matplotlib做到这一点,那将非常有帮助.)

I read that pyqtgraph is suppose to be a much faster plotting library than matplotlib. So I gave it a try and like it, but it seems to have the same problem as matplotlib when displaying an image. It stops the entire GUI. I did some research and came across this question: Painting without paintEvent and one of the answers proposed the use of QtProcess(). Therefore, my question is whether it is possible (and if so, can you provide some example code) to implement QtProcess() into a GUI, i.e. how can I make a plot on a GUI run in a separate process? (Also, if there is a way to do this with matplotlib, that would be extremely helpful.)

这是我设计用来测试问题的简单示例.我从pyqtgraph的示例中获取了一个示例脚本,并将pyqtgraph图导入了pyqt小部件中.然后,当按下按钮时,将显示其图.如果运行脚本,您会注意到第一个绘图将花费很长时间(6或7秒)加载.再次按下该按钮,加载速度似乎更快.我正在QThread()中完成所有数据生成,但是绘图似乎需要主线程才能工作,即使它是在QThread中完成的也是如此.除了使用QtProcess()处理绘图外,我想做与本示例完全相同的操作.

Here is the simple example I designed to test out my problem. I took an example script from pyqtgraph's examples and imported a pyqtgraph plot into a pyqt widget. Then when the pushButton is pressed, the plot it displayed. If you run the script, you will notice that the first plot will take a long time to load (6 or 7 seconds). Press the button again and it seems to load much faster. I am doing all of the data generation in a QThread(), but the plotting seems to require the main thread to work even though it is done in the QThread. I want to do exactly the same thing as in this example, except use QtProcess() to handle the plotting.

欢迎提供有关绘图的其他建议或可能的替代方法.谢谢

Any other suggestions about plotting or possible alternatives are welcome. Thanks

GUI脚本:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'GUI.ui'
#
# Created: Fri Jun 28 14:40:22 2013
#      by: PyQt4 UI code generator 4.9.5
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(800, 534)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.gridLayout_2 = QtGui.QGridLayout(self.centralwidget)
        self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
        self.gridLayout = QtGui.QGridLayout()
        self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
        self.scrollArea = QtGui.QScrollArea(self.centralwidget)
        self.scrollArea.setFrameShape(QtGui.QFrame.NoFrame)
        self.scrollArea.setFrameShadow(QtGui.QFrame.Plain)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName(_fromUtf8("scrollArea"))
        self.scrollAreaWidgetContents = QtGui.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 780, 514))
        self.scrollAreaWidgetContents.setObjectName(_fromUtf8("scrollAreaWidgetContents"))
        self.widgetPlot = QtGui.QWidget(self.scrollAreaWidgetContents)
        self.widgetPlot.setGeometry(QtCore.QRect(20, 10, 741, 451))
        self.widgetPlot.setObjectName(_fromUtf8("widgetPlot"))
        self.pushButtonPlot = QtGui.QPushButton(self.scrollAreaWidgetContents)
        self.pushButtonPlot.setGeometry(QtCore.QRect(340, 480, 75, 23))
        self.pushButtonPlot.setObjectName(_fromUtf8("pushButtonPlot"))
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gridLayout.addWidget(self.scrollArea, 1, 0, 1, 1)
        self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButtonPlot.setText(QtGui.QApplication.translate("MainWindow", "Plot", None, QtGui.QApplication.UnicodeUTF8))

包装器:

import numpy as np
import scipy

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg

import sys

from PyQt4.QtCore import * 
from PyQt4.QtGui import *

from GUI import Ui_MainWindow


class MyForm(QMainWindow):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.imv = pg.ImageView()

        vbox = QVBoxLayout()
        vbox.addWidget(self.imv)
        self.ui.widgetPlot.setLayout(vbox)

        self.plot_thread = plotData()

        self.connect(self.ui.pushButtonPlot, SIGNAL("clicked()"), lambda : self.plot_thread.input(self.imv))

    def threadPlot(self):
        self.plot_thread.input(self.imv)


    def plot(self):
        ## Create random 3D data set with noisy signals
        self.img = scipy.ndimage.gaussian_filter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
        self.img = self.img[np.newaxis,:,:]
        decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
        data = np.random.normal(size=(100, 200, 200))
        data += self.img * decay
        data += 2

        ## Add time-varying signal
        sig = np.zeros(data.shape[0])
        sig[30:] += np.exp(-np.linspace(1,10, 70))
        sig[40:] += np.exp(-np.linspace(1,10, 60))
        sig[70:] += np.exp(-np.linspace(1,10, 30))

        sig = sig[:,np.newaxis,np.newaxis] * 3
        data[:,50:60,50:60] += sig

        self.imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))


class plotData(QThread):
    def __init__(self,parent=None):
        QThread.__init__(self,parent)
        self.exiting = False
    def input(self, imv):
        self.imv = imv
        self.start()

    def collectImage(self):
        ## Create random 3D data set with noisy signals
        self.img = scipy.ndimage.gaussian_filter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
        self.img = self.img[np.newaxis,:,:]
        decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
        data = np.random.normal(size=(100, 200, 200))
        data += self.img * decay
        data += 2

        ## Add time-varying signal
        sig = np.zeros(data.shape[0])
        sig[30:] += np.exp(-np.linspace(1,10, 70))
        sig[40:] += np.exp(-np.linspace(1,10, 60))
        sig[70:] += np.exp(-np.linspace(1,10, 30))

        sig = sig[:,np.newaxis,np.newaxis] * 3
        data[:,50:60,50:60] += sig

        self.imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))

    def run(self):

        self.collectImage()
        self.emit(SIGNAL("Done"))


app = QApplication(sys.argv)
myapp = MyForm()

myapp.show()
sys.exit(app.exec_())

我尝试使用QtProcess()的尝试(不成功,我不知道如何将QtProcess()小部件导入Qt GUI):

My attempt at trying to use QtProcess() (unsuccessful, I do not know how to import a QtProcess() widget into a Qt GUI):

import numpy as np
import scipy

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg

import sys

from PyQt4.QtCore import * 
from PyQt4.QtGui import *

from GUI import Ui_MainWindow


class MyForm(QMainWindow):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.proc = mp.QtProcess()

        self.remotepg = self.proc._import('pyqtgraph')
        self.win = self.remotepg.plot()

        self.imv = self.win.plot([1,4,2,3], [4,6,3,4], pen=None, symbol='o')

        vbox = QVBoxLayout()
        vbox.addWidget(self.imv)
        self.ui.widgetPlot.setLayout(vbox)

app = QApplication(sys.argv)
myapp = MyForm()

myapp.show()
sys.exit(app.exec_())

推荐答案

有三种通用方法可在后台进行工作时保持GUI的响应速度:

There are three general approaches to keeping your GUI responsive while doing work in the background:

  1. 计时器-一切都在GUI线程中运行,并且通过使用计时器来完成您的工作 在GUI更新之间调用某些函数.这是最简单,最常见的方法, 使用pyqtgraph或matplotlib很有可能(如果正确编写).缺点 这种方法的原因是如果工作功能花费很长时间,则GUI将无响应. 不过,从相机中提取数据并显示它不会造成问题.
  2. 线程-如果您的工作功能过长,则可以移动一些工作 出一个单独的线程.重要的是,您不能从其他线程对GUI进行更改. 做尽可能多的工作,然后将结果发送到GUI线程进行显示.
  3. 进程-与线程使用方法大致相同,但具有优势 您可能会获得更好的CPU利用率(了解python中的全局解释器锁),以及 进程之间的通信比线程之间的通信更麻烦. Pyqtgraph具有内置的多处理功能,可简化此过程 (请参见pyqtgraph/examples/RemoteSpeedTest.py)
  1. Timers -- Everything operates within the GUI thread and your work is done by having a timer call some function in between GUI updates. This is the simplest and most common approach and is very likely to be adequate (if written correctly) using either pyqtgraph or matplotlib. The drawback of this approach is that if the work function takes a long time, the GUI will become unresponsive. Pulling data from a camera and displaying it should not cause you problems, though.
  2. Threads -- In the case that your work function becomes too long, you can move some of the work out to a separate thread. Importantly, you can NOT make changes to the GUI from a different thread. Do as much work as possible, then send the results to the GUI thread to be displayed.
  3. Processes -- This is more or less the same approach as using threads, but has the advantage that you may get better CPU utilization (read about the global interpreter lock in python), and the disadvantage that communication between processes can be more cumbersome than between threads. Pyqtgraph has built-in multiprocessing features to make this easier (see pyqtgraph/examples/RemoteSpeedTest.py)

该主题在其他地方进行了广泛的讨论,所以我将其保留.

This topic is discussed extensively elsewhere, so I'll leave it at that.

请记住,您发布的代码存在一些问题:

With that in mind, there are a few problems with the code you have posted:

  1. 您将在每次更新帧时生成并显示 3D 数据集(100帧视频). 这就是它似乎更新得如此缓慢的原因.参见pyqtgraph/examples/ImageItem.py和 VideoSpeedTest.py,以获取正确完成视频的示例.
  2. 您正在从工作线程中调用setImage;有时这可能会起作用,但希望它会崩溃. 更好的方法是在worker中生成数据,然后将数据发送到主GUI 要显示的线程. Qt文档深入讨论了线程. (但正如我提到的 之前,完全避免使用线程会更好)
  1. You are generating and displaying a 3D data set (100-frame video) with every frame update. This is the reason it appears to update so slowly. See pyqtgraph/examples/ImageItem.py and VideoSpeedTest.py for examples of video done correctly.
  2. You are calling setImage from the worker thread; this may work sometimes, but expect it to crash. A better approach is to generate the data in the worker, then send the data to the main GUI thread to be displayed. The Qt documentation discusses threading in depth. (but as I mentioned before, avoiding threads altogether would be even better)

最后,要遵循的一条重要规则:如果遇到性能问题,请配置代码.您必须先找出导致问题的原因,才能解决问题.

Finally, one important rule to follow: If you are having performance issues, profile your code. You cannot fix the problem until you know what is causing it.

这篇关于在pyqt小部件中实现pyqtgraph多重处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆