2

我正在尝试与 Chaco 和 pyqt 合作,为实验室硬件绘制实时数据采集任务。我之前使用的是 matplotlib,但事实证明它太慢了(我什至尝试过动画)。当我在 pyqt 窗口中嵌入 matplotlib 图时,以下代码工作正常,但是使用 chaco,当我从线程内部发出更新信号时,什么也没有发生。如果您不使用线程进行模拟采集,此代码将起作用。我也尝试过使用 qthreads 也无济于事(包括这样的东西:Threading and Signals problem in 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 回答