2013-08-21 27 views
2

我试图与Chaco和pyqt一起绘制实验室硬件的实时数据采集任务。我以前使用matplotlib,但它证明太慢了(我甚至尝试过动画)。当我在pyqt窗口中嵌入一个matplotlib图时,下面的代码工作的很好,但是当使用chaco时,当我从线程内部发出更新信号时没有任何反应。如果您不使用线程进行模拟采集,此代码将起作用。我曾尝试使用qthreads也无济于事(包括这样的事情:Threading and Signals problem in PyQt)。有没有人使用pyqt + chaco +线程来帮助我找到错误的地方,或者发生了什么?Chaco和PyQt发出信号失败

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() 
+0

你永远不会启动一个事件循环,并且在信号发出后(除了连接到信号的插槽之外)不要调用'processEvents'。所以信号无法传递并不奇怪。如果在't.join()'之后添加'processEvents',会发生什么? – mata

+0

如果我在t.join()之后添加processEvents,则更新将会执行,但只能在线程完成后立即执行。 – hackyday

+0

你应该在'signal.emit'之后添加它以允许gui更新。将它放在'update_display'中是毫无意义的,因为除非事件已经发生,否则不会调用它。 – mata

回答

1

您在主线程(即UI线程)上加入的呼叫阻止该线程并阻止事件由UI处理。如果您在主函数中启动app/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() 
+0

就是这样!如果我删除't.join()',然后放入一个app.exec_(),它会正确更新并等待用户关闭窗口,或者发信号通知应用程序退出。我也不需要'processEvent'调用。 – hackyday