Qt之进程通信-IPC(QLocalServer,QLocalSocket 含源码+注释)

文章目录

  • 一、IPC通信示例图
    • 1.1 设置关键字并连接的示例图
    • 1.2 进程间简单的数据通信示例图
    • 1.3 断开连接的示例图
      • 1.3.1 由Server主动断开连接
      • 1.3.2 由Socket主动断开连接
  • 1.4 Server停止监听后的效果
  • 二、个人理解与一些心得
  • 三、一些疑问(求教 家人们😂)
  • 四、源码
    • CMainWindowServer
      • CMainWindowServer.h
      • CMainWindowServer.cpp
      • CMainWindowServer.ui
    • CMainWindowSocket
      • CMainWindowSocket.h
      • CMainWindowSocket.cpp
      • CMainWindowSocket.ui
  • 总结
  • 相关文章

一、IPC通信示例图

1.1 设置关键字并连接的示例图

如下,分别在各个界面的关键字控件中填入key,依次连接。
请添加图片描述

1.2 进程间简单的数据通信示例图

如下,简单演示了server与全部、指定socket通信及接收socket发送的数据。
请添加图片描述

1.3 断开连接的示例图

1.3.1 由Server主动断开连接

如下,演示了单独断开一个及断开全部的操作,其中断开操作是由server发送数据通知socket断开,server这边则等待断开返回。
请添加图片描述

1.3.2 由Socket主动断开连接

如下演示了socket程序主动断开的操作
请添加图片描述

1.4 Server停止监听后的效果

如下,演示了server停止监听后仍可以与已经连接过的socket的通信的效果。
请添加图片描述

二、个人理解与一些心得

  1. 若要使用QLocalServer/QLocalSocket,需要在 pro添加network模块(添加这一行QT += network)。
  2. 在我个人使用中发现,在同一进程中,调用socket的write是不会触发当前进程的readyRead信号链接的信号槽。
  3. 在QLocalServer停止监听后不会影响已经连接好的Socket对象,因为QLocalServer的close仅负责停止监听,并不断开。

三、一些疑问(求教 家人们😂)

  1. 在帮助中又下方的帮助代码,但是在本地测试发现不能先调用disconnectFromServer,后面的waitForDisconnected总是拿不到状态。
	socket->disconnectFromServer();
  	if (socket->waitForDisconnected(1000))
      qDebug("Disconnected!");
  1. 以及在个人理解中的第2点也存在一些疑问

四、源码

CMainWindowServer

CMainWindowServer.h

#ifndef CMAINWINDOWSERVER_H
#define CMAINWINDOWSERVER_H

#include <QMainWindow>
#include <QLocalServer>

namespace Ui {
class CMainWindowServer;
}

class QLocalSocket;
class CMainWindowServer : public QMainWindow
{
    Q_OBJECT

public:
    explicit CMainWindowServer(QWidget *parent = nullptr);
    ~CMainWindowServer();

private:
    /**
     * @brief disconnectSocketByStr 指定socket断开函数(复用)
     * @param socketStr 指定的socket套接字字符串
     */
    void disconnectSocketByStr(const QString &socketStr);

private slots:
    /**
     * @brief on_btnListen_clicked 开始监听按钮
     */
    void on_btnListen_clicked();

    /**
     * @brief on_btnStopListen_clicked 停止监听按钮
     */
    void on_btnStopListen_clicked();

    /**
     * @brief on_newConnection 新连接槽函数
     */
    void on_newConnection();

    /**
     * @brief on_socketReadyRead 数据接收槽函数
     */
    void on_socketReadyRead();

    /**
     * @brief on_btnDisconnectSocket_clicked 断开socket槽函数
     */
    void on_btnDisconnectSocket_clicked();

    /**
     * @brief on_btnSend_clicked 数据发送按钮
     */
    void on_btnSend_clicked();

private:
    Ui::CMainWindowServer *ui;

    QLocalServer            m_localServer;          // 通信服务对象

    QList<QLocalSocket *>   m_listLocalSockets;     //  本地套接字列表
};

#endif // CMAINWINDOWSERVER_H

CMainWindowServer.cpp

#include "CMainWindowServer.h"
#include "ui_CMainWindowServer.h"

#include <QLocalServer>
#include <QMessageBox>
#include <QLocalSocket>
#include <QDebug>
#include <QTimer>

CMainWindowServer::CMainWindowServer(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindowServer)
{
    ui->setupUi(this);
    // 关联套接字连接槽函数
    connect(&m_localServer, &QLocalServer::newConnection, this, &CMainWindowServer::on_newConnection);
}

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

