问题
I am writting a program that's supposed to receive 4 random values from my Arduino every second and create an updating bar chart with them. I then want to create a PyQt5 Window to show that bar chart. I was sucessful at reading the values coming from my Arduino and using them to create the bar chart. However, since I'm a beginner when it comes to GUI/Arduino, I have no clue how to add that bar chart to my PyQt Window. I'd appreciate if you could help me with that.
Here's my Arduino code:
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);
}
and here's what the graph should look like (the titles and labels are in french, but don't mind that):
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)
Now, here is what my Window should look like, but replace the static chart with a dynamic one (don't mind the buttons, they're not meant to do anything right now):
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:
If you are going to use PyQt5 then forget about pyplot since you have to use the respective canvas. On the other hand do not use pyserial since it is better to use QSerialPort since it uses the Qt eventloop.
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_())
来源:https://stackoverflow.com/questions/62031226/how-to-add-a-live-updating-graph-to-my-pyqt5-window-using-serial