本文介绍了如何使用串行将实时(更新)图形添加到我的PyQt5窗口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个程序,该程序应该每秒从Arduino接收4个随机值,并使用它们创建一个更新的条形图.然后,我想创建一个PyQt5窗口来显示该条形图.我成功地读取了来自Arduino的值并使用它们来创建条形图.但是,由于我是GUI/Arduino的初学者,因此我不知道如何将条形图添加到我的PyQt窗口中.如果您能帮助我,我将不胜感激.

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.

这是我的Arduino代码:

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_())

推荐答案

如果要使用PyQt5,则不必使用pyplot,因为必须使用相应的画布.另一方面,不要使用pyserial,因为最好使用QSerialPort,因为它使用Qt eventloop.

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_())

这篇关于如何使用串行将实时(更新)图形添加到我的PyQt5窗口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-19 15:55