如何使用串行向我的 PyQt5 窗口添加实时(更新)图?
Posted
技术标签:
【中文标题】如何使用串行向我的 PyQt5 窗口添加实时(更新)图?【英文标题】:How to add a live (updating) graph to my PyQt5 Window using Serial? 【发布时间】:2020-05-26 20:52:42 【问题描述】:我正在编写一个程序,它应该每秒从我的 Arduino 接收 4 个随机值,并用它们创建一个更新的条形图。然后我想创建一个 PyQt5 窗口来显示该条形图。我成功地读取了来自我的 Arduino 的值并使用它们创建条形图。但是,由于我是 GUI/Arduino 的初学者,所以我不知道如何将该条形图添加到我的 PyQt 窗口中。如果您能帮我解决这个问题,我将不胜感激。
这是我的 Arduino 代码:
void setup()
Serial.begin(115200);
void loop()
float nb1 = random(0, 100);
float nb2 = random(0, 100);
float nb3 = random(0, 100);
float nb4 = random(0, 100);
Serial.print(nb1);
Serial.print(" , ");
Serial.print(nb2);
Serial.print(" , ");
Serial.print(nb3);
Serial.print(" , ");
Serial.println(nb4);
delay(1000);
这是图表的外观(标题和标签是法语,但不要介意):
import serial
import numpy
import matplotlib.pyplot as plt
from drawnow import *
plt.ion()
def make_figure():
plt.ylim(0, 100)
plt.title("Titre que tu veux")
plt.tick_params(axis = 'x', which = 'both', bottom = False, top = False, labelbottom = False)
plt.hlines(16, -0.5, 0.5, color = 'g')
plt.ylabel("Nom de la variable")
plt.bar(0, nb1, color = 'b')
plt.bar(2, nb2, color = 'b')
plt.hlines(42, 1.5, 2.5, color = 'g')
plt.bar(4, nb3, color = 'b')
plt.hlines(32, 3.5, 4.5, color = 'g')
plt.bar(6, nb4, color = 'b')
plt.hlines(80, 5.5, 6.5, color = 'g')
with serial.Serial('COM3', baudrate = 115200) as ser:
while True:
while (ser.inWaiting() == 0):
pass
string_received = ser.readline().decode().strip('\r\n')
dataArray = string_received.split(" , ")
nb1 = float(dataArray[0])
nb2 = float(dataArray[1])
nb3 = float(dataArray[2])
nb4 = float(dataArray[3])
drawnow(make_figure)
plt.pause(0.000001)
现在,我的窗口应该是这样的,但是用动态图表替换静态图表(不要介意按钮,它们现在不打算做任何事情):
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super(MplCanvas, self).__init__(fig)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("Titre que tu veux")
MainWindow.resize(1510, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushgetMax = QtWidgets.QPushButton(self.centralwidget)
self.pushgetMax.setGeometry(QtCore.QRect(1240, 30, 93, 28))
self.pushgetMax.setObjectName("pushgetMax")
self.pushAll = QtWidgets.QPushButton(self.centralwidget)
self.pushAll.setGeometry(QtCore.QRect(1350, 30, 93, 28))
self.pushAll.setObjectName("pushAll")
self.pushButton1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton1.setGeometry(QtCore.QRect(1240, 70, 93, 28))
self.pushButton1.setObjectName("pushButton1")
self.pushButton2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton2.setGeometry(QtCore.QRect(1350, 70, 93, 28))
self.pushButton2.setObjectName("pushButton2")
self.pushButton3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton3.setGeometry(QtCore.QRect(1240, 110, 93, 28))
self.pushButton3.setObjectName("pushButton3")
self.pushButton_6 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_6.setGeometry(QtCore.QRect(1350, 110, 93, 28))
self.pushButton_6.setObjectName("pushButton_6")
self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(20, 10, 1191, 531))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
barre1 = MplCanvas(self, width=5, height=4, dpi=100)
barre1.axes.set_ylim([0, 100])
barre1.axes.set_title('Titre 1')
barre1.axes.tick_params(axis = 'x', which = 'both', bottom = False, top = False, labelbottom = False)
barre1.axes.bar(0, 22, color = 'b')
barre1.axes.hlines(25, -0.5, 0.5, color = 'g')
barre1.axes.bar(2, 50, color = 'b')
barre1.axes.hlines(60, 1.5, 2.5, color = 'g')
barre1.axes.bar(4, 32, color = 'b')
barre1.axes.hlines(50, 3.5, 4.5, color = 'g')
barre1.axes.bar(6, 81, color = 'b')
barre1.axes.hlines(70, 5.5, 6.5, color = 'g')
self.horizontalLayout.addWidget(barre1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1510, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushgetMax.setText(_translate("MainWindow", "GetMax"))
self.pushAll.setText(_translate("MainWindow", "All"))
self.pushButton1.setText(_translate("MainWindow", "1"))
self.pushButton2.setText(_translate("MainWindow", "2"))
self.pushButton3.setText(_translate("MainWindow", "3"))
self.pushButton_6.setText(_translate("MainWindow", "4"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
【问题讨论】:
【参考方案1】:如果您打算使用 PyQt5,那么请忘记 pyplot,因为您必须使用相应的画布。另一方面,不要使用 pyserial,因为使用 QSerialPort 会更好,因为它使用 Qt 事件循环。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtSerialPort
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class SerialPortManager(QtCore.QObject):
dataChanged = QtCore.pyqtSignal(list)
def __init__(self, parent=None):
super().__init__(parent)
self._serial = QtSerialPort.QSerialPort(baudRate=115200)
self.serial.setPortName("COM3")
self.serial.readyRead.connect(self.on_ready_read)
@property
def serial(self):
return self._serial
def start(self):
self.serial.open(QtCore.QIODevice.ReadOnly)
@QtCore.pyqtSlot()
def on_ready_read(self):
if self.serial.canReadLine():
line = self.serial.readLine().data().decode()
values = line.strip().split(",")
try:
data = list(map(float, values))
except ValueError as e:
print("error", e)
else:
self.dataChanged.emit(data)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
fig = Figure(figsize=(5, 4), dpi=100)
self.canvas = FigureCanvas(fig)
self.max_button = QtWidgets.QPushButton(self.tr("GetMax"))
self.all_button = QtWidgets.QPushButton(self.tr("All"))
self.one_button = QtWidgets.QPushButton(self.tr("1"))
self.two_button = QtWidgets.QPushButton(self.tr("2"))
self.three_button = QtWidgets.QPushButton(self.tr("3"))
self.four_button = QtWidgets.QPushButton(self.tr("4"))
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
hlay = QtWidgets.QHBoxLayout(central_widget)
hlay.addWidget(self.canvas, stretch=1)
grid_layout = QtWidgets.QGridLayout()
grid_layout.addWidget(self.max_button, 0, 0)
grid_layout.addWidget(self.all_button, 0, 1)
grid_layout.addWidget(self.one_button, 1, 0)
grid_layout.addWidget(self.two_button, 1, 1)
grid_layout.addWidget(self.three_button, 2, 0)
grid_layout.addWidget(self.four_button, 2, 1)
vlay = QtWidgets.QVBoxLayout()
vlay.addLayout(grid_layout)
vlay.addStretch()
hlay.addLayout(vlay)
self.axes = self.canvas.figure.add_subplot(111)
self.axes.set_ylim([0, 100])
self.axes.set_title("Titre 1")
self.axes.tick_params(
axis="x", which="both", bottom=False, top=False, labelbottom=False
)
self.axes.hlines(25, -0.5, 0.5, color="g")
self.axes.hlines(60, 1.5, 2.5, color="g")
self.axes.hlines(50, 3.5, 4.5, color="g")
self.axes.hlines(70, 5.5, 6.5, color="g")
self.containers = []
self.update_bars([0, 0, 0, 0])
self.resize(640, 480)
@QtCore.pyqtSlot(list)
def update_bars(self, values):
if len(values) == 4:
[c.remove() for c in self.containers]
self.containers = []
for index, value in zip((0, 2, 4, 6), values):
c = self.axes.bar(index, value, color="b")
self.containers.append(c)
self.canvas.draw()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
manager = SerialPortManager()
manager.dataChanged.connect(w.update_bars)
manager.start()
sys.exit(app.exec_())
【讨论】:
非常感谢!我会调查的!以上是关于如何使用串行向我的 PyQt5 窗口添加实时(更新)图?的主要内容,如果未能解决你的问题,请参考以下文章
如何在python中使用pyqt5将QSlider添加到主窗口的工具栏