2 votes

Signal ne parvient pas à émettre avec Chaco et PyQt

J'essaie de travailler avec Chaco et pyqt pour tracer une tâche d'acquisition de données en temps réel pour du matériel de laboratoire. J'utilisais auparavant matplotlib, mais il s'est avéré trop lent (j'ai même essayé l'animation). Le code suivant fonctionne bien lorsque j'intègre un tracé matplotlib dans une fenêtre pyqt, mais avec chaco, rien ne se passe lorsque j'émets mon signal de mise à jour depuis l'intérieur d'un thread. Ce code fonctionnera si vous n'utilisez pas de thread pour l'acquisition simulée. J'ai essayé d'utiliser qthreads sans succès non plus (y compris quelque chose comme ceci : Problème de threading et de signaux dans PyQt ). Y a-t-il quelqu'un qui a utilisé pyqt + chaco + threading et qui pourrait m'aider à trouver où je me trompe, ou ce qui se passe ?

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

1voto

dpinte Points 432

Votre appel à joindre sur le mainthread (qui est le thread de l'interface utilisateur) bloque ce thread et empêche les événements d'être traités par l'interface utilisateur. Si vous avez lancé la boucle d'événements de l'application et de l'interface utilisateur dans la fonction principale et que vous attendez que l'application soit fermée sans appeler t.join(), cela devrait fonctionner correctement.

C'est ainsi qu'il faut procéder avec les applications Traits/TraitsUI/Chaco ordinaires.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X