2

まず、私はPythonが初めてです。私は長年の MatLab ユーザー (コンピューター科学者ではなくエンジニア) であり、Python、NumPy、SciPy などを自分のワークフローに取り込もうとするプロセスを開始しています。ですから、素晴らしいプログラミング言語とは何かについての私の明らかな無知を許してください!

最初の試みとして、開発中のセンサーと対話するアプリケーションを構築することにしました。センサーの解像度はマイクロ秒 (500 マイクロ秒ごとに 512 個の高エネルギー「ピクセル」と 512 個の低エネルギー「ピクセル」からのデータ) ですが、I/O はブロックされます。デバイスを継続的にポーリングするので、GUI の応答性を維持するためにスレッド化が重要であることはわかっています (GUI は最終的に別のデバイスとのシリアル通信も統合し、センサー データで動作する画像処理サブルーチンを備えています)。センサーからのこれらの「リアルタイム」データをプロットするために、MatPlotLib のスレッド化されたインスタンスを作成しました。センサーと個別に通信するモジュールを作成し、Python でそれを行う方法を知っていることを確認しましたが、ここでは単純に「シミュレーション」から始めます。低エネルギーの「ピクセル」に対しては 8 から 12 の間の 512 の乱数を生成し、高エネルギーの「ピクセル」に対しては 90 から 110 の間の 512 の乱数を生成することにより、データの それがスレッド化されたものです。ここの多くの例から作業して、ブリッティングを使用して MatPlotLib で十分に高速な画面更新を取得することも学びました-しかし、問題は、使用しない限り、スレッド化されたプロセスを20ミリ秒スリープ状態にするtime.sleep(0.02)、GUI が応答しません。これは、MatPlotLib からのインタラクティブな X、Y データ ポイント フィードバックが機能せず、「停止」ボタンを使用してプロセスを中断できないため、確認できます。これより長いtime.sleep(0.02)と、GUI の操作がさらにスムーズになりますが、「データ レート」が犠牲になります。これより遅いtime.sleep(0.02)と、GUI が応答しなくなります。なぜだかよくわかりません。代わりにGUIqwtを使用しようとしましたが、MatPlotLibを放棄する前にここで質問すると思いました。それが問題であるかどうかさえわからないからです。スレッドを 20 ミリ秒スリープ状態にすると、センサー アレイから少なくとも 40 行の潜在的なデータが失われるのではないかと心配しています (40 行 * 500us/行 = 20 ミリ秒)。

現在のコードは次のとおりです。

import time, random, sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class ApplicationWindow(QMainWindow):

    def __init__(self, parent = None):

        QMainWindow.__init__(self, parent)

        self.thread = Worker()

        self.create_main_frame()
        self.create_status_bar()

        self.connect(self.thread, SIGNAL("finished()"), self.update_UI)
        self.connect(self.thread, SIGNAL("terminated()"), self.update_UI)       
        self.connect(self.startButton, SIGNAL("clicked()"), self.start_acquisition)       
        self.connect(self.stopButton, SIGNAL("clicked()"), self.stop_acquisition)
        self.thread.pixel_list.connect(self.update_figure)

    def create_main_frame(self):
        self.main_frame = QWidget()

        self.dpi = 100
        self.width = 10
        self.height = 8
        self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
        self.axes = self.fig.add_subplot(111)               
        self.axes.axis((0,512,0,120))

        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.main_frame)
        self.canvas.updateGeometry()    
        self.canvas.draw()
        self.background = None

        self.lE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)
        self.hE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)          

        self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)

        self.startButton = QPushButton(self.tr("&Start"))
        self.stopButton = QPushButton(self.tr("&Stop"))

        layout = QGridLayout()
        layout.addWidget(self.canvas, 0, 0)
        layout.addWidget(self.mpl_toolbar, 1, 0)
        layout.addWidget(self.startButton, 2, 0)       
        layout.addWidget(self.stopButton, 2, 1)

        self.main_frame.setLayout(layout)
        self.setCentralWidget(self.main_frame)

        self.setWindowTitle(self.tr("XRTdev Interface"))

    def create_status_bar(self):
        self.status_text = QLabel("I am a status bar.  I need a status to show!")
        self.statusBar().addWidget(self.status_text, 1)

    def start_acquisition(self):
        self.thread.exiting = False
        self.startButton.setEnabled(False)
        self.stopButton.setEnabled(True)
        self.thread.render()

    def stop_acquisition(self):
        self.thread.exiting = True
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()

    def update_figure(self, lE, hE):
        if self.background == None:
            self.background = self.canvas.copy_from_bbox(self.axes.bbox)
        self.canvas.restore_region(self.background)
        self.lE_line.set_ydata(lE)
        self.hE_line.set_ydata(hE)
        self.axes.draw_artist(self.lE_line)
        self.axes.draw_artist(self.hE_line)
        self.canvas.blit(self.axes.bbox)

    def update_UI(self):
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()        

    def cleanup_UI(self):
        self.background = None
        self.axes.clear()        
        self.canvas.draw()

class Worker(QThread):

    pixel_list = pyqtSignal(list, list)

    def __init__(self, parent = None):
        QThread.__init__(self, parent)
        self.exiting = False

    def __del__(self):
        self.exiting = True
        self.wait()

    def render(self):
        self.start()

    def run(self):
        # simulate I/O
        n = random.randrange(100,200)
        while not self.exiting and n > 0:
            lE = [random.randrange(5,16) for i in xrange(512)]
            hE = [random.randrange(80,121) for i in xrange(512)]
            self.pixel_list.emit(lE, hE)
            time.sleep(0.02)
            n -= 1

