【发布时间】:2020-11-04 01:53:26
【问题描述】:
背景
我目前正在做一个项目,我想将 matplotlib 图嵌入到 pyqt5 GUI 中。该图是交互式的,允许绘制阴影矩形。
问题
问题是当它嵌入到pyqt窗口中时,它不是交互式的。当我运行程序时,下面代码中的第 147 行(mplWidget 类中的 plt.show())显示了 matplotlib 图,然后我可以绘制一个矩形,如下所示:
但是,当这个窗口关闭并且绘图嵌入到 pyqt 窗口中时,它变得不可编辑
我希望 GUI 绘图能够像 matplotlib 图形那样发挥作用。
建议的解决方案/问题
我知道这与我必须通过 connect() 语句提供 pyqt 功能这一事实有关,但我不知道它们的去向/如何适应这个程序。
我不知道如何连接到 matplotlib。我是否只对 mplWidget 类函数使用 connect() 语句?
任何帮助表示赞赏!
(我意识到我需要取出第 147 行 (plt.show()) 以便图形框架不会在 gui 之前弹出,但我只是暂时显示 mpl 类仍然按预期运行,问题是它在嵌入时变为“静态”)
代码
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_())
【问题讨论】:
标签: python matplotlib user-interface pyqt5 embed