Skip to content

Commit 858b7e1

Browse files
committed
update graph_widget.py
1 parent c4a38da commit 858b7e1

File tree

1 file changed

+101
-7
lines changed

1 file changed

+101
-7
lines changed

kivy_matplotlib_widget/uix/graph_widget.py

+101-7
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
from kivy.vector import Vector
1717
from matplotlib.backends.backend_agg import FigureCanvasAgg
1818
from matplotlib import cbook
19+
from matplotlib.colors import to_hex
1920
from weakref import WeakKeyDictionary
2021
from kivy.metrics import dp
2122
import numpy as np
23+
from kivy.utils import get_color_from_hex
24+
2225

2326
class MatplotFigure(Widget):
2427
"""Widget to show a matplotlib figure in kivy.
@@ -57,6 +60,7 @@ class MatplotFigure(Widget):
5760
fast_draw = BooleanProperty(True) #True will don't draw axis
5861
xsorted = BooleanProperty(False) #to manage x sorted data
5962
minzoom = NumericProperty(dp(40))
63+
hover_instance = ObjectProperty(None, allownone=True)
6064

6165
def on_figure(self, obj, value):
6266
self.figcanvas = _FigureCanvas(self.figure, self)
@@ -130,6 +134,10 @@ def __init__(self, **kwargs):
130134
self.anchor_x = None
131135
self.anchor_y = None
132136

137+
#manage hover data
138+
self.x_hover_data = None
139+
self.y_hover_data = None
140+
133141
#manage back and next event
134142
self._nav_stack = cbook.Stack()
135143
self.set_history_buttons()
@@ -245,20 +253,53 @@ def hover(self, event) -> None:
245253
line=good_line[idx_best]
246254
self.x_cursor, self.y_cursor = line.get_data()
247255
x = self.x_cursor[good_index[idx_best]]
248-
y = self.y_cursor[good_index[idx_best]]
249-
self.set_cross_hair_visible(True)
256+
y = self.y_cursor[good_index[idx_best]]
257+
258+
if not self.hover_instance:
259+
self.set_cross_hair_visible(True)
250260

251261
# update the cursor x,y data
252262
ax=line.axes
253263
self.horizontal_line.set_ydata(y)
254264
self.vertical_line.set_xdata(x)
255265

256266
#x y label
257-
if self.cursor_xaxis_formatter:
258-
x = self.cursor_xaxis_formatter.format_data(x)
259-
if self.cursor_yaxis_formatter:
260-
y = self.cursor_yaxis_formatter.format_data(y)
261-
self.text.set_text(f"x={x}, y={y}")
267+
if self.hover_instance:
268+
xy_pos = ax.transData.transform([(x,y)])
269+
self.x_hover_data = x
270+
self.y_hover_data = y
271+
self.hover_instance.x_hover_pos=float(xy_pos[0][0]) + self.x
272+
self.hover_instance.y_hover_pos=float(xy_pos[0][1]) + self.y
273+
self.hover_instance.show_cursor=True
274+
275+
if self.cursor_xaxis_formatter:
276+
x = self.cursor_xaxis_formatter.format_data(x)
277+
if self.cursor_yaxis_formatter:
278+
y = self.cursor_yaxis_formatter.format_data(y)
279+
self.hover_instance.label_x_value=f"{x}"
280+
self.hover_instance.label_y_value=f"{y}"
281+
282+
self.hover_instance.ymin_line = float(ax.bbox.bounds[1]) + self.y
283+
self.hover_instance.ymax_line = float(ax.bbox.bounds[1] + ax.bbox.bounds[3]) + self.y
284+
285+
self.hover_instance.custom_label = line.get_label()
286+
self.hover_instance.custom_color = get_color_from_hex(to_hex(line.get_color()))
287+
288+
if self.hover_instance.x_hover_pos>self.x+self.axes.bbox.bounds[2] + self.axes.bbox.bounds[0] or \
289+
self.hover_instance.x_hover_pos<self.x+self.axes.bbox.bounds[0] or \
290+
self.hover_instance.y_hover_pos>self.y+self.axes.bbox.bounds[1] + self.axes.bbox.bounds[3] or \
291+
self.hover_instance.y_hover_pos<self.y+self.axes.bbox.bounds[1]:
292+
self.hover_instance.hover_outside_bound=True
293+
else:
294+
self.hover_instance.hover_outside_bound=False
295+
296+
return
297+
else:
298+
if self.cursor_xaxis_formatter:
299+
x = self.cursor_xaxis_formatter.format_data(x)
300+
if self.cursor_yaxis_formatter:
301+
y = self.cursor_yaxis_formatter.format_data(y)
302+
self.text.set_text(f"x={x}, y={y}")
262303

263304
#blit method (always use because same visual effect as draw)
264305
if self.background is None:
@@ -281,6 +322,12 @@ def hover(self, event) -> None:
281322
#if touch is too far, hide cross hair cursor
282323
else:
283324
self.set_cross_hair_visible(False)
325+
if self.hover_instance:
326+
self.hover_instance.x_hover_pos=self.x
327+
self.hover_instance.y_hover_pos=self.y
328+
self.hover_instance.show_cursor=False
329+
self.x_hover_data = None
330+
self.y_hover_data = None
284331

285332
def home(self) -> None:
286333
""" reset data axis
@@ -411,6 +458,27 @@ def _draw_bitmap(self):
411458
self._img_texture.blit_buffer(
412459
bytes(self._bitmap), colorfmt="rgba", bufferfmt='ubyte')
413460
self._img_texture.flip_vertical()
461+
462+
if self.hover_instance:
463+
#update hover pos if needed
464+
if self.hover_instance.show_cursor and self.x_hover_data and self.y_hover_data:
465+
xy_pos = self.axes.transData.transform([(self.x_hover_data,self.y_hover_data)])
466+
self.hover_instance.x_hover_pos=float(xy_pos[0][0]) + self.x
467+
self.hover_instance.y_hover_pos=float(xy_pos[0][1]) + self.y
468+
469+
# ymin,ymax=self.axes.get_ylim()
470+
# ylim_pos = self.axes.transData.transform([(ymin,ymax)])
471+
self.hover_instance.ymin_line = float(self.axes.bbox.bounds[1]) + self.y
472+
self.hover_instance.ymax_line = float(self.axes.bbox.bounds[1] + self.axes.bbox.bounds[3] )+ self.y
473+
474+
if self.hover_instance.x_hover_pos>self.x+self.axes.bbox.bounds[2] + self.axes.bbox.bounds[0] or \
475+
self.hover_instance.x_hover_pos<self.x+self.axes.bbox.bounds[0] or \
476+
self.hover_instance.y_hover_pos>self.y+self.axes.bbox.bounds[1] + self.axes.bbox.bounds[3] or \
477+
self.hover_instance.y_hover_pos<self.y+self.axes.bbox.bounds[1]:
478+
self.hover_instance.hover_outside_bound=True
479+
else:
480+
self.hover_instance.hover_outside_bound=False
481+
414482