def main():
    app = QApplication(sys.argv)
    form = ApplicationWindow()
    form.show()
    app.exec_()

if __name__ == "__main__":
    main()

おそらく私の問題は、MatPlotLib や PyQT4 でもなく、スレッドの実装方法にあるのでしょう。私が指摘したように、私はこれに慣れておらず、学んでいます。そして、GUIqwt がこれらの問題のいずれかに対処するかどうかさえわかりませんが、GUI での「リアルタイム」プロットに MatPlotLib よりも高速なものを使用するための多くの推奨事項をここで見たことは知っています。これについて助けてくれてありがとう!

4

1 に答える 1

3

QThread[混乱/混乱しているため編集]

それをサブクラス化する(あなたの例とドキュメントが言うように)か、ワーカーオブジェクトを作成してからスレッドに移動するかの2つの使用方法があるようです(このブログ投稿を参照してください)。その後、シグナル/スロットを混在させると、さらに混乱します。Avaris が言うように、この変更は問題ではないかもしれません。

私はあなたのWorkerクラスをサブクラスとして作り直しましQObjectた(これは私が理解しているスタイルだからです)。

問題は、偽のデータ システムに a を配置しない場合sleep、メイン ウィンドウへのすべてのコールバックが 1 秒未満で生成されることです。メイン スレッドは、シグナル キューをクリアするため、実質的にブロックされます。遅延を指定した遅延 (0.0005 秒) に設定すると、データが表示されるよりもはるかに速く生成されます。これは、これが問題に適していない可能性があることを示唆しているようです (実用的に、それほど高速ではなく、30 ~ 40 fps で問題ないようです)。

import time, random, sys
#from PySide.QtCore import *
#from PySide.QtGui import *

from PyQt4 import QtCore
from PyQt4 import QtGui

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class ApplicationWindow(QtGui.QMainWindow):
    get_data = QtCore.pyqtSignal()

    def __init__(self, parent = None):

        QtGui.QMainWindow.__init__(self, parent)


        self.thread = QtCore.QThread(parent=self)
        self.worker = Worker(parent=None)
        self.worker.moveToThread(self.thread)

        self.create_main_frame()
        self.create_status_bar()

        self.startButton.clicked.connect(self.start_acquisition) 
        self.stopButton.clicked.connect(self.stop_acquisition)
        self.worker.pixel_list.connect(self.update_figure)
        self.worker.done.connect(self.update_UI)

        self.get_data.connect(self.worker.get_data)


        self.thread.start()


    def create_main_frame(self):
        self.main_frame = QtGui.QWidget()

        self.dpi = 100
        self.width = 10
        self.height = 8
        self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
        self.axes = self.fig.add_subplot(111)               
        self.axes.axis((0,512,0,120))

        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.main_frame)
        self.canvas.updateGeometry()    
        self.canvas.draw()
        self.background = None

        self.lE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)
        self.hE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)          

        self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)

        self.startButton = QtGui.QPushButton(self.tr("&Start"))
        self.stopButton = QtGui.QPushButton(self.tr("&Stop"))

        layout = QtGui.QGridLayout()
        layout.addWidget(self.canvas, 0, 0)
        layout.addWidget(self.mpl_toolbar, 1, 0)
        layout.addWidget(self.startButton, 2, 0)       
        layout.addWidget(self.stopButton, 2, 1)

        self.main_frame.setLayout(layout)
        self.setCentralWidget(self.main_frame)

        self.setWindowTitle(self.tr("XRTdev Interface"))

    def create_status_bar(self):
        self.status_text = QtGui.QLabel("I am a status bar.  I need a status to show!")
        self.statusBar().addWidget(self.status_text, 1)

    def start_acquisition(self):
        self.worker.exiting = False
        self.startButton.setEnabled(False)
        self.stopButton.setEnabled(True)
        self.get_data.emit()

    def stop_acquisition(self):
        self.worker.exiting = True
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()

    def update_figure(self, lE, hE):
        if self.background == None:
            self.background = self.canvas.copy_from_bbox(self.axes.bbox)
        self.canvas.restore_region(self.background)
        self.lE_line.set_ydata(lE)
        self.hE_line.set_ydata(hE)
        self.axes.draw_artist(self.lE_line)
        self.axes.draw_artist(self.hE_line)
        self.canvas.blit(self.axes.bbox)

    def update_UI(self):
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()        

    def cleanup_UI(self):
        self.background = None
        self.axes.clear()        
        self.canvas.draw()

class Worker(QtCore.QObject):

    pixel_list = QtCore.pyqtSignal(list, list)
    done = QtCore.pyqtSignal()

    def __init__(self, parent = None):
        QtCore.QObject.__init__(self, parent)
        self.exiting = True

    @QtCore.pyqtSlot()
    def get_data(self):
        # simulate I/O
        print 'data_start'
        n = random.randrange(100,200)
        while not self.exiting and n > 0:
            lE = [random.randrange(5,16) for i in xrange(512)]
            hE = [random.randrange(80,121) for i in xrange(512)]
            self.pixel_list.emit(lE, hE)
            time.sleep(0.05)
            n -= 1
        print 'n: ', n
        self.done.emit()
于 2013-02-02T21:40:53.717 に答える