Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Modification of the example from Chaco
Allows editing of a line plot.
Left-dragging a point will move its position.
Right-drag pans the plot.
Mousewheel up and down zooms the plot in and out.
Pressing "z" brings up the Zoom Box, and you can click-drag a rectangular
region to zoom. If you use a sequence of zoom boxes, pressing control-y and
control-z (use Meta-y and Meta-z on Mac) moves you forwards and backwards
through the "zoom history".
# Major library imports
from numpy import linspace, array
from scipy.special import jn
from chaco.example_support import COLOR_PALETTE
# Enthought library imports
from import DragTool
from enable.api import Component, ComponentEditor
from traits.api import HasTraits, Instance, Int, Tuple, Array, Delegate, on_trait_change
from traitsui.api import UItem, View, Item
# Chaco imports
from chaco.api import add_default_axes, add_default_grids, \
OverlayPlotContainer, PlotLabel, ScatterPlot, create_line_plot
from import PanTool, ZoomTool
class PointDraggingTool(DragTool):
component = Instance(Component)
x_u = component.x_u
y_u = component.y_u
# The pixel distance from a point that the cursor is still considered
# to be 'on' the point
threshold = Int(5)
# The index of the point being dragged
_drag_index = Int(-1)
# The original dataspace values of the index and value datasources
# corresponding to _drag_index
_orig_value = Tuple
def is_draggable(self, x, y):
# Check to see if (x,y) are over one of the points in self.component
if self._lookup_point(x, y) is not None:
return True
return False
def normal_mouse_move(self, event):
plot = self.component
ndx = plot.map_index((event.x, event.y), self.threshold)
if ndx is None:
if plot.index.metadata.has_key('selections'):
del plot.index.metadata['selections']
plot.index.metadata['selections'] = [ndx]
def drag_start(self, event):
plot = self.component
ndx = plot.map_index((event.x, event.y), self.threshold)
if ndx is None:
self._drag_index = ndx
self._orig_value = (plot.index.get_data()[ndx], plot.value.get_data()[ndx])
def dragging(self, event):
plot = self.component
data_x, data_y = plot.map_data((event.x, event.y))
plot.index._data[self._drag_index] = data_x
plot.value._data[self._drag_index] = data_y
plot.index.data_changed = True
plot.value.data_changed = True
def drag_cancel(self, event):
plot = self.component
plot.index._data[self._drag_index] = self._orig_value[0]
plot.value._data[self._drag_index] = self._orig_value[1]
plot.index.data_changed = True
plot.value.data_changed = True
def drag_end(self, event):
plot = self.component
if plot.index.metadata.has_key('selections'):
del plot.index.metadata['selections']
self.component.x_u = plot.index.get_data()
self.component.y_u = plot.value.get_data()
print "Draggable point has moved: x=", self.component.x_u, "y=", self.component.y_u
def _lookup_point(self, x, y):
""" Finds the point closest to a screen point if it is within self.threshold
x : float
screen x-coordinate
y : float
screen y-coordinate
(screen_x, screen_y, distance) of datapoint nearest to the input *(x,y)*.
If no data points are within *self.threshold* of *(x,y)*, returns None.
if hasattr(self.component, 'get_closest_point'):
# This is on BaseXYPlots
return self.component.get_closest_point((x,y), threshold=self.threshold)
return None
# Attributes to use for the plot view.
title="Simple line plot"
# # Demo class that is used by the application.
class Demo(HasTraits):
plot = Instance(Component)
x = Array
y = Array
traits_view = View(UItem('plot', editor=ComponentEditor()),
width=size[0], height=size[1], resizable=True,
def update_xy(self):
print "From Demo - point has moved: x=", self.plot.component.x_u, "y=", self.plot.component.y_u
self.x = self.plot.component.x_u
self.y = self.plot.component.y_u
def _plot_default(self):
# return _create_plot_component()
# # Create the Chaco plot.
#def _create_plot_component(self):
container = OverlayPlotContainer(padding = 50, fill_padding = True,
bgcolor = "lightgray", use_backbuffer=True)
lineplot = create_line_plot((self.x,self.y), color=tuple(COLOR_PALETTE[0]), width=2.0)
lineplot.selected_color = "none"
scatter = ScatterPlot(index = lineplot.index,
value = lineplot.value,
index_mapper = lineplot.index_mapper,
value_mapper = lineplot.value_mapper,
x_u = Array,
y_u = Array,
color = tuple(COLOR_PALETTE[0]),
marker_size = 5)
scatter.index.sort_order = "ascending"
scatter.bgcolor = "white"
scatter.border_visible = True
add_default_axes(scatter), drag_button="right"))
# The ZoomTool tool is stateful and allows drawing a zoom
# box to select a zoom region.
zoom = ZoomTool(scatter, tool_mode="box", always_on=False)
# Add the title at the top
container.overlays.append(PlotLabel("Line Editor",
font = "swiss 16",
return container
if __name__ == "__main__":
demo = Demo()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment