2

実験用ハードウェアのリアルタイム データ取得タスクをプロットする際に、Chaco と pyqt を使用しようとしています。以前は matplotlib を使用していましたが、遅すぎることがわかりました (アニメーションも試しました)。次のコードは、matplotlib プロットを pyqt ウィンドウに埋め込んだときに正常に動作しますが、chaco では、スレッド内から更新シグナルを送信しても何も起こりません。このコードは、シミュレートされた取得にスレッドを使用しない場合に機能します。私は qthreads を無駄に使用しようとしました(このようなものを含む: PyQt のスレッド化とシグナルの問題)。私がどこで間違っているのか、または何が起こっているのかを見つけるのに役立つpyqt + chaco + threadingを使用した人はいますか?

import sys
import threading, time
import numpy as np

from enthought.etsconfig.etsconfig import ETSConfig
ETSConfig.toolkit = "qt4"

from enthought.enable.api import Window
from enthought.chaco.api import ArrayPlotData, Plot

from PyQt4 import QtGui, QtCore


class Signals(QtCore.QObject):
    done_collecting = QtCore.pyqtSignal(np.ndarray, np.ndarray)

class PlotWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        x = np.linspace(0,2*np.pi,200)
        y = np.sin(x)
        plotdata = ArrayPlotData(x=x, y=y)
        plot = Plot(plotdata, padding=50, border_visible=True)
        plot.plot(('x', 'y'))

        window = Window(self,-1, component=plot)
        self.setCentralWidget(window.control)
        self.resize(500,500)

        self.pd = plotdata

    def update_display(self, x, y):
        print 'updating'
        self.pd.set_data('x', x)
        self.pd.set_data('y', y)


def run_collection(signal):
    # this is where I would start and stop my hardware,
    # but I will just call the read function myself here
    for i in range(1,10):
        every_n_collected(i, signal)
        time.sleep(0.5)

def every_n_collected(frequency, signal):
    # dummy data to take place of device read
    x = np.linspace(0,2*np.pi,200)
    y = np.sin(x*frequency)
    print 'emitting'
    signal.emit(x, y)
    QtGui.QApplication.processEvents()

def main():
    plt = PlotWindow()
    plt.show()
    QtGui.QApplication.processEvents()

    signals = Signals()
    signals.done_collecting.connect(plt.update_display)

    t = threading.Thread(target=run_collection, args=(signals.done_collecting,))
    t.start()
    t.join()
    QtGui.QApplication.processEvents()    

    # it works without threads though...
    # run_collection(signals.done_collecting)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    main()
4

1 に答える 1

1

メインスレッド (UI スレッド) で join を呼び出すと、そのスレッドがブロックされ、イベントが UI によって処理されなくなります。メイン関数でアプリ/GUI イベント ループを開始し、t.join() を呼び出さずにアプリが閉じられるのを待つと、正常に動作するはずです。

これは、通常の Traits/TraitsUI/Chaco アプリで行う方法です。

import time
import threading

import numpy as np

from traits.etsconfig.etsconfig import ETSConfig
ETSConfig.toolkit = "qt4"

from enable.api import ComponentEditor
from chaco.api import ArrayPlotData, Plot

from traits.api import Event, HasTraits, Instance
from traitsui.api import View, Item

class PlotWindow(HasTraits):

    dataset = Instance(ArrayPlotData)
    plot = Instance(Plot)

    def _dataset_default(self):
        x = np.linspace(0,2*np.pi,200)
        y = np.sin(x)
        plotdata = ArrayPlotData(x=x, y=y)
        return plotdata

    def _plot_default(self):
        plot = Plot(self.dataset, padding=50, border_visible=True)
        plot.plot(('x', 'y'))
        return plot

    def update_display(self, x, y):
        print 'updating', threading.current_thread()
        self.dataset.set_data('x', x)
        self.dataset.set_data('y', y)

    traits_view = View(
        Item('plot', editor=ComponentEditor(size=(400, 400)), show_label=False)
    )

def run_collection(datamodel):
    # this is where I would start and stop my hardware,
    # but I will just call the read function myself here
    for i in range(1,10):
        x = np.linspace(0,2*np.pi,200)
        y = np.sin(x*i)
        datamodel.update_display(x, y)
        time.sleep(0.5)

def main():
    plot = PlotWindow()

    t = threading.Thread(target=run_collection, args=(plot,))
    t.start()

    # Starts the UI and the GUI mainloop
    plot.configure_traits()

    # don't call t.join() as it blocks the current thread...

if __name__ == "__main__":
    main()
于 2013-08-22T09:57:02.000 に答える