2013-08-21 32 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