void CMainWindowServer::disconnectSocketByStr(const QString &socketStr)
{
    // 强转当前指针字符串或者socket指针对象
    QLocalSocket *socket = (QLocalSocket *)socketStr.toUInt();
    // 判断是否存在于socket容器中
    if(m_listLocalSockets.contains(socket)) {
        // 发送关闭提示给socket
        socket->write(u8"服务器断开!");
        // 等待3000毫秒接收断开链接的信息
        if(!socket->waitForDisconnected(3000)) {
            QMessageBox::information(this, u8"提示", "断开超时");
        }
        else {
            // 移除当前位置的控件
            QMessageBox::information(this, u8"提示", "断开成功");
            // 移除当前指定的
            ui->comboBoxSockets->removeItem(ui->comboBoxSockets->findText(socketStr));
            m_listLocalSockets.removeOne(socket);
        }
    }
    else {
        QMessageBox::information(this, u8"提示", socketStr + u8"地址无记录");
    }
}

void CMainWindowServer::on_btnListen_clicked()
{
    QString listenKey = ui->lineEditListenKey->text();
    // 获取是否监听成功
    bool flag =  m_localServer.listen(listenKey);
    if(!flag) {
        QMessageBox::information(this, u8"提示", m_localServer.errorString());
    }
    else {
        QMessageBox::information(this, u8"提示", u8"监听成功");
        // 监听后‘开始监听’按钮禁用,‘停止监听’按钮启用
        ui->btnListen->setEnabled(false);
        ui->btnStopListen->setEnabled(true);
    }
}

void CMainWindowServer::on_btnStopListen_clicked()
{
    m_localServer.close();
    if(!m_localServer.isListening()) {
        QMessageBox::information(this, u8"提示", u8"停止监听成功");
        // 停止监听后‘开始监听’按钮启用,‘停止监听’按钮禁用
        ui->btnListen->setEnabled(true);
        ui->btnStopListen->setEnabled(false);
    }
    else {
        QMessageBox::information(this, u8"提示", u8"停止监听失败");
    }
}

void CMainWindowServer::on_newConnection()
{
    // 判断是否存在新的socket连接
    if(m_localServer.hasPendingConnections()) {
        // 获取套接字对象
        QLocalSocket *socketTmp = m_localServer.nextPendingConnection();
        // 套接字对象添加到套接字容器中
        m_listLocalSockets.append(socketTmp);
        // 套接字地址转为数值
        QString socketStr = QString::number((uint64_t)socketTmp);
        // 套接字文本添加到下拉列表中并在界面做出连接提示
        ui->comboBoxSockets->addItem(socketStr);
        ui->textEdit->append(socketStr + "加入连接!");
        // 关联新数据的信号槽
        connect(socketTmp, &QLocalSocket::readyRead, this, &CMainWindowServer::on_socketReadyRead);
    }
}

void CMainWindowServer::on_socketReadyRead()
{
    // 获取发送信号的对象
    QLocalSocket *curSocket = dynamic_cast<QLocalSocket *>(sender());
    // 将数据直接读取并添加到多行文本框中
    ui->textEdit->append(QString::number((uint64_t)curSocket) + ":" + curSocket->readAll());
}

void CMainWindowServer::on_btnDisconnectSocket_clicked()
{
    // 获取将要断开的文本并弹出断开提示
    QString socketStr = ui->comboBoxSockets->currentText();
    QMessageBox::StandardButton flag = QMessageBox::information(this, u8"提示", u8"是否断开" + socketStr + "?");
    if(QMessageBox::Ok != flag) {
        return;
    }

    // 根据断开文本做不不同断开操作
    if(0 == socketStr.compare(u8"全部")) {
        foreach(QLocalSocket *socket, m_listLocalSockets) {
            disconnectSocketByStr(QString::number((uint64_t)socket));
        }
    }
    else {
        disconnectSocketByStr(socketStr);
    }
}

void CMainWindowServer::on_btnSend_clicked()
{
    // 获取将要接收数据的识别文本
    QString socketStr = ui->comboBoxSockets->currentText();
    // 获取将要发送的数据
    QString data = ui->textEditSendData->toPlainText();

    // 根据识别文本做出不同的操作
    if(0 == socketStr.compare(u8"全部")) {
        foreach(QLocalSocket *socket, m_listLocalSockets) {
            socket->write(data.toUtf8());
        }
    }
    else {
        // 直接将当前文本强转为套接字对象(因为该文本为指针地址强转而来)
        QLocalSocket *socket = (QLocalSocket *)socketStr.toUInt();
        if(m_listLocalSockets.contains(socket)) {
            socket->write(data.toUtf8());
        }
        else {
            QMessageBox::information(this, u8"提示", socketStr + "地址找不到");
        }
    }

}

