我正在尝试使用Multicursor为两个轴实现matplotlib十字准线 。我想要一个新功能,该功能只需为指针所在的轴绘制水平光标线,而不为其他任何
示例代码:
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.widgets import MultiCursor
from PyQt5.QtWidgets import QMainWindow,QVBoxLayout
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow_code_serarch(object):
def setup_code_serarch(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(870, 680)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(17, 50, 741, 553))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.figure = plt.figure(facecolor='black')
self.canvas = FigureCanvas(self.figure)
self.verticalLayout.addWidget(self.canvas)
axes, axes2 = self.figure.subplots(nrows=2, sharex=True)
axes.plot([1, 2, 3, 4,5,6,7,8])
axes2.plot([1, 2, 3, 4,7,8,9])
axes.set_position([0.02, 0.37, 0.88, 0.6])
axes2.set_position([0.02, 0.15, 0.88, 0.22])
axes.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
axes.yaxis.tick_right()
axes2.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
axes2.grid(color='lightgray', linewidth=.5, linestyle=':')
axes.grid(color='lightgray', linewidth=.5, linestyle=':')
axes2.yaxis.tick_right()
axes.autoscale_view()
axes2.autoscale_view()
axes.margins(0, .5)
axes2.margins(0, .5)
axes.set_facecolor('#041105')
axes2.set_facecolor('#041105')
self.multi = MultiCursor(self.canvas, (axes, axes2), color='r', lw=1,horizOn=True, vertOn=True)
self.canvas.draw()
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 246, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
# self.pushButton.clicked.connect(self.graphShowCode)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
# self.pushButton.setText(_translate("MainWindow", "OK"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = MainWindow_code_serarch()
ui.setup_code_serarch(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
示例输出如下: How to wait until remote .NET debugger attached
还有其他可以遵循的过程,请提出建议。
注意:我在这里使用了python pyqt5和matplotlib库
答案 0 :(得分:1)
编辑:基于以下评论,我认为您正在寻找“图形级”光标,无论您将鼠标悬停在哪个轴上,该光标都将跟随鼠标。我基于the code for MultiCursor
创建了一个新类,该类应该按照您的想法进行。
class FigureCursor(Widget):
def __init__(self, fig, horizOn=True, vertOn=True, useblit=True, **lineprops):
self._cidmotion = None
self._ciddraw = None
self.background = None
self.needclear = False
self.visible = True
self.canvas = fig.canvas
self.fig = fig
self.horizOn = horizOn
self.vertOn = vertOn
self.useblit = useblit
self.vline, = fig.axes[0].plot([.5, .5], [0., 1.], visible=vertOn, transform=self.fig.transFigure,
clip_on = False, **lineprops)
self.hline, = fig.axes[0].plot([0., 1.], [.5, .5], visible=horizOn, transform=self.fig.transFigure,
clip_on=False, **lineprops)
self.connect()
def connect(self):
"""connect events"""
self._cidmotion = self.canvas.mpl_connect('motion_notify_event', self.onmove)
self._ciddraw = self.canvas.mpl_connect('draw_event', self.clear)
def disconnect(self):
"""disconnect events"""
self.canvas.mpl_disconnect(self._cidmotion)
self.canvas.mpl_disconnect(self._ciddraw)
def clear(self, event):
"""clear the cursor"""
if self.ignore(event):
return
if self.useblit:
self.background = (
self.canvas.copy_from_bbox(self.canvas.figure.bbox))
for line in [self.vline, self.hline]:
line.set_visible(False)
def onmove(self, event):
if self.ignore(event):
return
if event.inaxes is None:
return
if not self.canvas.widgetlock.available(self):
return
self.needclear = True
if not self.visible:
return
trans = event.inaxes.transData + self.fig.transFigure.inverted()
x_fig, y_fig = trans.transform([event.xdata, event.ydata])
if self.vertOn:
self.vline.set_xdata([x_fig, x_fig])
self.vline.set_visible(self.visible)
if self.horizOn:
self.hline.set_ydata([y_fig, y_fig])
self.hline.set_visible(self.visible)
self._update()
def _update(self):
if self.useblit:
if self.background is not None:
self.canvas.restore_region(self.background)
if self.vertOn:
self.fig.draw_artist(self.vline)
if self.horizOn:
self.fig.draw_artist(self.hline)
self.canvas.blit(self.canvas.figure.bbox)
else:
self.canvas.draw_idle()
使用您自己的代码:
(...)
self.multi = FigureCursor(self.figure, horizOn=True, vertOn=True, color='r', lw=1)
(...)
编辑:下面的部分是我先前尝试回答问题的尝试
我写了一个继承自MultiCursor
的类,它不是接受True/False
和horizOn=
的{{1}}而是列出了要在其中绘制轴的轴的列表。水平或垂直线。
当鼠标从一个斧头移到另一个斧头时,我发现vertOn=
的行为有些奇怪,但是我没有改变这种行为。如果要进一步修改该类,此代码应使您入门。特别是,您可以覆盖the onmove()
function。
MutiCursor
并在您的课程中,创建一个实例:
class MyMultiCursor(MultiCursor):
def __init__(self, canvas, axes, useblit=True, horizOn=[], vertOn=[], **lineprops):
super(MyMultiCursor, self).__init__(canvas, axes, useblit=useblit, horizOn=False, vertOn=False, **lineprops)
self.horizAxes = horizOn
self.vertAxes = vertOn
if len(horizOn) > 0:
self.horizOn = True
if len(vertOn) > 0:
self.vertOn = True
xmin, xmax = axes[-1].get_xlim()
ymin, ymax = axes[-1].get_ylim()
xmid = 0.5 * (xmin + xmax)
ymid = 0.5 * (ymin + ymax)
self.vlines = [ax.axvline(xmid, visible=True, **lineprops) for ax in self.vertAxes]
self.hlines = [ax.axhline(ymid, visible=True, **lineprops) for ax in self.horizAxes]
答案 1 :(得分:1)
由于我每次尝试绘制新轨迹时都会编辑一些代码,因此我会显示以前的光标显示。因此,我隐藏了默认的hline和vline更改坐标值。
from matplotlib.widgets import MultiCursor
from matplotlib.widgets import *
from matplotlib.figure import Figure
class FigureCursor(Widget):
def __init__(self, fig, horizOn=True, vertOn=True, useblit=True, **lineprops):
self._cidmotion = None
self._ciddraw = None
self.background = None
self.needclear = False
self.visible = True
self.canvas = fig.canvas
self.fig = fig
self.horizOn = horizOn
self.vertOn = vertOn
self.useblit = useblit
self.vline, = fig.axes[0].plot([1, 1], [0., 1.], visible=vertOn, transform=self.fig.transFigure,
clip_on = False, **lineprops)
self.hline, = fig.axes[0].plot([0., 1.], [-1., 0.], visible=horizOn, transform=self.fig.transFigure,
clip_on=False, **lineprops)
self.connect()
def connect(self):
"""connect events"""
self._cidmotion = self.canvas.mpl_connect('motion_notify_event', self.onmove)
self._ciddraw = self.canvas.mpl_connect('draw_event', self.clear)
def disconnect(self):
"""disconnect events"""
self.canvas.mpl_disconnect(self._cidmotion)
self.canvas.mpl_disconnect(self._ciddraw)
def clear(self, event):
"""clear the cursor"""
if self.ignore(event):
return
if self.useblit:
self.background = (
self.canvas.copy_from_bbox(self.canvas.figure.bbox))
for line in [self.vline, self.hline]:
line.set_visible(False)
def onmove(self, event):
if self.ignore(event):
return
if event.inaxes is None:
return
if not self.canvas.widgetlock.available(self):
return
self.needclear = True
if not self.visible:
return
trans = event.inaxes.transData + self.fig.transFigure.inverted()
x_fig, y_fig = trans.transform([event.xdata, event.ydata])
if self.vertOn:
self.vline.set_xdata([x_fig, x_fig])
self.vline.set_visible(self.visible)
if self.horizOn:
self.hline.set_ydata([y_fig, y_fig])
self.hline.set_visible(self.visible)
self._update()
def _update(self):
if self.useblit:
if self.background is not None:
self.canvas.restore_region(self.background)
if self.vertOn:
self.fig.draw_artist(self.vline)
if self.horizOn:
self.fig.draw_artist(self.hline)
self.canvas.blit(self.canvas.figure.bbox)
else:
self.canvas.draw_idle()