pythonenthoughtchaco

Dynamical plot with Chaco


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

Solution

  • 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