- Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathdraggable_plot.py
More file actions
Latest commit
117 lines (96 loc) · 3.56 KB
/
Copy pathdraggable_plot.py
File metadata and controls
117 lines (96 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# -*- coding: utf-8 -*-
importmath
importmatplotlib.pyplotasplt
frommatplotlib.backend_basesimportMouseEvent
classDraggablePlotExample(object):
u""" An example of plot with draggable markers """
def__init__(self):
self._figure, self._axes, self._line=None, None, None
self._dragging_point=None
self._points= {}
self._init_plot()
def_init_plot(self):
self._figure=plt.figure("Example plot")
axes=plt.subplot(1, 1, 1)
axes.set_xlim(0, 100)
axes.set_ylim(0, 100)
axes.grid(which="both")
self._axes=axes
self._figure.canvas.mpl_connect('button_press_event', self._on_click)
self._figure.canvas.mpl_connect('button_release_event', self._on_release)
self._figure.canvas.mpl_connect('motion_notify_event', self._on_motion)
plt.show()
def_update_plot(self):
ifnotself._points:
self._line.set_data([], [])
else:
x, y=zip(*sorted(self._points.items()))
# Add new plot
ifnotself._line:
self._line, =self._axes.plot(x, y, "b", marker="o", markersize=10)
# Update current plot
else:
self._line.set_data(x, y)
self._figure.canvas.draw()
def_add_point(self, x, y=None):
ifisinstance(x, MouseEvent):
x, y=int(x.xdata), int(x.ydata)
self._points[x] =y
returnx, y
def_remove_point(self, x, _):
ifxinself._points:
self._points.pop(x)
def_find_neighbor_point(self, event):
u""" Find point around mouse position
:rtype: ((int, int)|None)
:return: (x, y) if there are any point around mouse else None
"""
distance_threshold=3.0
nearest_point=None
min_distance=math.sqrt(2* (100**2))
forx, yinself._points.items():
distance=math.hypot(event.xdata-x, event.ydata-y)
ifdistance<min_distance:
min_distance=distance
nearest_point= (x, y)
ifmin_distance<distance_threshold:
returnnearest_point
returnNone
def_on_click(self, event):
u""" callback method for mouse click event
:type event: MouseEvent
"""
# left click
ifevent.button==1andevent.inaxesin [self._axes]:
point=self._find_neighbor_point(event)
ifpoint:
self._dragging_point=point
else:
self._add_point(event)
self._update_plot()
# right click
elifevent.button==3andevent.inaxesin [self._axes]:
point=self._find_neighbor_point(event)
ifpoint:
self._remove_point(*point)
self._update_plot()
def_on_release(self, event):
u""" callback method for mouse release event
:type event: MouseEvent
"""
ifevent.button==1andevent.inaxesin [self._axes] andself._dragging_point:
self._dragging_point=None
self._update_plot()
def_on_motion(self, event):
u""" callback method for mouse motion event
:type event: MouseEvent
"""
ifnotself._dragging_point:
return
ifevent.xdataisNoneorevent.ydataisNone:
return
self._remove_point(*self._dragging_point)
self._dragging_point=self._add_point(event)
self._update_plot()
if__name__=="__main__":
plot=DraggablePlotExample()