[英]How to make a matplotlib plot interactive in pyqt5
I'm currently working on a project where I want to embed a matplotlib plot into a pyqt5 GUI.我目前正在做一个项目,我想将 matplotlib plot 嵌入到 pyqt5 GUI 中。 The plot is interactive and allows for the drawing of shaded rectangles.
plot 是交互式的,允许绘制阴影矩形。
The problem is that the plot is not interactive when it is embedded in the pyqt window.问题是当 plot 嵌入到 pyqt window 中时,它是不可交互的。 When I run the program, line 147 in the below code (plt.show() in the mplWidget class) displays the matplotlib plot, and I can then draw a rectangle as you can see here:
当我运行程序时,下面代码中的第 147 行(mplWidget 类中的 plt.show())显示 matplotlib plot,然后我可以绘制一个矩形,如下所示:
However, when this window closes and the plot is embedded in the pyqt window, it becomes uneditable但是,当这个 window 关闭并且 plot 嵌入到 pyqt Z05B8C74CBD902FBF2DE4C1 中时,它变得不可编辑
I want the GUI plot to function as the matplotlib figure does.我想要 GUI plot 到 function 就像 matplotlib 图一样。
I know that this has to do with the fact that I have to provide the pyqt functionality with connect()
statements, but I don't know where those go / how they would fit in to this program.我知道这与我必须通过
connect()
语句提供 pyqt 功能这一事实有关,但我不知道那些 go 的位置/它们如何适合该程序。
I don't know how to connect to a matplotlib.我不知道如何连接到 matplotlib。 Do I just use
connect()
statements to the mplWidget class functions?我是否只对 mplWidget class 函数使用
connect()
语句?
Any help is appreciated!任何帮助表示赞赏!
(I realize that I will need to take line 147 out (plt.show()) so that the figure frame does not pop up before the gui, but I just had it temporarily to show that the mpl class still functions as intended, and the problem is that it becomes "static" upon embedding) (我意识到我需要取出第 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_())
In your code there are many so I will only list them:在你的代码中有很多,所以我只会列出它们:
if self.plotSnap is False:
, just if not self.plotSnap:
also I see it illogical to think that "plotSnap" is False, if you want to disable then set an impossible value, such as 0 or negative.if self.plotSnap is False:
, if not self.plotSnap:
我也认为认为“plotSnap”为 False 是不合逻辑的,如果你想禁用然后设置一个不可能的值,例如 0 或负数。 Considering the above I have made MplWidget inherit from FigureCanvasQTAgg, I have eliminated the use of pyplot:考虑到上面我已经让 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.