Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enh: Simplify controlling text of ImageInspectorOverlay #431

Merged
merged 9 commits into from
Jun 10, 2019
52 changes: 34 additions & 18 deletions chaco/tools/image_inspector_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ def normal_mouse_move(self, event):
if hasattr(plot, "_cached_mapped_image") and \
plot._cached_mapped_image is not None:
self.new_value = \
dict(indices=ndx,
data_value=image_data.data[y_index, x_index],
color_value=plot._cached_mapped_image[y_index,
x_index])
{"indices": ndx,
"data_value": image_data.data[y_index, x_index],
"color_value": plot._cached_mapped_image[y_index,
x_index]}

else:
self.new_value = \
dict(indices=ndx,
color_value=image_data.data[y_index, x_index])
{"indices": ndx,
"color_value": image_data.data[y_index, x_index]}

self.last_mouse_position = (event.x, event.y)
return
Expand All @@ -100,10 +100,35 @@ class ImageInspectorOverlay(TextBoxOverlay):
# tool's location, or whether it should be forced to be hidden or visible.
visibility = Enum("auto", True, False)

# Private interface -------------------------------------------------------

def _build_text_from_event(self, event):
""" Create a formatted string to display from the mouse event data.
"""
newstring = ""
if 'indices' in event:
newstring += '(%d, %d)' % event['indices']
if 'color_value' in event:
try:
newstring += "\n(%d, %d, %d)" % tuple(
map(int, event['color_value'][:3]))
except IndexError:
# color value is an integer, for example if gray scale image
newstring += "\n%d" % event['color_value']

if 'data_value' in event:
newstring += "\n{}".format(event['data_value'])

return newstring

# Traits listeners --------------------------------------------------------

def _image_inspector_changed(self, old, new):
if old:
old.on_trait_event(self._new_value_updated, 'new_value', remove=True)
old.on_trait_change(self._tool_visible_changed, "visible", remove=True)
old.on_trait_event(self._new_value_updated, 'new_value',
remove=True)
old.on_trait_change(self._tool_visible_changed, "visible",
remove=True)
if new:
new.on_trait_event(self._new_value_updated, 'new_value')
new.on_trait_change(self._tool_visible_changed, "visible")
Expand All @@ -123,16 +148,7 @@ def _new_value_updated(self, event):
else:
self.alternate_position = None

d = event
newstring = ""
if 'indices' in d:
newstring += '(%d, %d)' % d['indices'] + '\n'
if 'color_value' in d:
newstring += "(%d, %d, %d)" % tuple(map(int,d['color_value'][:3])) + "\n"
if 'data_value' in d:
newstring += str(d['data_value'])

self.text = newstring
self.text = self._build_text_from_event(event)
self.component.request_redraw()

def _visible_changed(self):
Expand Down
209 changes: 209 additions & 0 deletions chaco/tools/tests/test_image_inspector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
""" Tests for the ImageInspectorTool and ImageInspectorOverlay tools.
"""

from unittest import TestCase
import numpy as np
from numpy.testing import assert_array_equal

from chaco.api import ArrayPlotData, Plot
from chaco.tools.api import ImageInspectorTool, ImageInspectorOverlay
from enable.testing import EnableTestAssistant
from traits.testing.unittest_tools import UnittestTools


def create_image_plot(img_values, **kwargs):
data = ArrayPlotData(img=img_values)
plot = Plot(data)
plot.img_plot("img", **kwargs)
return plot


class CustomImageInspectorOverlay(ImageInspectorOverlay):
def _build_text_from_event(self, event):
return 'Position: ({}, {})'.format(*event['indices'])


class BaseImageInspectorTool(EnableTestAssistant, UnittestTools):
def setUp(self):
# Control the pixel size of the plot to know where the tiles are:
self.plot.bounds = [100, 100]
self.plot._window = self.create_mock_window()
renderer = self.plot.plots["plot0"][0]
self.tool = ImageInspectorTool(component=renderer)
self.overlay = ImageInspectorOverlay(component=renderer,
image_inspector=self.tool)
self.overlay2 = CustomImageInspectorOverlay(component=renderer,
image_inspector=self.tool)
self.plot.active_tool = self.tool
self.plot.do_layout()

self.insp_event = None

def test_mouse_move_records_last_position(self):
tool = self.tool

self.assertEqual(tool.last_mouse_position, ())

self.mouse_move(tool, 0, 0)
self.assertEqual(tool.last_mouse_position, (0, 0))

self.mouse_move(tool, 10, 10)
self.assertEqual(tool.last_mouse_position, (10, 10))

self.mouse_leave(tool, 1000, 1000)
self.assertEqual(tool.last_mouse_position, (10, 10))

def test_mouse_move_custom_overlay(self):
tool = self.tool

# Add a listener to catch the emitted event:
tool.on_trait_change(self.store_inspector_event, "new_value")
try:
self.assertIsNone(self.insp_event)

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay2, "text", 1):
self.mouse_move(tool, 0, 0)

