Python 如何使用双击事件(例如添加标记、取消以前的操作)与matplotlib绘图交互?
我正在学习如何使用单击事件在matplotlib中触发特定操作 在我的可复制示例中,我希望:Python 如何使用双击事件(例如添加标记、取消以前的操作)与matplotlib绘图交互?,python,matplotlib,pyqt,pyside2,Python,Matplotlib,Pyqt,Pyside2,我正在学习如何使用单击事件在matplotlib中触发特定操作 在我的可复制示例中,我希望: task1:单击/拖动标记时,将其拖动到不同的位置[此操作已完成] 任务2:用鼠标左键双击绘图时,在绘图中添加标记 任务3:用鼠标右键双击时取消上一次单击操作(添加标记或拖动标记) 任务1 这些帖子对实现可拖动的数据非常有用,我认为在我的示例中效果很好 任务2 我认为在下面的代码中,我应该接近实现“添加标记”事件,但是当我更新艺术家的数据时,有些地方不正确,因为新标记没有出现在绘图上 任务3 我不知
- task1:单击/拖动标记时,将其拖动到不同的位置[此操作已完成]
- 任务2:用鼠标左键双击绘图时,在绘图中添加标记
- 任务3:用鼠标右键双击时取消上一次单击操作(添加标记或拖动标记)
event.x
,event.y
而不是event.xdata
,event.ydata
(它自动获取坐标,而不是将像素转换为坐标)。我将新点附加到旧标记的坐标上,并更新了绘图
任务3
我创建了一个新的实例变量self.memory
,其中包含标记的坐标<使用self.save_to_memory()
函数拖动标记或添加新标记之前,code>self.memory会进行更新。self.undo()
函数使用self.memory撤消上次更改。
我还用一个elif替换了行if event.button==1:
,因为双击时,它访问两个if语句
import sys
from PySide2.QtWidgets import QApplication
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt4agg import FigureManagerQT
import numpy as np
class MyFigureCanvas(FigureCanvasQTAgg):
def __init__(self):
super(MyFigureCanvas, self).__init__(Figure())
# init class attributes:
self.background = None
self.draggable = None
self.msize = 6
# plot some data:
x = np.random.rand(25)
self.ax = self.figure.add_subplot(111)
(self.markers,) = self.ax.plot(x, marker="o", ms=self.msize)
self.memory = self.markers.get_xydata()
# define event connections:
self.mpl_connect("motion_notify_event", self.on_motion)
self.mpl_connect("button_press_event", self.on_click)
self.mpl_connect("button_release_event", self.on_release)
def save_to_memory(self):
self.memory = self.markers.get_xydata()
def undo(self):
self.markers.set_xdata(self.memory[:, 0])
self.markers.set_ydata(self.memory[:, 1])
self.draw()
self.ax.draw_artist(self.markers)
self.update()
def on_click(self, event):
if event.dblclick:
if event.button == 1: # add a marker on the line plotted
self.save_to_memory()
# get mouse cursor coordinates in pixels:
x, y = event.xdata, event.ydata
# update the data of the artist:
old_xy = self.markers.get_xydata()
old_xy = np.vstack((old_xy, np.array([[x, y]])))
self.markers.set_xdata(old_xy[:, 0])
self.markers.set_ydata(old_xy[:, 1])
self.ax.draw_artist(self.markers)
self.update()
print(f"{event.button} - coords: x: {x} / y: {y} ")
elif event.button == 3: # cancel previous action
print(f"Double clicked event - {str(event.button)}")
self.undo()
elif event.button == 1: # 2 is for middle mouse button
# get mouse cursor coordinates in pixels:
x = event.x
y = event.y
# print(f"{event.button} - coords: x: {x} / y: {y} ")
# get markers xy coordinate in pixels:
xydata = self.ax.transData.transform(self.markers.get_xydata())
xdata, ydata = xydata.T
# compute the linear distance between the markers and the cursor:
r = ((xdata - x) ** 2 + (ydata - y) ** 2) ** 0.5
if np.min(r) < self.msize:
self.save_to_memory()
# save figure background:
self.markers.set_visible(False)
self.draw()
self.background = self.copy_from_bbox(self.ax.bbox)
self.markers.set_visible(True)
self.ax.draw_artist(self.markers)
self.update()
# store index of draggable marker:
self.draggable = np.argmin(r)
else:
self.draggable = None
def on_motion(self, event):
if self.draggable is not None:
if event.xdata and event.ydata:
# get markers coordinate in data units:
xdata, ydata = self.markers.get_data()
# change the coordinate of the marker that is
# being dragged to the ones of the mouse cursor:
xdata[self.draggable] = event.xdata
ydata[self.draggable] = event.ydata
# update the data of the artist:
self.markers.set_xdata(xdata)
self.markers.set_ydata(ydata)
# update the plot:
self.restore_region(self.background)
self.ax.draw_artist(self.markers)
self.update()
def on_release(self, event):
self.draggable = None
if __name__ == "__main__":
app = QApplication(sys.argv)
canvas = MyFigureCanvas()
manager = FigureManagerQT(canvas, 1)
manager.show()
sys.exit(app.exec_())
非常感谢你的回答!出于好奇,您将如何编辑您的答案,以便能够
class MyFigureCanvas(FigureCanvasQTAgg):
def __init__(self):
super(MyFigureCanvas, self).__init__(Figure())
# init class attributes:
self.background = None
self.draggable = None
self.msize = 6
# plot some data:
x = np.random.rand(25)
self.ax = self.figure.add_subplot(111)
(self.markers,) = self.ax.plot(x, marker="o", ms=self.msize)
self.history = []
self.history_depth = 10
# define event connections:
self.mpl_connect("motion_notify_event", self.on_motion)
self.mpl_connect("button_press_event", self.on_click)
self.mpl_connect("button_release_event", self.on_release)
def append_to_history(self, type, index, x=None, y=None):
if index < 0:
index += self.markers.get_xydata().shape[0]
if type == "move":
self.history.append({"type": "move",
"index": index,
"x": x,
"y": y})
elif type == "append":
self.history.append({"type": "append",
"index": index + 1})
if len(self.history) > self.history_depth:
del self.history[0]
def undo(self):
if len(self.history) > 0:
last_move = self.history[-1]
if last_move["type"] == "move":
self.move_marker(last_move["index"], last_move["x"], last_move["y"])
elif last_move["type"] == "append":
self.remove_marker(last_move["index"])
del self.history[-1]
def update_markers(self, x_data, y_data):
self.markers.set_xdata(x_data)
self.markers.set_ydata(y_data)
self.draw()
self.ax.draw_artist(self.markers)
self.update()
def move_marker(self, i, x, y):
xdata, ydata = self.markers.get_data()
xdata[i], ydata[i] = x, y
self.update_markers(xdata, ydata)
def append_marker(self, x, y):
new_xy = np.vstack((self.markers.get_xydata(), np.array([[x, y]])))
self.update_markers(new_xy[:, 0], new_xy[:, 1])
def remove_marker(self, i):
new_xy = np.delete(self.markers.get_xydata(), i, axis=0)
self.update_markers(new_xy[:, 0], new_xy[:, 1])
def on_click(self, event):
if event.dblclick:
if event.button == 1:
self.append_to_history("append", -1)
self.append_marker(event.xdata, event.ydata)
elif event.button == 3:
self.undo()
# Single Click
elif event.button == 1:
x, y = event.x, event.y
xydata = self.ax.transData.transform(self.markers.get_xydata())
xdata, ydata = xydata.T
r = ((xdata - x) ** 2 + (ydata - y) ** 2) ** 0.5
if np.min(r) < self.msize:
index = np.where(r == r.min())[0][0]
x_i, y_i = self.markers.get_xydata()[index]
self.append_to_history("move", index, x_i, y_i)
self.markers.set_visible(False)
self.draw()
self.background = self.copy_from_bbox(self.ax.bbox)
self.markers.set_visible(True)
self.ax.draw_artist(self.markers)
self.update()
self.draggable = np.argmin(r)
else:
self.draggable = None
def on_motion(self, event):
if self.draggable is not None:
if event.xdata and event.ydata:
self.move_marker(self.draggable, event.xdata, event.ydata)
def on_release(self, event):
self.draggable = None