CMainWindowServer.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindowServer</class>
 <widget class="QMainWindow" name="CMainWindowServer">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>340</width>
    <height>420</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,3,1,0">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QLineEdit" name="lineEditListenKey"/>
      </item>
      <item>
       <widget class="QPushButton" name="btnListen">
        <property name="text">
         <string>监听</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnStopListen">
        <property name="enabled">
         <bool>false</bool>
        </property>
        <property name="text">
         <string>停止监听</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_3">
      <item>
       <widget class="QComboBox" name="comboBoxSockets">
        <item>
         <property name="text">
          <string>全部</string>
         </property>
        </item>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnDisconnectSocket">
        <property name="text">
         <string>断开当前链接选项</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <widget class="QTextEdit" name="textEdit"/>
    </item>
    <item>
     <widget class="QTextEdit" name="textEditSendData"/>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="btnSend">
        <property name="text">
         <string>发送</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>340</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

CMainWindowSocket

CMainWindowSocket.h

#ifndef CMAINWINDOWSOCKET_H
#define CMAINWINDOWSOCKET_H

#include <QMainWindow>
#include <QLocalSocket>

namespace Ui {
class CMainWindowSocket;
}

class CMainWindowSocket : public QMainWindow
{
    Q_OBJECT

public:
    explicit CMainWindowSocket(QWidget *parent = nullptr);
    ~CMainWindowSocket();

private slots:
    /**
     * @brief on_btnConnect_clicked 连接按钮信号槽
     */
    void on_btnConnect_clicked();

    /**
     * @brief on_btnSend_clicked 发送按钮信号槽
     */
    void on_btnSend_clicked();

    /**
     * @brief on_btnDisConnected_clicked 断开连接信号槽
     */
    void on_btnDisConnected_clicked();

    /**
     * @brief on_socketReadyRead 数据接收信号槽
     */
    void on_socketReadyRead();

private:
    Ui::CMainWindowSocket *ui;

    QLocalSocket    m_localSocket;  // 套接字对象
};

#endif // CMAINWINDOWSOCKET_H

CMainWindowSocket.cpp

#include "CMainWindowSocket.h"
#include "ui_CMainWindowSocket.h"

#include <QMessageBox>
#include <QTimer>

CMainWindowSocket::CMainWindowSocket(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindowSocket)
{
    ui->setupUi(this);
    // 关联数据接收信号槽
    connect(&m_localSocket, &QLocalSocket::readyRead, this, &CMainWindowSocket::on_socketReadyRead);
}

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

void CMainWindowSocket::on_btnConnect_clicked()
{
    // 根据key连接服务
    m_localSocket.connectToServer(ui->lineEditConnectKey->text());
    // 等待一秒是否连接成功
    if(m_localSocket.waitForConnected(1000)) {
        QString tip = u8"连接成功";
        // 连接成功后打开读写通道
        if(!m_localSocket.open(QIODevice::ReadWrite)) {
            tip.append(QString(u8",但Socket读写打开失败(%1)").arg(m_localSocket.errorString()));
        }
        QMessageBox::information(this, u8"提示", tip);
        // 连接后‘连接’按钮禁用,‘断开连接’按钮启用
        ui->btnConnect->setEnabled(false);
        ui->btnDisConnected->setEnabled(true);

    }
    else {
        QMessageBox::information(this, u8"提示", u8"连接失败");
    }
}

void CMainWindowSocket::on_btnSend_clicked()
{
    // 写入数据
    m_localSocket.write(ui->textEditSendData->toPlainText().toUtf8());
    // 等待写入信号,若未写入成功弹出提示
    if(!m_localSocket.waitForBytesWritten(100)) {
        QMessageBox::information(this, u8"提示", m_localSocket.errorString());
    }
}

void CMainWindowSocket::on_btnDisConnected_clicked()
{
    if(QLocalSocket::ConnectedState == m_localSocket.state()) {
        m_localSocket.write(QString(u8"%1已断开!").arg((uint64_t)this).toUtf8());
        m_localSocket.disconnectFromServer();
        // 断开连接后‘连接’按钮启用,‘断开连接’按钮禁用
        ui->btnConnect->setEnabled(true);
        ui->btnDisConnected->setEnabled(false);
    }
    else {
        QMessageBox::information(this, u8"提示", u8"断开失败,当前并非连接状态!" );
    }
}

