Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
Chaco CHANGELOG
===============

Release 4.7.2
-------------

Fixes

* FIX: Ensure contiguous inputs to points_in_polygon (#409 & #410)
* FIX: Handle multiple plots in LegendHighlighter (#403)
* FIX: alias six.moves as sm (#401)
* FIX: Respect visibility in the LegendHighlighter (#402)
* BUG: Fix use of itertools.chain() (#394)


Release 4.7.1
-------------

Expand Down
3 changes: 1 addition & 2 deletions chaco/errorbar_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import with_statement

import six
import six.moves
import six.moves as sm

# Major library imports
from numpy import column_stack, compress, invert, isnan, transpose
Expand Down Expand Up @@ -159,4 +159,3 @@ def _render_bar_endcap(self, gc, start, end, low, high, axis):

def _render_icon(self, gc, x, y, width, height):
pass

2 changes: 1 addition & 1 deletion chaco/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ def _handle_range_changed(self, name, old, new):
if new is not None:
new.add(datasource)
range_name = name + "_range"
for renderer in itertools.chain(six.itervalues(self.plots)):
for renderer in itertools.chain(*six.itervalues(self.plots)):
if hasattr(renderer, range_name):
setattr(renderer, range_name, new)

Expand Down
2 changes: 1 addition & 1 deletion chaco/polygon_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def hittest(self, screen_pt, threshold=7.0, return_distance=False):
data_pt = self.map_data(screen_pt, all_values=True)
index = self.index.get_data()
value = self.value.get_data()
poly = np.vstack((index,value)).T
poly = np.column_stack((index, value))
if points_in_polygon([data_pt], poly)[0] == 1:
return True
else:
Expand Down
36 changes: 36 additions & 0 deletions chaco/tests/errorbarplot_test_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import unittest

import numpy as np
from numpy import alltrue

# Chaco imports
from chaco.api import (
ArrayDataSource, DataRange1D, ErrorBarPlot, LinearMapper,
PlotGraphicsContext
)


class DrawErrorBarPlotCase(unittest.TestCase):
def test_errorbarplot(self):
""" Coverage test to check basic case works """
size = (50, 50)
x = np.array([1, 2])
y = np.array([5, 10])
errors = np.array([1, 2])
low = ArrayDataSource(y - errors)
high = ArrayDataSource(y + errors)
errorbar_plot = ErrorBarPlot(
index=ArrayDataSource(x),
values=ArrayDataSource(y),
index_mapper=LinearMapper(range=DataRange1D(low=0, high=3)),
value_mapper=LinearMapper(range=DataRange1D(low=0, high=15)),
value_low=low,
value_high=high,
color='blue',
line_width=3.0,
)
errorbar_plot.outer_bounds = list(size)
gc = PlotGraphicsContext(size)
gc.render_component(errorbar_plot)
actual = gc.bmp_array[:, :, :]
self.assertFalse(alltrue(actual == 255))
15 changes: 14 additions & 1 deletion chaco/tests/plot_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from numpy import arange

# Chaco imports
from chaco.api import ArrayPlotData, Plot
from chaco.api import ArrayPlotData, Plot, DataRange1D


class PlotTestCase(unittest.TestCase):
Expand All @@ -18,5 +18,18 @@ def test_plot_from_unsupported_array_shape(self):
data.update_data(x=arr, y=arr)
self.assertRaises(ValueError, plot.plot, ("x", "y"))

def test_range_change(self):
arr = arange(10)
data = ArrayPlotData(x=arr, y=arr)
plot = Plot(data)
renderer = plot.plot(('x', 'y'))[0]
new_range = DataRange1D()
old_range = plot.index_range
self.assertIsNot(old_range, new_range)
self.assertIs(renderer.index_range, old_range)
plot.index_range = new_range
self.assertIs(plot.index_range, new_range)
self.assertIs(renderer.index_range, new_range)

if __name__ == "__main__":
unittest.main()
6 changes: 3 additions & 3 deletions chaco/tools/lasso_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""
# Major library imports
import numpy
from numpy import array, empty, sometrue, transpose, vstack, zeros
from numpy import array, column_stack, empty, sometrue, vstack, zeros

# Enthought library imports
from traits.api import Any, Array, Enum, Event, Bool, Instance, \
Expand Down Expand Up @@ -308,8 +308,8 @@ def _map_data(self, point):
def _get_data(self):
""" Returns the datapoints in the plot, as an Nx2 array of (x,y).
"""
return transpose(array((self.plot.index.get_data(), self.plot.value.get_data())))

return column_stack((self.plot.index.get_data(),
self.plot.value.get_data()))

#------------------------------------------------------------------------
# Property getter/setters
Expand Down
61 changes: 34 additions & 27 deletions chaco/tools/legend_highlighter.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import operator

import six
import six.moves as sm
from itertools import chain

# ETS imports
from chaco.tools.api import LegendTool
from traits.api import List, Float

concat = chain.from_iterable


def _ensure_list(obj):
""" NOTE: The Legend stores plots in a dictionary with either single
renderers as values, or lists of renderers.
This function helps us assume we're always working with lists
"""
return obj if isinstance(obj, list) else [obj]


def get_hit_plots(legend, event):
if legend is None or not legend.is_in(event.x, event.y):
Expand All @@ -27,7 +34,7 @@ def get_hit_plots(legend, event):
ndx = legend._cached_labels.index(label)
label_name = legend._cached_label_names[ndx]
renderers = legend.plots[label_name]
return renderers
return _ensure_list(renderers)
except (ValueError, KeyError):
return []

Expand All @@ -50,39 +57,39 @@ class LegendHighlighter(LegendTool):
_selected_renderers = List

def normal_left_down(self, event):
if not self.component.is_in(event.x, event.y):
if (not self.component.visible or
not self.component.is_in(event.x, event.y)):
return

plots = get_hit_plots(self.component, event)

if len(plots) > 0:
plot = plots[0]

if event.shift_down:
# User in multi-select mode by using [shift] key.
if event.shift_down:
# User in multi-select mode by using [shift] key.
for plot in plots:
if plot in self._selected_renderers:
self._selected_renderers.remove(plot)
else:
self._selected_renderers.append(plot)

else:
# User in single-select mode.
add_plot = plot not in self._selected_renderers
self._selected_renderers = []
if add_plot:
self._selected_renderers.append(plot)

if self._selected_renderers:
self._set_states(self.component.plots)
else:
self._reset_selects(self.component.plots)
plot.request_redraw()
elif plots:
# User in single-select mode.
add_plot = any(plot not in self._selected_renderers
for plot in plots)
self._selected_renderers = []
if add_plot:
self._selected_renderers.extend(plots)

if self._selected_renderers:
self._set_states(self.component.plots)
else:
self._reset_selects(self.component.plots)

if plots:
plots[0].request_redraw()

event.handled = True

def _reset_selects(self, plots):
""" Set all renderers to their default values. """
for plot in sm.reduce(operator.add, plots.values()):
for plot in concat(_ensure_list(p) for p in plots.values()):
if not hasattr(plot, '_orig_alpha'):
plot._orig_alpha = plot.alpha
plot._orig_line_width = plot.line_width
Expand All @@ -92,7 +99,7 @@ def _reset_selects(self, plots):

def _set_states(self, plots):
""" Decorates a plot to indicate it is selected """
for plot in sm.reduce(operator.add, plots.values()):
for plot in concat(_ensure_list(p) for p in plots.values()):
if not hasattr(plot, '_orig_alpha'):
# FIXME: These attributes should be put into the class def.
plot._orig_alpha = plot.alpha
Expand Down
5 changes: 3 additions & 2 deletions examples/demo/multiaxis.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
from chaco.api import create_line_plot, add_default_axes, \
add_default_grids, OverlayPlotContainer, \
PlotLabel, Legend, PlotAxis
from chaco.tools.api import PanTool, LegendTool, TraitsTool, \
BroadcasterTool
from chaco.tools.api import (PanTool, LegendTool, LegendHighlighter,
TraitsTool, BroadcasterTool)

#===============================================================================
# # Create the Chaco plot.
Expand Down Expand Up @@ -76,6 +76,7 @@ def _create_plot_component():

legend = Legend(component=container, padding=10, align="ur")
legend.tools.append(LegendTool(legend, drag_button="right"))
legend.tools.append(LegendHighlighter(legend))
container.overlays.append(legend)

# Set the list of plots on the legend
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

MAJOR = 4
MINOR = 7
MICRO = 2
MICRO = 3

IS_RELEASED = False

Expand Down