Add point in interactive plot Chaco

552 views Asked by At

I'm using chaco and traits.api to do an interactive plot. I can select some points in the plot, and do some calculus. Now I need update my initial plot, but I don't know how to do (I need add points). Thanks

This is the code

import numpy as np
from enable.component_editor import ComponentEditor
from traits.api import HasTraits, Instance, Int, List, Str, Enum, on_trait_change, Any, DelegatesTo, Array
from traitsui.api import Item, View, HSplit, VGroup, EnumEditor, HGroup, spring, Label

from enthought.traits.api import HasTraits, Array, Range, Float, Enum, on_trait_change, Property

from chaco.api import ArrayPlotData, Plot, PlotAxis, \
    ScatterInspectorOverlay
from chaco.chaco_plot_editor import ChacoPlotItem
from chaco.scales.api import CalendarScaleSystem
from chaco.scales_tick_generator import ScalesTickGenerator
from chaco.example_support import COLOR_PALETTE
from chaco.tools.api import PanTool, ZoomTool, RangeSelection, \
    RangeSelectionOverlay, LegendTool, ScatterInspector

# Attributes to use for the plot view.
size=(800,800)
title="Scatter plot with selection"
bg_color="lightgray"
#===============================================================================

class PlotApp(HasTraits):

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

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

corr_renderer = Any()

x_min = Float()
x_max = Float()
posicion_puntos_selec = Array

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),
                    orientation = "vertical"),
                resizable=True, title=title
                )


def __init__(self, *symbols, **kwtraits):
    super(PlotApp, self).__init__(symbols=list(symbols), **kwtraits)
    self._create_data()
    self._create_returns_plot()

    return

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")[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
    seleccionado_manu = 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

    self.posicion_puntos_selec = seleccionado_manu

def calculos(self):
    indice_min = 0
    x_selec = np.delete(self.x_ordenado, self.posicion_puntos_selec)
    y_selec = np.delete(self.y_ordenado, self.posicion_puntos_selec)
    indice_max = x_selec.size - 1

    #ara saber los extremos del vector x que son tomado por rangesel
    if self.x_min != 0 and self.x_max != 0:
        while self.x_min >= x_selec[indice_min]:
            indice_min += 1
        while self.x_max <= x_selec[indice_max]:
            indice_max -= 1

    #x e y con los cuales quiero hacer los calculos
    self.x_calcular = x_selec[indice_min:indice_max + 1]
    self.y_calcular = y_selec[indice_min:indice_max + 1]

@on_trait_change("x_min, x_max, posicion_puntos_selec")
def _perform_calculations(self):
    self.calculos()
    plot = Plot(self.plotdata)
    renderer = plot.plot(("index", "value"), type="scatter")[0]



demo = PlotApp("AAPL", "GOOG", "MSFT")
if __name__ == "__main__":
    demo.configure_traits()
1

There are 1 answers

0
labjunky On

Take a look at the chaco example data_stream.py. The data in this example is updated every 100ms in response to a timer tick (the callback function timer_tick() in the Controller class) https://github.com/enthought/chaco/blob/master/examples/demo/advanced/data_stream.py