void CMainWindowSocket::on_socketReadyRead()
{
    // 读取索引数据
    QString data = m_localSocket.readAll();
    // 识别数据文本,当复合条件是断开连接
    if(0 == data.compare(u8"服务器断开!")) {
        on_btnDisConnected_clicked();
    }
    ui->textEdit->append(data);
}

CMainWindowSocket.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindowSocket</class>
 <widget class="QMainWindow" name="CMainWindowSocket">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>300</width>
    <height>420</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindowSocket</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout" stretch="0,3,1,0">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QLineEdit" name="lineEditConnectKey"/>
      </item>
      <item>
       <widget class="QPushButton" name="btnConnect">
        <property name="text">
         <string>连接</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnDisConnected">
        <property name="enabled">
         <bool>false</bool>
        </property>
        <property name="text">
         <string>断开连接</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <widget class="QTextEdit" name="textEdit"/>
    </item>
    <item>
     <widget class="QTextEdit" name="textEditSendData"/>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="btnSend">
        <property name="text">
         <string>发送</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>300</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

总结

在使用QLocalServer和QLocalSocket的过程中,发现QLocalSocket不是数据通道的持有对象,而是数据通道本身(如共享内存是通过data获取共享内存的地址,而QLocalSocket是直接调用write写入,当然和他的继承有关系),而相对来说QLocalServer更像使用者。不过IPC相对于共享内存来说可能有及时性的特点,因为数据一来IPC就直接读取,而共享内存则是需要定时读取数据。

相关文章

