[英]How to make a matplotlib plot interactive in pyqt5
我目前正在做一个项目,我想将 matplotlib plot 嵌入到 pyqt5 GUI 中。 plot 是交互式的,允许绘制阴影矩形。
问题是当 plot 嵌入到 pyqt window 中时,它是不可交互的。 当我运行程序时,下面代码中的第 147 行(mplWidget 类中的 plt.show())显示 matplotlib plot,然后我可以绘制一个矩形,如下所示:
但是,当这个 window 关闭并且 plot 嵌入到 pyqt Z05B8C74CBD902FBF2DE4C1 中时,它变得不可编辑
我想要 GUI plot 到 function 就像 matplotlib 图一样。
我知道这与我必须通过connect()
语句提供 pyqt 功能这一事实有关,但我不知道那些 go 的位置/它们如何适合该程序。
我不知道如何连接到 matplotlib。 我是否只对 mplWidget class 函数使用connect()
语句?
任何帮助表示赞赏!
(我意识到我需要取出第 147 行(plt.show()),以便图形框架不会在 gui 之前弹出,但我只是暂时显示 mpl class 仍然按预期工作,并且问题是它在嵌入时变为“静态”)
import numpy as np
import matplotlib.pyplot as plt
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar)
class topLevelWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Add a central widget to the main window and lay it out in a grid
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_5 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_5.setObjectName("gridLayout_5")
# Create mainTabs object as well as the displayFrame and displaySettingsFrame that go in the central widget
# Display Frame and Display settings frames
self.displayFrame = QtWidgets.QFrame(self.centralwidget)
self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.displayFrame)
self.verticalLayout_22.setObjectName("verticalLayout_22")
self.gridLayout_5.addWidget(self.displayFrame, 1, 0, 1, 1)
self.devConstructor = mplWidget()
self.dynamic_canvas = FigureCanvasQTAgg(self.devConstructor.fig)
self.verticalLayout_22.addWidget(self.dynamic_canvas)
self._dynamic_ax = self.devConstructor.ax
self.setCentralWidget(self.centralwidget)
# Perform final windows setup (set buddies, translate, tab order, initial tabs, etc)
_translate = QtCore.QCoreApplication.translate
self.setWindowTitle(_translate("MainWindow", "MainWindow")) # Was self.setWindowTitle
QtCore.QMetaObject.connectSlotsByName(self)
self.show()
class mplWidget(QtWidgets.QWidget): # n.b. changed this from Object to QWidget and added a super()
def setSnapBase(self, base):
return lambda value: int(base*round(float(value)/base))
def onclick(self, event):
if self.plotSnap is False:
self.bottomLeftX = event.xdata
self.bottomLeftY = event.ydata
else:
self.calculateSnapCoordinates = self.setSnapBase(self.plotSnap)
self.bottomLeftX = self.calculateSnapCoordinates(event.xdata)
self.bottomLeftY = self.calculateSnapCoordinates(event.ydata)
try:
self.aspan.remove()
except:
pass
self.moving = True
def onrelease(self, event):
if self.plotSnap is False:
self.topRightX = event.xdata
self.topRightY = event.ydata
else:
try:
calculateSnapCoordinates = self.setSnapBase(self.plotSnap)
self.topRightX = calculateSnapCoordinates(event.xdata)
self.topRightY = calculateSnapCoordinates(event.ydata)
except:
pass
self.x = np.array([self.bottomLeftX, self.bottomLeftX, self.topRightX, self.topRightX, self.bottomLeftX])
self.y = np.array([self.bottomLeftY, self.topRightY, self.topRightY, self.bottomLeftY, self.bottomLeftY])
self.myPlot.set_xdata(self.x)
self.myPlot.set_ydata(self.y)
# ax.fill_between(x, y, color=defaultColors[0, :], alpha=.25)
ylimDiff = self.ax.get_ylim()[1] - self.ax.get_ylim()[0]
self.aspan = self.ax.axvspan(self.bottomLeftX, self.topRightX,
(self.bottomLeftY-self.ax.get_ylim()[0])/ylimDiff,
(self.topRightY-self.ax.get_ylim()[0])/ylimDiff,
color=self.defaultColors[0, :], alpha=.25)
self.moving = False
self.fig.canvas.draw()
def onmotion(self, event):
if self.moving is False:
return
if event.inaxes is None:
return
if event.button != 1:
return
if self.plotSnap is False:
self.topRightX = event.xdata
self.topRightY = event.ydata
else:
self.calculateSnapCoordinates = self.setSnapBase(self.plotSnap)
self.topRightX = self.calculateSnapCoordinates(event.xdata)
self.topRightY = self.calculateSnapCoordinates(event.ydata)
self.x = np.array([self.bottomLeftX, self.bottomLeftX, self.topRightX, self.topRightX, self.bottomLeftX])
self.y = np.array([self.bottomLeftY, self.topRightY, self.topRightY, self.bottomLeftY, self.bottomLeftY])
self.myPlot.set_xdata(self.x)
self.myPlot.set_ydata(self.y)
self.fig.canvas.draw()
def __init__(self):
super(mplWidget, self).__init__()
# Set default colors array
self.defaultColors = np.array([[0, 0.4470, 0.7410], [0.8500, 0.3250, 0.0980], [0.9290, 0.6940, 0.1250],
[0.4660, 0.6740, 0.1880], [0.6350, 0.0780, 0.1840], [0.4940, 0.1840, 0.5560],
[0.3010, 0.7450, 0.9330]])
# Create a figure with axes
self.fig = plt.figure()
self.ax = self.fig.gca()
# Form the plot and shading
self.bottomLeftX = 0; self.bottomLeftY = 0; self.topRightX = 0; self.topRightY = 0
self.x = np.array([self.bottomLeftX, self.bottomLeftX, self.topRightX, self.topRightX, self.bottomLeftX])
self.y = np.array([self.bottomLeftY, self.topRightY, self.topRightY, self.bottomLeftY, self.bottomLeftY])
self.myPlot, = self.ax.plot(self.x, self.y, color=self.defaultColors[0, :])
self.aspan = self.ax.axvspan(self.bottomLeftX, self.topRightX, color= self.defaultColors[0, :], alpha=0)
# Set moving flag false (determines if mouse is being clicked and dragged inside plot). Set graph snap
self.moving = False
self.plotSnap = 5
# Set up connectivity
self.cid = self.fig.canvas.mpl_connect('button_press_event', self.onclick)
self.cid = self.fig.canvas.mpl_connect('button_release_event', self.onrelease)
self.cid = self.fig.canvas.mpl_connect('motion_notify_event', self.onmotion)
# Set plot limits and show it
plt.ylim((-100, 100))
plt.xlim((-100, 100))
plt.show()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = topLevelWindow()
sys.exit(app.exec_())
在你的代码中有很多,所以我只会列出它们:
if self.plotSnap is False:
, if not self.plotSnap:
我也认为认为“plotSnap”为 False 是不合逻辑的,如果你想禁用然后设置一个不可能的值,例如 0 或负数。考虑到上面我已经让 MplWidget 从 FigureCanvasQTAgg 继承,我已经消除了 pyplot 的使用:
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg,
NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure
class MplWidget(FigureCanvasQTAgg):
def __init__(self, parent=None):
fig = Figure()
super(MplWidget, self).__init__(fig)
self.setParent(parent)
# Set default colors array
self.defaultColors = np.array(
[
[0, 0.4470, 0.7410],
[0.8500, 0.3250, 0.0980],
[0.9290, 0.6940, 0.1250],
[0.4660, 0.6740, 0.1880],
[0.6350, 0.0780, 0.1840],
[0.4940, 0.1840, 0.5560],
[0.3010, 0.7450, 0.9330],
]
)
# Create a figure with axes
self.ax = self.figure.add_subplot(111)
# Form the plot and shading
self.bottomLeftX = 0
self.bottomLeftY = 0
self.topRightX = 0
self.topRightY = 0
self.x = np.array(
[
self.bottomLeftX,
self.bottomLeftX,
self.topRightX,
self.topRightX,
self.bottomLeftX,
]
)
self.y = np.array(
[
self.bottomLeftY,
self.topRightY,
self.topRightY,
self.bottomLeftY,
self.bottomLeftY,
]
)
(self.myPlot,) = self.ax.plot(self.x, self.y, color=self.defaultColors[0, :])
self.aspan = self.ax.axvspan(
self.bottomLeftX, self.topRightX, color=self.defaultColors[0, :], alpha=0
)
self.ax.set_xlim((-100, 100))
self.ax.set_ylim((-100, 100))
# Set moving flag false (determines if mouse is being clicked and dragged inside plot). Set graph snap
self.moving = False
self.plotSnap = 5
# Set up connectivity
self.cid1 = self.mpl_connect("button_press_event", self.onclick)
self.cid2 = self.mpl_connect("button_release_event", self.onrelease)
self.cid3 = self.mpl_connect("motion_notify_event", self.onmotion)
def setSnapBase(self, base):
return lambda value: int(base * round(float(value) / base))
def onclick(self, event):
if self.plotSnap <= 0:
self.bottomLeftX = event.xdata
self.bottomLeftY = event.ydata
else:
self.calculateSnapCoordinates = self.setSnapBase(self.plotSnap)
self.bottomLeftX = self.calculateSnapCoordinates(event.xdata)
self.bottomLeftY = self.calculateSnapCoordinates(event.ydata)
try:
self.aspan.remove()
except:
pass
self.moving = True
def onrelease(self, event):
if self.plotSnap <= 0:
self.topRightX = event.xdata
self.topRightY = event.ydata
else:
try:
calculateSnapCoordinates = self.setSnapBase(self.plotSnap)
self.topRightX = calculateSnapCoordinates(event.xdata)
self.topRightY = calculateSnapCoordinates(event.ydata)
except:
pass
self.x = np.array(
[
self.bottomLeftX,
self.bottomLeftX,
self.topRightX,
self.topRightX,
self.bottomLeftX,
]
)
self.y = np.array(
[
self.bottomLeftY,
self.topRightY,
self.topRightY,
self.bottomLeftY,
self.bottomLeftY,
]
)
self.myPlot.set_xdata(self.x)
self.myPlot.set_ydata(self.y)
# ax.fill_between(x, y, color=defaultColors[0, :], alpha=.25)
ylimDiff = self.ax.get_ylim()[1] - self.ax.get_ylim()[0]
self.aspan = self.ax.axvspan(
self.bottomLeftX,
self.topRightX,
(self.bottomLeftY - self.ax.get_ylim()[0]) / ylimDiff,
(self.topRightY - self.ax.get_ylim()[0]) / ylimDiff,
color=self.defaultColors[0, :],
alpha=0.25,
)
self.moving = False
self.draw()
def onmotion(self, event):
if not self.moving:
return
if event.inaxes is None:
return
if event.button != 1:
return
if self.plotSnap <= 0:
self.topRightX = event.xdata
self.topRightY = event.ydata
else:
self.calculateSnapCoordinates = self.setSnapBase(self.plotSnap)
self.topRightX = self.calculateSnapCoordinates(event.xdata)
self.topRightY = self.calculateSnapCoordinates(event.ydata)
self.x = np.array(
[
self.bottomLeftX,
self.bottomLeftX,
self.topRightX,
self.topRightX,
self.bottomLeftX,
]
)
self.y = np.array(
[
self.bottomLeftY,
self.topRightY,
self.topRightY,
self.bottomLeftY,
self.bottomLeftY,
]
)
self.myPlot.set_xdata(self.x)
self.myPlot.set_ydata(self.y)
self.draw()
class TopLevelWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.canvas = MplWidget()
self.setCentralWidget(self.canvas)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = TopLevelWindow()
w.show()
sys.exit(app.exec_())
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.