Skip to content

Commit bbbe9d4

Browse files
authored
Merge pull request #56 from Carifio24/add-tools
Add hover and horizontal/vertical zoom tools
2 parents 8707c32 + 570170b commit bbbe9d4

File tree

10 files changed

+225
-5
lines changed

10 files changed

+225
-5
lines changed

glue_plotly/common/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def base_rectilinear_axis(viewer_state, axis):
7878
ticks='outside',
7979
showline=True,
8080
showgrid=False,
81+
fixedrange=True,
8182
showticklabels=True,
8283
tickfont=dict(
8384
family=DEFAULT_FONT,

glue_plotly/viewers/common/tests/__init__.py

Whitespace-only changes.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from glue_jupyter import jglue
2+
3+
from glue_plotly.viewers.common.tests.utils import ExampleViewer
4+
5+
6+
class TestTools(object):
7+
8+
def setup_method(self, method):
9+
self.app = jglue()
10+
self.viewer = self.app.new_data_viewer(ExampleViewer)
11+
12+
def teardown_method(self, method):
13+
pass
14+
15+
def get_tool(self, id):
16+
return self.viewer.toolbar.tools[id]
17+
18+
def test_rectzoom(self):
19+
tool = self.get_tool('plotly:zoom')
20+
tool.activate()
21+
assert self.viewer.figure.layout['selectdirection'] == 'any'
22+
assert self.viewer.figure.layout['dragmode'] == 'select'
23+
tool.deactivate()
24+
assert self.viewer.figure.layout['dragmode'] is False
25+
26+
def test_hzoom(self):
27+
tool = self.get_tool('plotly:hzoom')
28+
tool.activate()
29+
assert self.viewer.figure.layout['selectdirection'] == 'h'
30+
assert self.viewer.figure.layout['dragmode'] == 'select'
31+
tool.deactivate()
32+
assert self.viewer.figure.layout['dragmode'] is False
33+
34+
def test_vzoom(self):
35+
tool = self.get_tool('plotly:vzoom')
36+
tool.activate()
37+
assert self.viewer.figure.layout['selectdirection'] == 'v'
38+
assert self.viewer.figure.layout['dragmode'] == 'select'
39+
tool.deactivate()
40+
assert self.viewer.figure.layout['dragmode'] is False
41+
42+
def test_pan(self):
43+
tool = self.get_tool('plotly:pan')
44+
tool.activate()
45+
assert self.viewer.figure.layout['dragmode'] == 'pan'
46+
tool.deactivate()
47+
assert self.viewer.figure.layout['dragmode'] is False
48+
49+
def test_hrange_select(self):
50+
tool = self.get_tool('plotly:xrange')
51+
tool.activate()
52+
assert self.viewer.figure.layout['selectdirection'] == 'h'
53+
assert self.viewer.figure.layout['dragmode'] == 'select'
54+
tool.deactivate()
55+
assert self.viewer.figure.layout['dragmode'] is False
56+
57+
def test_vrange_select(self):
58+
tool = self.get_tool('plotly:yrange')
59+
tool.activate()
60+
assert self.viewer.figure.layout['selectdirection'] == 'v'
61+
assert self.viewer.figure.layout['dragmode'] == 'select'
62+
tool.deactivate()
63+
assert self.viewer.figure.layout['dragmode'] is False
64+
65+
def test_rect_select(self):
66+
tool = self.get_tool('plotly:rectangle')
67+
tool.activate()
68+
assert self.viewer.figure.layout['selectdirection'] == 'any'
69+
assert self.viewer.figure.layout['dragmode'] == 'select'
70+
tool.deactivate()
71+
assert self.viewer.figure.layout['dragmode'] is False
72+
73+
def test_lasso_select(self):
74+
tool = self.get_tool('plotly:lasso')
75+
tool.activate()
76+
assert self.viewer.figure.layout['dragmode'] == 'lasso'
77+
tool.deactivate()
78+
assert self.viewer.figure.layout['dragmode'] is False
79+
80+
def test_home(self):
81+
xmin, xmax = self.viewer.state.x_min, self.viewer.state.x_max
82+
ymin, ymax = self.viewer.state.y_min, self.viewer.state.y_max
83+
self.viewer.state.x_min = 10
84+
self.viewer.state.x_max = 27
85+
self.viewer.state.y_min = -5
86+
self.viewer.state.y_max = 13
87+
tool = self.get_tool('plotly:home')
88+
tool.activate()
89+
print(self.viewer.state)
90+
assert self.viewer.state.x_min == xmin
91+
assert self.viewer.state.x_max == xmax
92+
assert self.viewer.state.y_min == ymin
93+
assert self.viewer.state.y_max == ymax
94+
xaxis = self.viewer.figure.layout.xaxis
95+
assert xaxis.range == (xmin, xmax)
96+
yaxis = self.viewer.figure.layout.yaxis
97+
assert yaxis.range == (ymin, ymax)
98+
99+
def test_hover(self):
100+
tool = self.get_tool('plotly:hover')
101+
tool.activate()
102+
assert self.viewer.figure.layout['hovermode'] == 'closest'
103+
tool.deactivate()
104+
assert self.viewer.figure.layout['hovermode'] is False
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from echo import CallbackProperty
2+
from glue.viewers.common.state import ViewerState
3+
from ipywidgets import VBox
4+
5+
from glue_plotly.viewers.common import PlotlyBaseView
6+
7+
8+
class ExampleState(ViewerState):
9+
x_axislabel = CallbackProperty("")
10+
y_axislabel = CallbackProperty("")
11+
x_min = CallbackProperty(0)
12+
x_max = CallbackProperty(1)
13+
y_min = CallbackProperty(0)
14+
y_max = CallbackProperty(1)
15+
show_axes = CallbackProperty(True)
16+
17+
def reset_limits(self):
18+
self.x_min = 0
19+
self.x_max = 1
20+
self.y_min = 0
21+
self.y_max = 1
22+
23+
24+
class ExampleOptions(VBox):
25+
26+
def __init__(self, viewer_state):
27+
28+
self.state = viewer_state
29+
super().__init__([])
30+
31+
32+
class ExampleViewer(PlotlyBaseView):
33+
34+
_state_cls = ExampleState
35+
_options_cls = ExampleOptions
36+
37+
tools = ['plotly:zoom', 'plotly:hzoom', 'plotly:vzoom',
38+
'plotly:pan', 'plotly:xrange', 'plotly:yrange',
39+
'plotly:rectangle', 'plotly:lasso', 'plotly:home',
40+
'plotly:hover']
41+
42+
def __init__(self, session, state=None):
43+
super().__init__(session, state)

glue_plotly/viewers/common/tools.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def deactivate(self):
3737
self.viewer.set_selection_callback(None)
3838
self.viewer.set_selection_active(False)
3939
self.viewer.figure.on_edits_completed(self._clear_selection)
40+
super().deactivate()
4041

4142
def _clear_selection(self):
4243
self.viewer.figure.plotly_relayout({'selections': [], 'dragmode': False})
@@ -73,6 +74,52 @@ def _on_selection(self, _trace, _points, selector):
7374
viewer_state.y_max = ymax
7475

7576

77+
@viewer_tool
78+
class PlotlyHZoomMode(PlotlySelectionMode):
79+
80+
icon = 'glue_zoom_to_rect'
81+
tool_id = 'plotly:hzoom'
82+
action_text = 'Horizontal zoom'
83+
tool_tip = 'Horizontal zoom'
84+
85+
def __init__(self, viewer):
86+
super().__init__(viewer, 'select')
87+
88+
def activate(self):
89+
super().activate()
90+
self.viewer.figure.update_layout(selectdirection="h")
91+
92+
def _on_selection(self, _trace, _points, selector):
93+
xmin, xmax = selector.xrange
94+
viewer_state = self.viewer.state
95+
with self.viewer.figure.batch_update(), delay_callback(viewer_state, 'x_min', 'x_max'):
96+
viewer_state.x_min = xmin
97+
viewer_state.x_max = xmax
98+
99+
100+
@viewer_tool
101+
class PlotlyVZoomMode(PlotlySelectionMode):
102+
103+
icon = 'glue_zoom_to_rect'
104+
tool_id = 'plotly:vzoom'
105+
action_text = 'Vertical zoom'
106+
tool_tip = 'Vertical zoom'
107+
108+
def __init__(self, viewer):
109+
super().__init__(viewer, 'select')
110+
111+
def activate(self):
112+
super().activate()
113+
self.viewer.figure.update_layout(selectdirection="v")
114+
115+
def _on_selection(self, _trace, _points, selector):
116+
ymin, ymax = selector.yrange
117+
viewer_state = self.viewer.state
118+
with self.viewer.figure.batch_update(), delay_callback(viewer_state, 'y_min', 'y_max'):
119+
viewer_state.y_min = ymin
120+
viewer_state.y_max = ymax
121+
122+
76123
@viewer_tool
77124
class PlotlyPanMode(PlotlyDragMode):
78125

@@ -84,6 +131,16 @@ class PlotlyPanMode(PlotlyDragMode):
84131
def __init__(self, viewer):
85132
super().__init__(viewer, 'pan')
86133

134+
def activate(self):
135+
super().activate()
136+
self.viewer.figure.layout['xaxis']['fixedrange'] = False
137+
self.viewer.figure.layout['yaxis']['fixedrange'] = False
138+
139+
def deactivate(self):
140+
self.viewer.figure.layout['xaxis']['fixedrange'] = True
141+
self.viewer.figure.layout['yaxis']['fixedrange'] = True
142+
super().deactivate()
143+
87144

88145
@viewer_tool
89146
class PlotlyHRangeSelectionMode(PlotlySelectionMode):
@@ -180,3 +237,18 @@ class PlotlyHomeTool(Tool):
180237
def activate(self):
181238
with self.viewer.figure.batch_update():
182239
self.viewer.state.reset_limits()
240+
241+
242+
@viewer_tool
243+
class PlotlyHoverTool(CheckableTool):
244+
245+
icon = 'glue_point'
246+
tool_id = 'plotly:hover'
247+
action_text = 'Hover'
248+
tool_tip = 'Show hover info'
249+
250+
def activate(self):
251+
self.viewer.figure.update_layout(hovermode="closest")
252+
253+
def deactivate(self):
254+
self.viewer.figure.update_layout(hovermode=False)

glue_plotly/viewers/common/viewer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class PlotlyBaseView(IPyWidgetView):
2020

2121
LAYOUT_SETTINGS = dict(
2222
include_dimensions=False,
23+
hovermode=False, hoverdistance=1,
2324
dragmode=False, showlegend=False, grid=None,
2425
newselection=dict(line=dict(color=INTERACT_COLOR), mode='immediate'),
2526
modebar=dict(remove=['toimage', 'zoom', 'pan', 'lasso', 'zoomIn2d',
@@ -84,7 +85,6 @@ def _remove_traces(self, traces):
8485
self.figure.data = [t for t in self.figure.data if t not in traces]
8586

8687
def _clear_traces(self):
87-
print("In _clear_traces")
8888
self.figure.data = [self.selection_layer]
8989

9090
@property

glue_plotly/viewers/histogram/layer_artist.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def _update_data(self):
115115

116116
bars = traces_for_layer(self.view.state, self.state, add_data_label=True)
117117
for bar in bars:
118-
bar.update(unselected=dict(marker=dict(opacity=self.state.alpha)))
118+
bar.update(hoverinfo='all', unselected=dict(marker=dict(opacity=self.state.alpha)))
119119
self._bars_id = bars[0].meta if bars else None
120120
self.view.figure.add_traces(bars)
121121

glue_plotly/viewers/histogram/viewer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@viewer_registry("plotly_histogram")
1414
class PlotlyHistogramView(PlotlyBaseView):
1515

16-
tools = ['plotly:home', 'plotly:zoom', 'plotly:pan', 'plotly:xrange']
16+
tools = ['plotly:home', 'plotly:zoom', 'plotly:pan', 'plotly:xrange', 'plotly:hover']
1717

1818
allow_duplicate_data = False
1919
allow_duplicate_subset = False

glue_plotly/viewers/scatter/layer_artist.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def _create_scatter(self):
129129

130130
scatter_info = dict(mode=scatter_mode(self.state),
131131
name=name,
132-
hoverinfo='skip',
132+
hoverinfo='all',
133133
unselected=dict(marker=dict(opacity=self.state.alpha)),
134134
meta=self._scatter_id)
135135
if self._viewer_state.using_rectilinear:

glue_plotly/viewers/scatter/viewer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
class PlotlyScatterView(PlotlyBaseView):
1818

1919
tools = ['plotly:home', 'plotly:zoom', 'plotly:pan', 'plotly:xrange',
20-
'plotly:yrange', 'plotly:rectangle', 'plotly:lasso']
20+
'plotly:yrange', 'plotly:rectangle', 'plotly:lasso', 'plotly:hover']
2121

2222
allow_duplicate_data = False
2323
allow_duplicate_subset = False

0 commit comments

Comments
 (0)