Qt之进程通信-共享内存(含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94120.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算法设计 || 第6题:基于最小成本检索的分支界限算法求解15谜问题

用基于最小成本检索的分支界限算法求解15谜问题 要求利用成本估计函数C^(X)&#xff0c; C^(X)为节点x排列中不在其目标位置上的牌的总张数&#xff08;不包括空格牌&#xff09;。 现定义空格牌的上下左右四种活动&#xff0c; 按四种活动分别生成其状态空间树&#xff0c;…

Node.js @zurmokeeper/exceljs 如何快速导出多表头的excel文件

Node.js 如何快速导出嵌套列&#xff08;多表头&#xff09;的excel文件。效果图如下&#xff1a; 1&#xff1a;使用 zurmokeeper/exceljs&#xff0c; V4.4.1以上 安装&#xff1a; npm i zurmokeeper/exceljs 2: 有一个 worksheet.makeColumns 方法&#xff0c;API文档&am…

​LeetCode解法汇总57. 插入区间

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给你一个 …

指针进阶详解

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.字符指针 2.指针数组 3.数组指针 4.数组传…

Mysql--技术文档--B树-数据结构的认知

阿丹解读&#xff1a; B树&#xff08;B tree&#xff09;和B树&#xff08;B-tree&#xff09;都是常见的自平衡搜索树数据结构&#xff0c;用于在存储和检索大量数据时提供高效的操作。 基本概念-B树/B树 B树&#xff08;B-tree&#xff09;和B树&#xff08;B tree&#x…

【如何对公司网络进行限速?一个案例详解】

有不少朋友问到了关于企业网络QoS配置&#xff0c;这个确实在实际网络应用中非常多&#xff0c;基本上大部分企业或个人都用到这个功能&#xff0c;本期我们详细了解下QoS如何对宽带进行限制&#xff0c;QoS如何企业中应用。 一、什么是QoS? Qos是用来解决网络延迟和阻塞等问…

基于Java swing和mysql实现学生信息管理系统(源码+数据库+运行指导视频)

一、项目简介 本项目是一套基于Java swing和mysql实现学生信息管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过…

【日常积累】Linux下ftp服务安装

概述 FTP是一种在互联网中进行文件传输的协议&#xff0c;基于客户端/服务器模式&#xff0c;默认使用20、21号端口&#xff0c;其中端口20用于进行数据传输&#xff0c;端口21用于接受客户端发出的相关FTP命令与参数。FTP服务器普遍部署于内网中&#xff0c;具有容易搭建、方…

供应链 | 大数据报童模型:基于机器学习的实践见解

论文解读&#xff1a;李欣 马玺渊 作者&#xff1a;Gah-Yi Ban, Cynthia Rudin 引用&#xff1a;Ban, Gah-Yi and Cynthia Rudin. The big data newsvendor: Practical insights from machine learning. Operations Research 67.1 (2019): 90-108. 文章链接&#xff1a;https…

Visual Studio软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 Visual Studio是微软公司开发的一款集成开发环境&#xff08;IDE&#xff09;&#xff0c;广泛应用于Windows平台上的应用程序和Web应用程序的开发。以下是Visual Studio软件的主要特点和功能&#xff1a; 集成开发环境&#x…

java八股文面试[JVM]——垃圾回收

参考&#xff1a;JVM学习笔记&#xff08;一&#xff09;_卷心菜不卷Iris的博客-CSDN博客 GC垃圾回收面试题&#xff1a; JVM内存模型以及分区&#xff0c;需要详细到每个区放什么 堆里面的分区&#xff1a;Eden&#xff0c;survival from to&#xff0c;老年代&#xff0c;各…

【halcon深度学习】图像分割数据集格式的转换

前言 目前用于**图像分割的**数据集&#xff0c;我目前接触到的用的比较多的有&#xff1a; 1 PASCAL VOC 2 COCO 3 YOLO 4 Halcon自己的格式&#xff08;其实就是Halcon字典类型&#xff09;当前我涉及到计算机视觉中的数据集格式有&#xff0c;PASCAL VOC、COCO 和 YOLO 用于…

英特尔开始加码封装领域 | 百能云芯

在积极推进先进制程研发的同时&#xff0c;英特尔正在加大先进封装领域的投入。在这个背景下&#xff0c;该公司正在马来西亚槟城兴建一座全新的封装厂&#xff0c;以加强其在2.5D/3D封装布局领域的实力。据了解&#xff0c;英特尔计划到2025年前&#xff0c;将其最先进的3D Fo…

计算机毕设 基于机器视觉的二维码识别检测 - opencv 二维码 识别检测 机器视觉

文章目录 0 简介1 二维码检测2 算法实现流程3 特征提取4 特征分类5 后处理6 代码实现5 最后 0 简介 今天学长向大家介绍一个机器视觉的毕设项目&#xff0c;二维码 / 条形码检测与识别 基于机器学习的二维码识别检测 - opencv 二维码 识别检测 机器视觉 1 二维码检测 物体检…

SAP-FI-会计凭字段替代OBBH

会计凭证替代OBBH 业务&#xff1a;文本必须等于某个字段的值&#xff0c;例如凭证日期 关闭确认功能&#xff0c;输入OBBH 双击“替代”进入功能配置&#xff0c;或者用GGB1&#xff0c;用GGB1的功能更多。 点击行项目&#xff0c;点击“新建替换”保存 点击新建YXL7331,点击…

【HCIP】生成树--STP

一、STP 1.产生背景 在星状拓扑或者树形拓扑中&#xff0c;当某个设备或者某条链路出现故障&#xff0c;就会导致数据不能正常转发&#xff0c;出现单点故障的问题。 为了防止出现单点故障&#xff0c;一般需要环形拓扑来保证链路的冗余性&#xff0c;当某条链路出现故障&…

OpenEuler 安装mysql

下载安装包 建议直接使用在openEuler官方编译移植过的mysql-5.7.21系列软件包 参考&#xff1a;操作系统迁移实战之在openEuler上部署MySQL数据库 | 数据库迁移方案 | openEuler社区官网 MySQL 5.7.21 移植指南&#xff08;openEuler 20.03 LTS SP1&#xff09; | 数据库移植…

天气插件和antv图表组件库的使用

目录 天气插件 antv组件库 特性 数据映射 data xField yField 图形样式 point state 图表组件 label tooltip 图表交互 添加交互 天气插件 网站:天气预报代码_天气预报插件_免费天气预报代码(插件)调用——天气网 (tianqi.com) 挑选想要的样式&#xff0c;点击 …

phpspreadsheet导出excel自动获得列,数字下标

安装composer require phpoffice/phpspreadsheetuse PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Border;$spreadsheet new Spreadsheet(); $sheet $spreadsheet->getActiveSheet();//从65开&a…

KCP协议

1、什么是kcp协议 了解kcp协议之前先回顾一下传输层的两大协议TCP和UDP。 kcp是一个快速可靠协议&#xff08;也可以叫udp的可靠性传输&#xff09;。结合了tcp的可靠性和udp的传输速度等优点&#xff0c;能以⽐ TCP浪费10%-20%带宽的代价&#xff0c;换取平均延迟降低 30%-40%…