self.assertEqual(self.overlay2.text, 'Position: (0, 0)')
finally:
tool.on_trait_change(self.store_inspector_event, "new_value",
remove=True)

# Helper methods ----------------------------------------------------------

def store_inspector_event(self, event):
self.insp_event = event


class TestImageInspectorToolGray(BaseImageInspectorTool, TestCase):
""" Tests for the ImageInspector tool with a gray scale image
"""

def setUp(self):
values = np.arange(4).reshape(2, 2)
self.plot = create_image_plot(values)
super(TestImageInspectorToolGray, self).setUp()

def test_mouse_move_collect_image_info(self):
tool = self.tool

# Add a listener to catch the emitted event:
tool.on_trait_change(self.store_inspector_event, "new_value")
try:
self.assertIsNone(self.insp_event)

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
self.mouse_move(tool, 0, 0)
self.assertEqual(self.insp_event["color_value"], 0)
self.assertEqual(self.insp_event["indices"], (0, 0))

self.assertEqual(self.overlay.text, '(0, 0)\n0')

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
# Move within the same tile:
self.mouse_move(tool, 90, 0)
self.assertEqual(self.insp_event["color_value"], 1)
self.assertEqual(self.insp_event["indices"], (1, 0))

self.assertEqual(self.overlay.text, '(1, 0)\n1')

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitDoesNotChange(self.overlay, "text"):
# Move within the same tile:
self.mouse_move(tool, 91, 0)
self.assertEqual(self.insp_event["color_value"], 1)
self.assertEqual(self.insp_event["indices"], (1, 0))

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
# Move to another value in the image:
self.mouse_move(tool, 0, 90)
self.assertEqual(self.insp_event["color_value"], 2)
self.assertEqual(self.insp_event["indices"], (0, 1))

self.assertEqual(self.overlay.text, '(0, 1)\n2')

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
# Move within the same tile:
self.mouse_move(tool, 90, 90)
self.assertEqual(self.insp_event["color_value"], 3)
self.assertEqual(self.insp_event["indices"], (1, 1))

self.assertEqual(self.overlay.text, '(1, 1)\n3')

finally:
tool.on_trait_change(self.store_inspector_event, "new_value",
remove=True)


class TestImageInspectorToolRGB(BaseImageInspectorTool, TestCase):
""" Tests for the ImageInspector tool with an RGB image.
"""

def setUp(self):
values = np.arange(12).reshape(2, 2, 3)
self.plot = create_image_plot(values)
super(TestImageInspectorToolRGB, self).setUp()

def test_mouse_move_collect_image_info(self):
tool = self.tool

# Add a listener to catch the emitted event:
tool.on_trait_change(self.store_inspector_event, "new_value")
try:
self.assertIsNone(self.insp_event)

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
self.mouse_move(tool, 0, 0)
assert_array_equal(self.insp_event["color_value"],
np.array([0, 1, 2]))
self.assertEqual(self.insp_event["indices"], (0, 0))

self.assertEqual(self.overlay.text, '(0, 0)\n(0, 1, 2)')

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
# Move within the same tile:
self.mouse_move(tool, 90, 0)
assert_array_equal(self.insp_event["color_value"],
np.array([3, 4, 5]))
self.assertEqual(self.insp_event["indices"], (1, 0))

self.assertEqual(self.overlay.text, '(1, 0)\n(3, 4, 5)')

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitDoesNotChange(self.overlay, "text"):
# Move within the same tile:
self.mouse_move(tool, 91, 0)
assert_array_equal(self.insp_event["color_value"],
np.array([3, 4, 5]))
self.assertEqual(self.insp_event["indices"], (1, 0))

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
# Move to another value in the image:
self.mouse_move(tool, 0, 90)
assert_array_equal(self.insp_event["color_value"],
np.array([6, 7, 8]))
self.assertEqual(self.insp_event["indices"], (0, 1))

self.assertEqual(self.overlay.text, '(0, 1)\n(6, 7, 8)')

with self.assertTraitChanges(tool, "new_value", 1):
with self.assertTraitChanges(self.overlay, "text", 1):
# Move within the same tile:
self.mouse_move(tool, 90, 90)
assert_array_equal(self.insp_event["color_value"],
np.array([9, 10, 11]))
self.assertEqual(self.insp_event["indices"], (1, 1))

self.assertEqual(self.overlay.text, '(1, 1)\n(9, 10, 11)')

finally:
tool.on_trait_change(self.store_inspector_event, "new_value",
remove=True)
2 changes: 1 addition & 1 deletion examples/demo/basic/image_inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _create_plot_component():# Create a scalar field to colormap
plot.tools.append(PanTool(plot))
zoom = ZoomTool(component=plot, tool_mode="box", always_on=False)
plot.overlays.append(zoom)
imgtool = ImageInspectorTool(img_plot)
imgtool = ImageInspectorTool(component=img_plot)
img_plot.tools.append(imgtool)
overlay = ImageInspectorOverlay(component=img_plot, image_inspector=imgtool,
bgcolor="white", border_visible=True)
Expand Down