2013-07-02 13 views
3

저는 파이썬으로 디자인 한 GUI에 이미지를 플롯하려고합니다. 전체 프로그램은 카메라에서 이미지 데이터를 수집 한 다음 GUI에 이미지를 표시합니다. matplotlib를 사용하여 탐색했지만 응용 프로그램에 너무 느립니다. 나는 오히려 신속하게 (카메라에서 획득 할 수있는 한 빨리, 1/2 초 또는 1 초) 업데이트 할 음모가 필요합니다. 이것은 QThread를 사용할 때조차도 플롯이 메인 스레드에서만 업데이트되어 내 프로그램이 멈추고 사용자가 GUI에 액세스 할 수 없기 때문에 pyqt로 인해 어려움을 겪고 있습니다.pyqt 위젯에 pyqtgraph 다중 처리 구현

필자는 pyqtgraph가 matplotlib보다 훨씬 빠른 플로팅 라이브러리라고 가정합니다. 그래서 그것을 시도하고 그것을 좋아하지만 이미지를 표시 할 때 matplotlib 같은 문제가있는 것으로 보인다. 전체 GUI를 중지합니다. 나는 약간의 연구를했고이 질문을 건너왔다 : Painting without paintEvent 그리고 하나의 답은 QtProcess()의 사용을 제안했다. 따라서 제 질문은 QtProcess()를 GUI에 구현할 수 있는지 (예 : 몇 가지 예제 코드를 제공 할 수 있는지)입니다. 즉 GUI에서 플롯을 어떻게 개별 프로세스로 실행할 수 있습니까? (또한 matplotlib로이 작업을 수행하는 방법이 있다면 매우 유용 할 것입니다.)

다음은 문제를 테스트하기 위해 설계된 간단한 예입니다. 필자는 pyqtgraph의 예제에서 예제 스크립트를 가져 와서 pyqtgraph 플롯을 pyqt 위젯으로 가져 왔습니다. 그런 다음 푸시 버튼을 누르면 플롯이 표시됩니다. 스크립트를 실행하면 첫 번째 줄거리가로드되는 데 오랜 시간이 걸립니다 (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()를 (사용하려고 시도에서 내 시도 Qt GUI 로의 QtProcess() 위젯) :

(210)

답변

9

백그라운드에서 작업을하는 동안 반응하여 GUI를 유지하는 일반적으로 세 가지 방법이 있습니다 :

  1. 타이머 - 모든 것은 GUI 스레드 내에서 작동하고 작업은 타이머를 전화 일부를함으로써 이루어집니다 GUI 업데이트 사이에서 작동합니다. 이것은 가장 간단하고 가장 일반적인 접근법이며 은 pyqtgraph 또는 matplotlib를 사용하여 (올바르게 작성된 경우) 적합 할 가능성이 매우 높습니다. 이 방법의 단점은 입니다. 작업 기능이 오래 걸리면 GUI가 응답하지 않게됩니다. 카메라에서 데이터를 가져와 표시하면 문제가 발생하지 않습니다.
  2. 스레드 - 작업 기능이 너무 길어질 경우 작업 중 일부를 별도의 스레드로 옮길 수 있습니다. 중요한 것은 다른 스레드에서 GUI를 변경할 수 없다는 것입니다. 가능한 한 많은 작업을 수행 한 다음 결과를 GUI 스레드로 보내 표시되도록하십시오.
  3. 프로세스 - 이것은 단점 통신 사이의 스레드를 사용하는 등 더 많거나 적은 동일한 접근 방식을,하지만 당신은 더 나은 CPU 사용률 (파이썬 글로벌 통역 잠금에 대해 읽어)을 얻을 수있는 장점 을 가지고 있으며, 프로세스는 스레드들 사이보다 더 복잡 할 수 있습니다. Pyqtgraph가 내장 된 멀티 기능하려면이 쉽게 (/ RemoteSpeedTest.py를 pyqtgraph/예 참조)

이 항목은 광범위하게 다른 곳에서 설명되어 있습니다, 그래서 그 그것을 떠날거야.

  1. 당신은 생성하고 모든 프레임 업데이트와 3D 데이터 세트 (100 프레임 비디오) 표시됩니다 염두에두고

    , 당신이 게시 한 코드에 몇 가지 문제가있다. 이렇게 느리게 업데이트되는 이유가 여기 있습니다. 올바르게 완료된 비디오의 예는 pyqtgraph/examples/ImageItem.py 및 VideoSpeedTest.py를 참조하십시오.
  2. 작업자 스레드에서 setImage를 호출하고 있습니다. 이것은 때로는 작동하지만 충돌이 예상됩니다. 더 나은 방법은 작업자에서 데이터를 생성 한 다음 데이터를 주 GUI 스레드로 전송하여 표시하는 것입니다. Qt 문서는 스레드 스레딩에 대해 자세히 설명합니다.

마지막으로 한 가지 중요한 규칙을 따르도록 (내가 전에 을 언급 한 바와 같이하지만, 모두 스레드를 피하는 것은 더 나은 것) : 성능 문제가 발생하는 경우, 코드 프로필. 원인을 알기 전까지는 문제를 해결할 수 없습니다.