Skip to main content
 首页 » 编程设计

python之Chaco 和 PyQt 无法发出信号

2025年05月04日11grandyang

我正在尝试与 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() 

请您参考如下方法:

您在主线程(即 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()