[英]Extracting information from a Matplotlib plot and displaying it in a PyQt5 GUI
我想知道 PyQt5 GUI 和嵌入式 matplotlib plot 如何在它們的屬性之間交互/傳遞信息。 具體來說,我了解內置 Qt 小部件的信號和插槽如何工作,但我不知道它如何擴展到自定義小部件或非本機對象,例如 matplotlib Z32FA6E1B78A9D4028953E60564A2AA4。
考慮下面的例子——我創建了一個 Qt GUI,它在左側顯示一個 matplotlib (mpl) plot,在右側有 4 個 QLineEdit 對象。 您可以在 mpl plot 內單擊並拖動以繪制矩形。 我想知道 go 如何將 QLineEdit 框連接到矩形的角坐標。 單擊並拖動矩形時,我希望頂部兩行編輯顯示左下角矩形角的 X 和 Y 數據,底部兩行編輯實時顯示右上角矩形角的 X 和 Y 數據. 相反,我還希望 QLineEdit 框中坐標的編輯反映在 mpl plot 中。 這是一張圖片供參考:
相關代碼可以在下面找到。 任何意見是極大的贊賞!
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar)
import numpy as np
class topLevelWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Create central widget and set layout
self.centralwidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.centralwidget)
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
# Create display frame, assign to parent layout, and create its own layout
self.displayFrame = QtWidgets.QFrame(self.centralwidget)
self.gridLayout.addWidget(self.displayFrame, 1, 0, 1, 1)
self.verticalLayout_1 = QtWidgets.QVBoxLayout(self.displayFrame)
# Add MplPlot object to display frame and assign to parent layout
self.plotWidget = MplPlot()
self.verticalLayout_1.addWidget(self.plotWidget)
# Create numbers gram, assign to parent layout, and create its own layout
self.numFrame = QtWidgets.QFrame(self.centralwidget)
self.gridLayout.addWidget(self.numFrame, 1, 1, 1, 1)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.numFrame)
# Add QLineEdits to numbers frame and assign to parent layout
self.bottomLeftCornerX = QtWidgets.QLineEdit(self.numFrame)
self.bottomLeftCornerY = QtWidgets.QLineEdit(self.numFrame)
self.topRightCornerX = QtWidgets.QLineEdit(self.numFrame)
self.topRightCornery = QtWidgets.QLineEdit(self.numFrame)
self.verticalLayout_2.addWidget(self.bottomLeftCornerX)
self.verticalLayout_2.addWidget(self.bottomLeftCornerY)
self.verticalLayout_2.addWidget(self.topRightCornerX)
self.verticalLayout_2.addWidget(self.topRightCornery)
QtCore.QMetaObject.connectSlotsByName(self)
self.show()
class MplPlot(FigureCanvasQTAgg):
def onclick(self, event):
self.bottomLeftX = event.xdata
self.bottomLeftY = event.ydata
self.topRightX = event.xdata
self.topRightY = 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
]
)
# Update the data
self.ax.lines[0].set_xdata(self.x)
self.ax.lines[0].set_ydata(self.y)
self.draw()
self.moving = True
def onrelease(self, event):
self.topRightX = event.xdata
self.topRightY = 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
]
)
# Update the data
self.ax.lines[0].set_xdata(self.x)
self.ax.lines[0].set_ydata(self.y)
self.draw()
self.moving = False
def onmotion(self, event):
if not self.moving:
return
self.topRightX = event.xdata
self.topRightY = 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.ax.lines[0].set_xdata(self.x)
self.ax.lines[0].set_ydata(self.y)
self.draw()
def __init__(self, parent=None):
fig = Figure()
super(MplPlot, self).__init__(fig)
self.setParent(parent)
self.regionUpdated = QtCore.pyqtSignal()
# Create a figure with axes
self.ax = self.figure.add_subplot(111)
self.ax.set_xlim((-100, 100))
self.ax.set_ylim((-100, 100))
self.ax.plot([0],[0])
# Initialize junk values
self.bottomLeftX = 0
self.bottomLeftY = 0
self.topRightX = 0
self.topRightY = 0
self.x = 0
self.y = 0
# Set moving flag false (determines if mouse is being clicked and dragged inside plot). Set graph snap
self.moving = False
# Set up connectivity
self.cid = self.mpl_connect("button_press_event", self.onclick)
self.cid = self.mpl_connect("button_release_event", self.onrelease)
self.cid = self.mpl_connect("motion_notify_event", self.onmotion)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = topLevelWindow()
sys.exit(app.exec_())
邏輯是創建一個發出該信息的信號,我還看到一個重復的代碼,所以我在 function 中減少了它。
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg,
NavigationToolbar2QT as NavigationToolbar,
)
import numpy as np
class topLevelWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Create central widget and set layout
self.centralwidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.centralwidget)
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
# Create display frame, assign to parent layout, and create its own layout
self.displayFrame = QtWidgets.QFrame(self.centralwidget)
self.gridLayout.addWidget(self.displayFrame, 1, 0, 1, 1)
self.verticalLayout_1 = QtWidgets.QVBoxLayout(self.displayFrame)
# Add MplPlot object to display frame and assign to parent layout
self.plotWidget = MplPlot()
self.verticalLayout_1.addWidget(self.plotWidget)
# Create numbers gram, assign to parent layout, and create its own layout
self.numFrame = QtWidgets.QFrame(self.centralwidget)
self.gridLayout.addWidget(self.numFrame, 1, 1, 1, 1)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.numFrame)
# Add QLineEdits to numbers frame and assign to parent layout
self.bottomLeftCornerX = QtWidgets.QLineEdit(self.numFrame)
self.bottomLeftCornerY = QtWidgets.QLineEdit(self.numFrame)
self.topRightCornerX = QtWidgets.QLineEdit(self.numFrame)
self.topRightCornery = QtWidgets.QLineEdit(self.numFrame)
self.verticalLayout_2.addWidget(self.bottomLeftCornerX)
self.verticalLayout_2.addWidget(self.bottomLeftCornerY)
self.verticalLayout_2.addWidget(self.topRightCornerX)
self.verticalLayout_2.addWidget(self.topRightCornery)
self.plotWidget.regionUpdated.connect(self.update_le)
@QtCore.pyqtSlot(QtCore.QRectF)
def update_le(self, region):
self.bottomLeftCornerX.setText(str(region.left()))
self.bottomLeftCornerY.setText(str(region.top()))
self.topRightCornerX.setText(str(region.right()))
self.topRightCornery.setText(str(region.bottom()))
class MplPlot(FigureCanvasQTAgg):
regionUpdated = QtCore.pyqtSignal(QtCore.QRectF)
def __init__(self, parent=None):
fig = Figure()
super(MplPlot, self).__init__(fig)
self.setParent(parent)
# Create a figure with axes
self.ax = self.figure.add_subplot(111)
self.ax.set_xlim((-100, 100))
self.ax.set_ylim((-100, 100))
self.ax.plot([0], [0])
# Initialize junk values
self.bottomLeftX = 0
self.bottomLeftY = 0
self.topRightX = 0
self.topRightY = 0
self.x = 0
self.y = 0
# Set moving flag false (determines if mouse is being clicked and dragged inside plot). Set graph snap
self.moving = False
# Set up connectivity
self.cid = self.mpl_connect("button_press_event", self.onclick)
self.cid = self.mpl_connect("button_release_event", self.onrelease)
self.cid = self.mpl_connect("motion_notify_event", self.onmotion)
def onclick(self, event):
self.bottomLeftX = event.xdata
self.bottomLeftY = event.ydata
self.topRightX = event.xdata
self.topRightY = event.ydata
self.update_rect()
self.moving = True
def onrelease(self, event):
self.topRightX = event.xdata
self.topRightY = event.ydata
self.update_rect()
self.moving = False
def onmotion(self, event):
if not self.moving:
return
self.topRightX = event.xdata
self.topRightY = event.ydata
self.update_rect()
def update_rect(self):
x = np.array(
[
self.bottomLeftX,
self.bottomLeftX,
self.topRightX,
self.topRightX,
self.bottomLeftX,
]
)
y = np.array(
[
self.bottomLeftY,
self.topRightY,
self.topRightY,
self.bottomLeftY,
self.bottomLeftY,
]
)
self.ax.lines[0].set_xdata(x)
self.ax.lines[0].set_ydata(y)
if any(
e is None
for e in [
self.topRightX,
self.topRightY,
self.bottomLeftX,
self.bottomLeftY,
]
):
return
rect = QtCore.QRectF(
self.topRightX, self.topRightY, self.bottomLeftX, self.bottomLeftY
)
self.regionUpdated.emit(rect)
self.draw()
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.