415483
def transform_with_touch(self, event):
416484
""" manage touch behaviour. based on kivy scatter method"""
@@ -489,6 +557,26 @@ def transform_with_touch(self, event):
489557
changed = True
490558
return changed
491559

560+
def on_motion(self,*args):
561+
'''Kivy Event to trigger mouse event on motion
562+
`enter_notify_event`.
563+
'''
564+
pos = args[1]
565+
newcoord = self.to_widget(pos[0], pos[1])
566+
x = newcoord[0]
567+
y = newcoord[1]
568+
inside = self.collide_point(x,y)
569+
if inside:
570+
571+
# will receive all motion events.
572+
if self.figcanvas and self.hover_instance:
573+
#avoid in motion if touch is detected
574+
if not len(self._touches)==0:
575+
return
576+
FakeEvent.x=x
577+
FakeEvent.y=y
578+
self.hover(FakeEvent)
579+
492580
def on_touch_down(self, event):
493581
""" Manage Mouse/touch press """
494582
x, y = event.x, event.y
@@ -819,6 +907,8 @@ def _onSize(self, o, size):
819907
self.figcanvas.draw()
820908
if self.legend_instance:
821909
self.legend_instance.update_size()
910+
if self.hover_instance:
911+
self.hover_instance.figwidth = self.width
822912

823913
def update_lim(self):
824914
""" update axis lim if zoombox is used"""
@@ -984,6 +1074,10 @@ def blit(self, bbox=None):
9841074
self.widget.bt_h = h
9851075
self.widget._draw_bitmap()
9861076

1077+
class FakeEvent:
1078+
x:None
1079+
y:None
1080+
9871081
from kivy.factory import Factory
9881082

9891083
Factory.register('MatplotFigure', MatplotFigure)

0 commit comments

Comments
 (0)