Dynamical plot with Chaco

619 views Asked by At

I need to add some points to an existent plot I do in chaco. I have tried with plot.request_redraw() but it didn't work. What else can I do?

This is the piece of code:

class PlotApp(HasTraits):

plotdata = Instance(ArrayPlotData)
returns_plot = Instance(Plot)

plot_type = Enum('line', 'scatter')

corr_renderer = Any()

x_min = Float()
x_max = Float()

traits_view = View(
                VGroup(
                    HGroup(spring, Label('Click point to select/unselect'), 
                        spring),
                    #Item('plot_type'),
                    Item('returns_plot', editor=ComponentEditor(size=size,
                                                        bgcolor=bg_color),
                         show_label=False),
                    #Item('num_medicion', width=-225),
                    orientation = "vertical"),
                resizable=True, title=title
                )

def _create_returns_plot(self):
    plot = Plot(self.plotdata)
    plot.legend.visible = True
    plot.x_axis = None
    x_axis = PlotAxis(plot, orientation="bottom") 
    plot.overlays.append(x_axis)

    renderer = plot.plot(("index", "value"), type="scatter",name = "Mediciones")[0]

    #Agrego todas las tools necesarias
    renderer.tools.append(ScatterInspector(renderer, selection_mode="toggle", persistent_hover=False))
    renderer.overlays.append(
            ScatterInspectorOverlay(renderer,
                hover_color = "transparent",
                hover_marker_size = 10,
                hover_outline_color = "purple",
                hover_line_width = 2,
                selection_marker_size = 8,
                selection_color = "red")
            )

    renderer.tools.append(RangeSelection(renderer, left_button_selects = False, disable_left_mouse = True, \
                                        rigth_button_selects = True, \
                                        auto_handle_event = False, metadata_name = "annotations"))
    renderer.overlays.append(RangeSelectionOverlay(component=renderer, metadata_name = "annotations"))

    renderer.tools.append(PanTool(renderer))
    renderer.overlays.append(ZoomTool(renderer, drag_button="right"))

    self.index_datasource = renderer.index
    self.index_datasource.on_trait_change(self._selections_changed, "metadata_changed")                                     
    self.returns_plot = plot

def _create_data(self):
    #genero los datos, más adelante los voy a leer con pandas
    npts = 40
    x_max = 10
    x = np.random.random(npts)
    x = x * x_max
    error = np.random.random(npts)
    y = 2 + 3*x + 5*error

    #Esta parte es para ordenar los elementos
    x_ordenado = np.array([])
    y_ordenado = np.array([])
    orden = range(x.size)
    nuevo_orden = np.array([])

    for i in range(x.size):
        arg_min = x.argmin()
        x_ordenado = np.append(x_ordenado, x[arg_min])
        y_ordenado = np.append(y_ordenado, y[arg_min])
        nuevo_orden = np.append(nuevo_orden, orden[arg_min])
        x = np.delete(x, arg_min)
        y = np.delete(y, arg_min)
        orden = np.delete(orden, arg_min)

    self.x_ordenado = x_ordenado
    self.y_ordenado = y_ordenado

    #Genero el retorno para el plot
    plotdata = ArrayPlotData()
    plotdata.set_data("index", x_ordenado)
    plotdata.set_data("value", y_ordenado)

    self.plotdata = plotdata


def _selections_changed(self):

    #Obtengo los puntos marcados manualmente
    self.posicion_puntos_selec = self.index_datasource.metadata.get('selections', ())
    #obtengo los puntos que marque con el rectangulo
    seleccionado_range = self.index_datasource.metadata.get('annotations', ())
    #Cuando desmarcon con el rectangu, el tipo de annotations es NoneType, 
    #con este if lo cambio a tuple
    type_range = type(self.index_datasource.metadata['annotations'])

    if type_range != tuple:
        self.index_datasource.metadata['annotations'] = []
    else:
        self.x_min, self.x_max = seleccionado_range

@on_trait_change("posicion_puntos_selec, x_min, x_max")
def _perform_calculations(self):
    plot = self.returns_plot
    x_nuevo = np.append(self.x_calcular, [11, 12])
    y_nuevo = np.append(self.y_calcular, [11, 12])
    self.corr_renderer = plot.plot((x_nuevo, y_nuevo),
                            type="scatter", color="blue")[0]

    plot.request_redraw()
1

There are 1 answers

0
jonathanrocher On BEST ANSWER

To update the data of an existing plot, the best and simplest is to update the existing ArrayPlotData of the existing Plot instance being displayed. There are listeners inside Chaco that will take care of the redraw. Below is an example inspired from your code:

from traits.api import HasTraits, Enum, Instance, Button
from traitsui.api import View, Item, VGroup
from enable.api import ComponentEditor
from chaco.api import Plot, ArrayPlotData, PlotAxis

from numpy import arange

class PlotApp(HasTraits):

    plotdata = Instance(ArrayPlotData)
    returns_plot = Instance(Plot)

    plot_type = Enum('line', 'scatter')

    add_points = Button

    traits_view = View(
                VGroup(Item("add_points"),
                    Item('returns_plot', editor=ComponentEditor(),
                        show_label=False),
                    orientation = "vertical"),
                resizable=True, title="Test"
                )

    def _returns_plot_default(self):
        self.plotdata = ArrayPlotData(index=arange(100), value=arange(100))
        plot = Plot(self.plotdata)
        plot.legend.visible = True
        plot.x_axis = None
        x_axis = PlotAxis(plot, orientation="bottom")
        plot.overlays.append(x_axis)

        plot.plot(("index", "value"), type="scatter", name = "Mediciones")
        return plot

    def _add_points_fired(self):
        current_length = len(self.plotdata["index"])
        self.plotdata.set_data("index", arange(current_length+1))
        self.plotdata.set_data("value", arange(current_length+1))

if __name__ == "__main__":
    app = PlotApp()
    app.configure_traits()

HTH, Jonathan