文章目录

  • 前言
  • 效果
  • 核心代码
  • 编程思路
  • 下位机
  • 上位机
  • widget.h
  • widget.cpp



前言

  在涉及到多个部门之间的软件开发过程时,往往会出现在同一台设备上的多个程序进程之间的通信问题。
  下位机进行数据的采集及解算工作,将运行结果发送至上位机进行展示等。由于上位机、下位机由不同单位或部门的人编写,所使用的编程语言可能不尽相同,且也有可能涉及到保密问题,故不可能互相公开源码以进行统一的编译工作,从而需要进行程序之间的通信。
  在本项目中,下位机(数据发送端、客户端)采用python的PyQt框架进行编写,发送实时处理后的图像、图像识别状态;上位机(数据接收端、服务端)采用C++的Qt框架进行编写,接收图像、识别状态并进行展示。
  利用Qt自带的QUdpSocket模块进行通信。


效果

  实验效果如下图所示,画面左边的小框是下位机右边的大框是上位机

python 制作上位机 pyqt上位机_下位机


  下位机的作用是实时拍摄人脸图像,并判断当前人脸是出于什么状态,总共有4个状态如下表所示,下位机将经过处理后的实时图像以及当前判定状态向上位机进行发送。

数字

含义

0

图像缺失

1

清醒

2

疲劳

3

重度疲劳

  上位机将接收到的QImage图像进行展示,并且一并展示出当前发出的状态信号,如“0”、“1”、“2”等,外加一个QLabel展示图像的状态,即“清醒”、“疲劳”或者是“图像缺失”等。


核心代码

编程思路

  通过Qt自带的QUdpSocket模块进行通信,由于是在同一台电脑上进行通信,上位机、下位机ip地址均设置为"127.0.0.1"即本机的回环地址,端口号设置为“5555”“5556”,其中“5555”用于发送图像数据、“5556”用于发送状态判断int型数值。

下位机

Widget类的初始化方法中,进行相关ip地址、端口号的绑定工作。

self.camera_thread.sendPicture[QImage].connect(self.receive)这里在Widget类中需要声明一个camera类,camera类用于处理实时接收图像等图像相关的操作,绑定camera类的sendPicture信号与Widget类中receive槽函数,令receive槽函数中进行相关的数据传输工作代码如下。

byte = QByteArray()
buff = QBuffer(byte)
buff.open(QIODevice.WriteOnly)
img.save(buff, "JPEG")
ss = qCompress(byte, 5)
self.udpSocket.writeDatagram(ss, self.dstip, self.dstport)
datagram = QByteArray(1, str(self.camera_thread.detector.noImageFlag))
self.udpSocket_2.writeDatagram(datagram, self.dstip, self.dstport_2)

下位机widget的相关代码如下所示

from UI.ui_Widget import *
from PyQt5.QtNetwork import QUdpSocket, QHostAddress
from PyQt5.QtCore import QBuffer, QByteArray, QIODevice, qCompress
from PyQt5.QtGui import QImage


class Widget(QWidget, Ui_Widget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.setupUi(self)
        
		#此处略去与通信无关的代码
		
        # UDP通信
        # 创建两个套接字,第一个用于传输图像,第二个用于传int型变量
        self.udpSocket = QUdpSocket()
        self.udpSocket_2 = QUdpSocket()
        # 设置ip地址
        self.dstip = QHostAddress('127.0.0.1')
        # 设置目标地址的端口号
        self.dstport = 5555
        self.dstport_2 = 5556
		# 发送端可以不bind具体的端口

		# 此处略去一些与通信无关的代码
        # 连接Camera的sendPicture信号与receive函数进行展示
        self.camera_thread.sendPicture[QImage].connect(self.receive)
   
       def receive(self, img):
        img_height = self.label_2.height()
        img_width = self.label_2.width()
        # QImage.scaled若图像尺寸较大,会卡顿,暂时没有什么好方法
        new_img = img.scaled(QSize(img_width, img_height))
        self.label_2.setPixmap(QPixmap.fromImage(new_img))

        byte = QByteArray()
        buff = QBuffer(byte)
        buff.open(QIODevice.WriteOnly)
        img.save(buff, "JPEG")
        ss = qCompress(byte, 5)
        self.udpSocket.writeDatagram(ss, self.dstip, self.dstport)
        if self.camera_thread.detector.noImage == 1:
            datagram = QByteArray(1, str(self.camera_thread.detector.noImageFlag))
        else:
            datagram = QByteArray(1, str(self.camera_thread.detector.JudgeCondition.condition))
        self.udpSocket_2.writeDatagram(datagram, self.dstip, self.dstport_2)

上位机

connect(&receiver,SIGNAL(readyRead()),this,SLOT(video_receive_show()));在udp套接字接收到信号完成后会发送一个readRead()信号,该信号绑定槽函数video_receive_show()进行图像数据显示,具体代码如下所示。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include<QPixmap>
#include<QImageReader>
#include<QBuffer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

public slots:
  void video_receive_show();
  void condition_show();

private:
    Ui::Widget *ui;
    QUdpSocket receiver;
    QUdpSocket receiver_2;

};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QProcess>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QHostAddress adrr = QHostAddress("127.0.0.1");
	
	//接收端需要bind具体的ip地址及端口号
    receiver.bind(adrr, 5555);
    receiver_2.bind(adrr, 5556);
    connect(&receiver,SIGNAL(readyRead()),this,SLOT(video_receive_show()));
    connect(&receiver_2,SIGNAL(readyRead()),this,SLOT(condition_show()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::video_receive_show()
{
    quint64 size = receiver.pendingDatagramSize();
    QByteArray buff;
    buff.resize(size);
    QHostAddress adrr ;
    quint16 port;
    receiver.readDatagram(buff.data(),buff.size(),&adrr,&port);

    buff = qUncompress(buff);
    QBuffer buffer(&buff);
    QImageReader reader(&buffer,"JPEG");
    QImage image = reader.read();
    QImage img;


    img = image.scaled(ui->label->width(), ui->label->height());
    ui->label->setPixmap(QPixmap::fromImage(img));
//    ui->label->resize(image.width(),image.height());

}

void Widget::condition_show()
{
    quint64 size = receiver_2.pendingDatagramSize();
    QByteArray buff;
    buff.resize(size);
    QHostAddress adrr ;
    quint16 port;
    receiver_2.readDatagram(buff.data(),buff.size(),&adrr,&port);
    QString msg = buff.data();

//    qDebug()<<buff;
    ui->label_3->setText(msg);

    int num = msg.toInt();

    switch(num)
    {
    case 1:
        ui->label_2->setText(QString::fromLocal8Bit("清醒"));
        ui->label_2->setStyleSheet("background-color: rgb(0, 255, 127);"
                                   "font: 22pt '微软雅黑';");
        break;

    case 2:
        ui->label_2->setText(QString::fromLocal8Bit("疲劳"));
        ui->label_2->setStyleSheet("background-color: rgb(255, 170, 0);;"
                                   "font: 22pt '微软雅黑';");
        break;

    case 3:
        ui->label_2->setText(QString::fromLocal8Bit("重度疲劳"));
        ui->label_2->setStyleSheet("background-color: rgb(255, 0, 0);;"
                                   "font: 22pt '微软雅黑';");
        break;

    case 0:
        ui->label_2->setText(QString::fromLocal8Bit("图像缺失"));
        ui->label_2->setStyleSheet("background-color: rgb(226, 226, 226);;"
                                   "font: 22pt '微软雅黑';");
        break;
    }
}