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()
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 theController
class) https://github.com/enthought/chaco/blob/master/examples/demo/advanced/data_stream.py