2013-07-02 45 views
3

我想在我用Python設計的GUI上繪製圖像。完整的程序將從相機收集圖像數據,然後在GUI上顯示圖像。我已經探討了使用matplotlib,但它對於我的應用程序來說太慢了。我需要劇情更新速度相當快(最好儘可能快地從相機採集,一次半秒或二次)。這對pyqt來說是一個挑戰,因爲即使在使用QThread時,該圖也只能在主線程上更新,這會導致我的程序停止並且GUI無法被用戶訪問。將pyqtgraph多處理器實現爲一個pyqt小部件

我讀到pyqtgraph被認爲是比matplotlib快得多的繪圖庫。所以我試了一下並且喜歡它,但是它在顯示圖像時似乎與matplotlib存在相同的問題。它會停止整個GUI。我做了一些研究,發現了這個問題:Painting without paintEvent,其中一個答案提出使用QtProcess()。因此,我的問題是,是否有可能(如果是這樣,你是否可以提供一些示例代碼)在GUI中實現QtProcess(),即如何在單獨的進程中對GUI執行繪圖? (另外,如果有一種方法可以用matplotlib來做到這一點,那將非常有幫助。)

這是我設計用來測試我的問題的簡單示例。我從pyqtgraph的示例中獲取了一個示例腳本,並將一個pyqtgraph圖輸入到一個pyqt小部件中。然後當pushButton被按下時,它顯示的繪圖。如果您運行腳本,您會注意到第一個圖需要很長時間才能加載(6或7秒)。再次按下按鈕,它似乎加載速度更快。我正在做一個QThread()中的所有數據生成,但繪圖似乎要求主線程工作,即使它在QThread中完成。我想要做與本例中完全相同的事情,除了使用QtProcess()來處理繪圖。

歡迎任何其他關於繪圖或可能的替代方案的建議。由於

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中):

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_()) 

回答

9

一般有三種方法來保持你的GUI響應,而這樣做在後臺工作:

  1. 定時器 - 一切GUI線程中運行,你的工作是由具有定時器 調用一些做在GUI更新之間起作用。這是最簡單和最常用的方法,並且使用pyqtgraph或matplotlib很可能足夠(如果寫入正確)。這種方法的缺點 是如果工作函數需要很長時間,GUI將變得無法響應。 儘管如此,從相機拉取數據並顯示它不應該引起您的問題。
  2. 主題 - 如果您的工作功能變得太長,您可以將部分工作 移出到單獨的線程。重要的是,您不能從其他線程更改GUI。 儘可能多地完成工作,然後將結果發送到GUI線程以進行顯示。
  3. 進程 - 這是或多或少相同的方法,使用線程,但具有以下優點: ,你可能會得到更好的CPU利用率(閱讀有關蟒蛇全局解釋鎖),以及 缺點之間的通信進程可能比線程之間更麻煩。 Pyqtgraph有內置的多的功能,使這更容易 (見pyqtgraph /例子/ RemoteSpeedTest.py)

這個話題被廣泛討論的其他地方,所以我會留在這。

考慮到這一點,有幾個問題你已經發布的代碼:

  1. 您生成並顯示每幀更新一3D數據集(100幀視頻)。 這就是它更新速度如此之慢的原因。有關正確完成的視頻示例,請參閱pyqtgraph/examples/ImageItem.py和 VideoSpeedTest.py。
  2. 您正在從工作線程調用setImage;這可能有時會奏效,但期望它會崩潰。 更好的方法是在工作人員中生成數據,然後將數據發送到要顯示的主要線程圖形的線程 。 Qt文檔深入討論了線程。 (但正如我前面提到 ,完全避免線程甚至會更好)

最後,一個重要的規則:如果你有性能問題,分析代碼。你不能解決問題,直到你知道是什麼導致它。