我将示例复制到Include matplotlib in pyqt5 with hover labels并放置了@ImportanceOfBeingErnest建议的修改。我有处理通知的示例应用程序。完整的代码位于bottom。
但是,我现在想通过按钮或菜单项修改图的内容。为简单起见,我创建了一个菜单并添加了动作并定义了函数updatePlot。我还尝试了另一个函数updatePlot2,该函数再次调用经过修改的get_canvas函数。
def updatePlot(self):
print('executing update code')
self.canvas.figure.axes.clear()
x = np.linspace(0.1, 2*pi, 20)
y = 3*cos(x)
self.canvas.figure.axes.plot(x, y, color='blue', marker='o', linestyle='dashed',linewidth=2, markersize=8)
def updatePlot2(self):
print('executing update code')
self.canvas.figure.axes.clear()
x = np.linspace(0.1, 2*pi, 20)
y = 3*cos(x)
self.canvas = self.get_canvas2(2)
def get_canvas2(self,n):
fig, ax = plt.subplots()
ax.tick_params(labeltop=False, labelright=True)
x = np.linspace(0.1, 2*pi, 20)
y = n*cos(x)
ax.plot(x, y, color='blue', marker='o', linestyle='dashed',linewidth=2, markersize=8)
canvas = FigureCanvas(fig)
self.cursor = FollowDotCursor(ax, x, y, tolerance=20)
return canvas
无论我做什么,我都无法使其正常工作。它给我一些错误,例如“列表对象没有属性图”或没有axis属性。如果我使用updatePlot2,则会在控制台(spyder)中获得正确的输出,但在UI中没有更新。
谢谢您的帮助。这是完整的代码:
import matplotlib.pyplot as plt import scipy.spatial as spatial import numpy as np from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget) from PyQt5.QtCore import Qt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as NavigationToolbar) import sys
pi = np.pi cos = np.cos
def fmt(x, y):
return 'Date: {x:0.2f}\nValue: {y:0.2f}\nDuration: {x:0.2f}'.format(x=x, y=y)
class FollowDotCursor(object):
"""Display the x,y location of the nearest data point.
https://stackoverflow.com/a/4674445/190597 (Joe Kington)
https://stackoverflow.com/a/13306887/190597 (unutbu)
https://stackoverflow.com/a/15454427/190597 (unutbu)
"""
def __init__(self, ax, x, y, tolerance=5, formatter=fmt, offsets=(-20, 20)):
try:
x = np.asarray(x, dtype='float')
except (TypeError, ValueError):
x = np.asarray(mdates.date2num(x), dtype='float')
y = np.asarray(y, dtype='float')
mask = ~(np.isnan(x) | np.isnan(y))
x = x[mask]
y = y[mask]
self._points = np.column_stack((x, y))
self.offsets = offsets
y = y[np.abs(y-y.mean()) <= 3*y.std()]
self.scale = x.ptp()
self.scale = y.ptp() / self.scale if self.scale else 1
self.tree = spatial.cKDTree(self.scaled(self._points))
self.formatter = formatter
self.tolerance = tolerance
self.ax = ax
self.fig = ax.figure
self.ax.xaxis.set_label_position('top')
self.dot = ax.scatter(
[x.min()], [y.min()], s=180, color='green', alpha=0.7)
self.annotation = self.setup_annotation()
self.cid = self.fig.canvas.mpl_connect('motion_notify_event', self)
def scaled(self, points):
points = np.asarray(points)
return points * (self.scale, 1)
def __call__(self, event):
ax = self.ax
# event.inaxes is always the current axis. If you use twinx, ax could be
# a different axis.
if event.inaxes == ax:
x, y = event.xdata, event.ydata
elif event.inaxes is None:
return
else:
inv = ax.transData.inverted()
x, y = inv.transform([(event.x, event.y)]).ravel()
annotation = self.annotation
x, y = self.snap(x, y)
annotation.xy = x, y
annotation.set_text(self.formatter(x, y))
self.dot.set_offsets((x, y))
bbox = ax.viewLim
event.canvas.draw()
def setup_annotation(self):
"""Draw and hide the annotation box."""
annotation = self.ax.annotate('', xy=(0, 0), ha = 'right', xytext = self.offsets, textcoords = 'offset points', va = 'bottom', bbox = dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.75),arrowprops = dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
return annotation
def snap(self, x, y):
"""Return the value in self.tree closest to x, y."""
dist, idx = self.tree.query(self.scaled((x, y)), k=1, p=1)
try:
return self._points[idx]
except IndexError:
# IndexError: index out of bounds
return self._points[0]
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'Testing'
self.setWindowTitle(self.title)
self.width = 1000
self.height = 800
self.setGeometry(10, 30, self.width, self.height)
self.statusBar().showMessage('Ready')
# Create menu and items
mainMenu = self.menuBar()
mainMenu.setNativeMenuBar(False)
fileMenu = mainMenu.addMenu('Sources')
# Update Plot
btnUpdate = QAction(QIcon(''), 'Update Chart', self)
btnUpdate.setShortcut('Ctrl+U')
btnUpdate.setStatusTip('Clic to update chart.')
btnUpdate.triggered.connect(self.updatePlot)
fileMenu.addAction(btnUpdate)
fileMenu.addSeparator()
exitButton = QAction(QIcon('exit24.png'), 'Exit', self)
exitButton.setShortcut('Ctrl+Q')
exitButton.setStatusTip('Exit application')
exitButton.triggered.connect(self.close)
fileMenu.addAction(exitButton)
self.canvas = self.get_canvas()
self.toolbar = NavigationToolbar(self.canvas, self)
w = QWidget()
w.layout = QVBoxLayout()
w.layout.addWidget(self.canvas)
w.layout.addWidget(self.toolbar)
w.setLayout(w.layout)
self.setCentralWidget(w)
# show maximize
self.showMaximized()
def updatePlot(self):
print('executing update code')
self.canvas.figure.axes.clear()
x = np.linspace(0.1, 2*pi, 20)
y = 3*cos(x)
self.canvas.figure.axes.plot(x, y, color='blue', marker='o', linestyle='dashed',linewidth=2, markersize=8)
def updatePlot2(self):
print('executing update code')
self.canvas.figure.axes.clear()
x = np.linspace(0.1, 2*pi, 20)
y = 3*cos(x)
self.canvas = self.get_canvas2(2)
def get_canvas2(self,n):
fig, ax = plt.subplots()
ax.tick_params(labeltop=False, labelright=True)
x = np.linspace(0.1, 2*pi, 20)
y = n*cos(x)
ax.plot(x, y, color='blue', marker='o', linestyle='dashed',linewidth=2, markersize=8)
canvas = FigureCanvas(fig)
self.cursor = FollowDotCursor(ax, x, y, tolerance=20)
return canvas
def get_canvas(self):
fig, ax = plt.subplots()
ax.tick_params(labeltop=False, labelright=True)
x = np.linspace(0.1, 2*pi, 20)
y = cos(x)
ax.plot(x, y, color='blue', marker='o', linestyle='dashed',linewidth=2, markersize=8)
canvas = FigureCanvas(fig)
self.cursor = FollowDotCursor(ax, x, y, tolerance=20)
return canvas
app = QApplication(sys.argv) win = MainWindow() sys.exit(app.exec_())
如何显示错误:
AttributeError: 'list' object has no attribute 'plot'
轴是一个包含多个AxesSubplot
的列表,因为使用plt.subplots()
可以生成多个轴,因此在您的情况下,解决方案是使用第一个(并且仅存在一个):
self.canvas.figure.axes[0].plot(...)