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

news2024/9/23 23:22:32

文章目录

  • 一、内存共享示例图
    • 读取文本
    • 读取图片
  • 二、界面操作共享内存示例图
    • 文本读取示例图
    • 图片读取示例图
    • 弹窗示例图
  • 二、个人理解与一些心得
  • 三、源码
    • 简易内存共享Demo
      • 创建者
      • 接收者
    • 界面共享内存Demo
      • 创建者
      • 读取者
  • 总结

一、内存共享示例图

读取文本

下图是读取文本的操作,其中显示了中文所占字节数及发送者和读取者的通信。
请添加图片描述

读取图片

下图将对应宏放开则是图片读取效果,其中图片读取的是死路径,需要自己定义。
请添加图片描述

二、界面操作共享内存示例图

文本读取示例图

下图操作了从创建key到共享内存的步骤,其中包括接收者修改数据的部分。
请添加图片描述

图片读取示例图

下图仅为图片的共享与读取。
请添加图片描述

弹窗示例图

下图仅演示了部分提示弹窗的效果,具体以代码为准。
请添加图片描述

提示:只需要了解用法,查看简易共享内存代码即可。
不会使用Qt设计师设计界面的小伙伴点击这里

二、个人理解与一些心得

  1. create函数创建的大小和实际指定的大小可能存在差异,我猜测会考虑内存对齐等等情况,讯飞星火认知大模型给出的回答是“这是因为在创建共享内存时,操作系统会为共享内存预留一些额外的空间,以便在需要时进行扩展。因此,实际获取到的共享内存大小可能会比创建时指定的大小要大一些。”;
  2. 当最后一个持有程序detach后,共享内存控件将自动释放,所以建议共享内存创建者的detach放在析构函数,其他进程“实报实销”。

三、源码

简易内存共享Demo

创建者

main.cpp

#include <QApplication>
#include <QDebug>
#include <QSharedMemory>
#include <QBuffer>
#include <QDataStream>
#include <QLabel>
#include <QImage>
#include <QPixmap>

//#define ACTIVE_SHARED_IMG

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSharedMemory sharedMemSender;
    // 设置共享内存区域Key
    sharedMemSender.setKey("SimpleShared");

    // 数据大小对象
    int size = 0;
#ifndef ACTIVE_SHARED_IMG
    // 创建字符数据
    QString sendData = u8"共享内存中的数据";
    //! 获取字符大小(中文在字节中所占小的存储与字符个数不一致)
    //! 下方代码中一个中文字符占三个字节
    size = sendData.toUtf8().size();
    qDebug() << u8"一个中文字符所占字节数:" << size / sendData.size();
#else
    // 创建QImage对象,并传入图片路径
    QImage img("D:/xx.jpg");
    // 创建QBuffer对象并以读写方式打开
    QBuffer buffer;
    buffer.open(QIODevice::ReadWrite);
    // 创建QDataStream对象并指定数据存储对象为buffer
    QDataStream inStream(&buffer);
    // 将图片数据传入
    inStream << img;
    // 获取需要共享的数据大小
    size = buffer.size();
    // 将共享的图片用label显示出来
    QLabel label;
    label.setPixmap(QPixmap::fromImage(img));
    label.setWindowTitle("Sender");
    label.show();
#endif


    // 创建共享内存区域
    sharedMemSender.create(488812);
    // 将共享内存区域锁定
    sharedMemSender.lock();
    //! 写入数据的操作
#ifndef ACTIVE_SHARED_IMG
    memcpy(sharedMemSender.data(), sendData.toStdString().data(), size);
#else
    memcpy(sharedMemSender.data(), buffer.data().data(), buffer.size());
#endif
    // 将共享内存区域解锁
    sharedMemSender.unlock();



    return a.exec();
}

接收者

#include <QApplication>
#include <QDebug>
#include <QSharedMemory>
#include <QBuffer>
#include <QDataStream>
#include <QLabel>
#include <QImage>
#include <QPixmap>

//#define ACTIVE_SHARED_IMG

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSharedMemory sharedMemReceiver;
    // 设置共享内存区域Key
    sharedMemReceiver.setKey("SimpleShared");

    // 将共享内存区域与程序挂接
    sharedMemReceiver.attach();
    // 将共享内存区域锁定
    sharedMemReceiver.lock();
    // 获取共享内存大小(此处相当于数据大小)
    int size = sharedMemReceiver.size();

#ifndef ACTIVE_SHARED_IMG
    QByteArray data;
    data.resize(size);
    memcpy(data.data(), sharedMemReceiver.data(), size);
    qDebug() << QString(data);
#else
    // 创建QBuffer对象
    QBuffer buffer;
    // 将共享内存的数据设置到buffer容器中
    buffer.setData((char *)sharedMemReceiver.constData(), size);
    // 然后以只读的方式打开
    buffer.open(QBuffer::ReadOnly);
    // 创建QDataStream对象读取数据,并指定数据buffer
    QDataStream outStream(&buffer);
    // 创建QImage并通过数据流对象将图片数据写入
    QImage img;
    outStream >> img;
    // 创建label将读取到的图片显示出来
    QLabel label;
    label.setWindowTitle("Receiver");
    label.setPixmap(QPixmap::fromImage(img));
    label.show();
#endif
    // 将共享内存解锁
    sharedMemReceiver.unlock();
    // 将挂接的共享内存与程序分离
    sharedMemReceiver.detach();
    return a.exec();
}

界面共享内存Demo

创建者

CMainWindowMemSender.h

#ifndef CMAINWINDOWMEMSENDER_H
#define CMAINWINDOWMEMSENDER_H

#include <QBuffer>
#include <QMainWindow>
#include <QSharedMemory>

namespace Ui {
class CMainWindowMemSender;
}

class CMainWindowMemSender : public QMainWindow
{
    Q_OBJECT

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

private:
    /**
     * @brief judgeSize 判断共享内存与传入值的大小比较,若是传入值大于共享内存则弹出指定提示文本
     * @param dataSize 传入的数据大小
     * @param note 提示文本
     * @return 共享内存与传入值的大小比较
     */
    bool judgeSize(qint64 dataSize, const QString &note);

private slots:
    /**
     * @brief on_btnShareData_clicked 数据共享按钮
     */
    void on_btnShareData_clicked();

    /**
     * @brief on_btnReadData_clicked 数据读取按钮
     */
    void on_btnReadData_clicked();

    /**
     * @brief on_btnCreateShareSize_clicked 创建共享内存按钮
     */
    void on_btnCreateShareSize_clicked();

    /**
     * @brief on_btnBindShareKey_clicked 绑定共享内存key按钮
     */
    void on_btnBindShareKey_clicked();

private:
    Ui::CMainWindowMemSender *ui;

    QSharedMemory   m_sharedMemory;    // 共享内存对象
};

#endif // CMAINWINDOWMEMSENDER_H

CMainWindowMemSender.cpp

#include "CMainWindowMemSender.h"
#include "ui_CMainWindowMemSender.h"

#include <QMessageBox>
#include <QImage>
#include <QPixmap>
#include <QDataStream>
#include <QBuffer>
#include <QFileDialog>
#include <QStandardPaths>

CMainWindowMemSender::CMainWindowMemSender(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindowMemSender)
{
    ui->setupUi(this);
    // 设置标题
    this->setWindowTitle(u8"共享内存数据发送窗口");
}

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

bool CMainWindowMemSender::judgeSize(qint64 dataSize, const QString &note)
{
    bool ret = m_sharedMemory.size() >= dataSize;
    if(!ret) {
        QMessageBox::information(this, u8"提示", note);
    }
    return ret;
}

void CMainWindowMemSender::on_btnShareData_clicked()
{
    if(judgeSize(1, u8"共享内存大小为0,请先创建共享内存")) {
        // 共享内存锁定
        m_sharedMemory.lock();
        // 当前为首个tab页签进入(此处是文本类型)
        if(0 == ui->tabWidget->currentIndex()) {
            // 获取共享的文本
            QString str = ui->textEdit->toPlainText();
            // 数据转为UTF-8再获取大小(考虑中文所占字节数的情况)
            qint64 dataSize = str.toUtf8().size();
            // 判断数据是否可以存入共享内存
            if(judgeSize(dataSize, u8"数据拷贝失败,共享内存大小小于数据大小")) {
                // 拷贝数据
                memcpy(m_sharedMemory.data(), str.toStdString().data(), dataSize);
            }
        }
        else { // 当前共享内容为图片
            // 通过文件弹窗获取图片路径
            QString imgPath = QFileDialog::getOpenFileName
                    (this, u8"选择图片"
                     , QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)
                     , "*.png *.jpg");
            // 判断获取的图片路径是否为空
            if(!imgPath.isEmpty()) {
                // 创建buffer对象
                QBuffer buffer;
                // 通过读写方式打开
                buffer.open(QIODevice::ReadWrite);
                // 创建数据流对象,并将流容器指定为创建的buffer对象
                QDataStream writeStream(&buffer);
                // 创建图片对象(方便存入buffer)
                QImage img(imgPath);
                // 将图片数据写入buffer
                writeStream << img;
                // 获取buffer的大小(相当于图片大小)
                qint64 imgSize = buffer.size();
                // 判断数据是否可以存入共享内存
                if(judgeSize(imgSize, u8"数据拷贝失败,共享内存大小小于数据大小")) {
                    // 拷贝数据到共享内存
                    memcpy(m_sharedMemory.data(), buffer.data().data(), imgSize);
                    // 将图片设置到对应的显示区域
                    ui->labelImg->setPixmap(QPixmap::fromImage(img));
                }
            }
        }
        // 将共享内存解锁
        m_sharedMemory.unlock();
    }
}

void CMainWindowMemSender::on_btnReadData_clicked()
{
    // 将共享内存锁定
    m_sharedMemory.lock();
    // 获取共享内存常量指针
    const char *memData = (char *)m_sharedMemory.constData();
    // 获取共享内存大小
    qint64 dataSize = m_sharedMemory.size();
    // 判断索引值,区分图片还是文本
    if(0 == ui->tabWidget->currentIndex()) {
        // 直接创建字节数组对象,并将共享内存常量指针和共享内存大小传入
        QByteArray data(memData, dataSize);
        ui->textEdit->setText(QString(data));
    }
    else {
        // 创建buffer对象
        QBuffer buffer;
        // 设置数据及数据大小
        buffer.setData(memData, dataSize);
        // 以读写方式打开buffer
        buffer.open(QIODevice::ReadWrite);
        // 创建读取的数据流对象
        QDataStream readStream(&buffer);
        // 创建图片容器
        QImage img;
        // 将数据写入图片容器
        readStream >> img;
        // 将读取的图片设置到图片显示位置
        ui->labelImg->setPixmap(QPixmap::fromImage(img));
    }
    // 将共享内存解锁
    m_sharedMemory.unlock();
}

void CMainWindowMemSender::on_btnCreateShareSize_clicked()
{
    if(m_sharedMemory.key().isEmpty()) {
        QMessageBox::information(this, u8"提示", u8"共享内存key为空,创建失败!");
        return;
    }
    int size = ui->spinBoxSize->value();
    if(0 < size && 0 == m_sharedMemory.size()) {
        if(!m_sharedMemory.create(size)) {
            QMessageBox::information(this, u8"提示", u8"共享内存创建失败!");
        }
    }
    else {
        QMessageBox::information(this, u8"提示", u8"指定创建内存大小小于0或已创建共享内存");
    }
}

void CMainWindowMemSender::on_btnBindShareKey_clicked()
{
    // 当前key为空进入
    if(m_sharedMemory.key().isEmpty())
    {
        // 获取需要指定的key,并判断key是否为空
        QString keyStr= ui->lineEditKey->text();
        if(keyStr.isEmpty()) {
            QMessageBox::information(this, u8"提示", u8"指定Key为空!");
            ui->labelImg->setPixmap(QPixmap());
            return;
        }
        // 设置key
        m_sharedMemory.setKey(keyStr);
    }
    else {
        QMessageBox::information(this, u8"提示", u8"已指定Key!");
    }
}


CMainWindowMemSender.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindowMemSender</class>
 <widget class="QMainWindow" name="CMainWindowMemSender">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>417</width>
    <height>498</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0" colspan="2">
     <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
      <item>
       <widget class="QLineEdit" name="lineEditKey">
        <property name="placeholderText">
         <string>请输入指定的共享内存Key</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnBindShareKey">
        <property name="text">
         <string>绑定共享内存Key</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item row="1" column="0" colspan="2">
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <widget class="QSpinBox" name="spinBoxSize">
        <property name="minimum">
         <number>1</number>
        </property>
        <property name="maximum">
         <number>999999999</number>
        </property>
        <property name="value">
         <number>1024</number>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnCreateShareSize">
        <property name="text">
         <string>创建共享内存</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item row="2" column="0">
     <widget class="QPushButton" name="btnShareData">
      <property name="text">
       <string>共享数据</string>
      </property>
     </widget>
    </item>
    <item row="2" column="1">
     <widget class="QPushButton" name="btnReadData">
      <property name="text">
       <string>读取数据</string>
      </property>
     </widget>
    </item>
    <item row="3" column="0" colspan="2">
     <widget class="QTabWidget" name="tabWidget">
      <property name="currentIndex">
       <number>0</number>
      </property>
      <widget class="QWidget" name="tab">
       <attribute name="title">
        <string>文本数据</string>
       </attribute>
       <layout class="QHBoxLayout" name="horizontalLayout_3">
        <item>
         <widget class="QTextEdit" name="textEdit"/>
        </item>
       </layout>
      </widget>
      <widget class="QWidget" name="tab_2">
       <attribute name="title">
        <string>图片数据</string>
       </attribute>
       <layout class="QHBoxLayout" name="horizontalLayout_4">
        <item>
         <widget class="QLabel" name="labelImg">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>417</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>

读取者

CMainWindowMemReceiver.h

#ifndef CMAINWINDOWMEMRECEIVER_H
#define CMAINWINDOWMEMRECEIVER_H

#include <QBuffer>
#include <QMainWindow>
#include <QSharedMemory>

namespace Ui {
class CMainWindowMemReceiver;
}

class CMainWindowMemReceiver : public QMainWindow
{
    Q_OBJECT

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

private:
    /**
     * @brief judgeSize 判断共享内存与传入值的大小比较,若是传入值大于共享内存则弹出指定提示文本
     * @param dataSize 传入的数据大小
     * @param note 提示文本
     * @return 共享内存与传入值的大小比较
     */
    bool judgeSize(qint64 dataSize, const QString &note);

private slots:
    /**
     * @brief on_btnShareData_clicked 数据共享按钮
     */
    void on_btnShareData_clicked();

    /**
     * @brief on_btnReadData_clicked 数据读取按钮
     */
    void on_btnReadData_clicked();

    /**
     * @brief on_btnBindShareKey_clicked 绑定共享内存key按钮
     */
    void on_btnBindShareKey_clicked();

private:
    Ui::CMainWindowMemReceiver *ui;

    QSharedMemory   m_sharedMemory;    // 共享内存对象
};

#endif // CMAINWINDOWMEMRECEIVER_H

CMainWindowMemReceiver.cpp

#include "CMainWindowMemReceiver.h"
#include "ui_CMainWindowMemReceiver.h"

#include <QMessageBox>
#include <QImage>
#include <QPixmap>
#include <QDataStream>
#include <QBuffer>
#include <QFileDialog>
#include <QStandardPaths>

CMainWindowMemReceiver::CMainWindowMemReceiver(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindowMemReceiver)
{
    ui->setupUi(this);
    // 设置标题
    this->setWindowTitle(u8"共享内存数据接收窗口");
}

CMainWindowMemReceiver::~CMainWindowMemReceiver()
{
    if(m_sharedMemory.size() > 0) {
        // 将共享内存与程序分离
        m_sharedMemory.detach();
    }
    delete ui;
}

bool CMainWindowMemReceiver::judgeSize(qint64 dataSize, const QString &note)
{
    bool ret = m_sharedMemory.size() >= dataSize;
    if(!ret) {
        QMessageBox::information(this, u8"提示", note);
    }
    return ret;
}

void CMainWindowMemReceiver::on_btnShareData_clicked()
{
    // 如果当前进程未挂接共享内存且挂接共享内存失败就进入判断并提示
    if(!m_sharedMemory.isAttached() && !m_sharedMemory.attach()) {
        QMessageBox::information(this, u8"提示", u8"共享内存未挂接成功!");
        return;
    }
    // 共享内存锁定
    m_sharedMemory.lock();
    // 当前为首个tab页签进入(此处是文本类型)
    if(0 == ui->tabWidget->currentIndex()) {
        // 获取共享的文本
        QString str = ui->textEdit->toPlainText();
        // 数据转为UTF-8再获取大小(考虑中文所占字节数的情况)
        qint64 dataSize = str.toUtf8().size();
        // 判断数据是否可以存入共享内存
        if(judgeSize(dataSize, u8"数据拷贝失败,共享内存大小小于数据大小")) {
            // 拷贝数据
            memcpy(m_sharedMemory.data(), str.toStdString().data(), dataSize);
        }
    }
    else {  // 当前共享内容为图片
        // 通过文件弹窗获取图片路径
        QString imgPath = QFileDialog::getOpenFileName
                (this, u8"选择图片"
                 , QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)
                 , "*.png *.jpg");
        // 判断获取的图片路径是否为空
        if(!imgPath.isEmpty()) {
            // 创建buffer对象
            QBuffer buffer;
            // 通过读写方式打开
            buffer.open(QIODevice::ReadWrite);
            // 创建数据流对象,并将流容器指定为创建的buffer对象
            QDataStream writeStream(&buffer);
            // 创建图片对象(方便存入buffer)
            QImage img(imgPath);
            // 将图片数据写入buffer
            writeStream << img;
            // 获取buffer的大小(相当于图片大小)
            qint64 imgSize = buffer.size();
            // 判断数据是否可以存入共享内存
            if(judgeSize(imgSize, u8"数据拷贝失败,共享内存大小小于数据大小")) {
                // 拷贝数据到共享内存
                memcpy(m_sharedMemory.data(), buffer.data().data(), imgSize);
                // 将图片设置到对应的显示区域
                ui->labelImg->setPixmap(QPixmap::fromImage(img));
            }
        }
    }
    // 将共享内存解锁
    m_sharedMemory.unlock();
    // 将共享内存与程序分离
    m_sharedMemory.detach();
}

void CMainWindowMemReceiver::on_btnReadData_clicked()
{
    // 如果当前进程未挂接共享内存且挂接共享内存失败就进入判断并提示
    if(!m_sharedMemory.isAttached() && !m_sharedMemory.attach()) {
        QMessageBox::information(this, u8"提示", u8"共享内存未挂接成功!");
        return;
    }
    // 将共享内存锁定
    m_sharedMemory.lock();
    // 获取共享内存常量指针
    const char *memData = (char *)m_sharedMemory.constData();
    // 获取共享内存大小
    qint64 dataSize = m_sharedMemory.size();
    // 判断索引值,区分图片还是文本
    if(0 == ui->tabWidget->currentIndex()) {
        // 直接创建字节数组对象,并将共享内存常量指针和共享内存大小传入
        QByteArray data(memData, dataSize);
        ui->textEdit->setText(QString(data));
    }
    else {
        // 创建buffer对象
        QBuffer buffer;
        // 设置数据及数据大小
        buffer.setData(memData, dataSize);
        // 以读写方式打开buffer
        buffer.open(QIODevice::ReadWrite);
        // 创建读取的数据流对象
        QDataStream readStream(&buffer);
        // 创建图片容器
        QImage img;
        // 将数据写入图片容器
        readStream >> img;
        // 将读取的图片设置到图片显示位置
        ui->labelImg->setPixmap(QPixmap::fromImage(img));
    }
    // 将共享内存解锁
    m_sharedMemory.unlock();
    // 将共享内存与程序分离
    m_sharedMemory.detach();
}

void CMainWindowMemReceiver::on_btnBindShareKey_clicked()
{
    // 当前key为空进入
    if(m_sharedMemory.key().isEmpty())
    {
        // 获取需要指定的key,并判断key是否为空
        QString keyStr= ui->lineEditKey->text();
        if(keyStr.isEmpty()) {
            QMessageBox::information(this, u8"提示", u8"指定Key为空!");
            ui->labelImg->setPixmap(QPixmap());
            return;
        }
        // 设置key
        m_sharedMemory.setKey(keyStr);
    }
    else {
        QMessageBox::information(this, u8"提示", u8"已指定Key!");
    }
}


CMainWindowMemReceiver.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindowMemReceiver</class>
 <widget class="QMainWindow" name="CMainWindowMemReceiver">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>417</width>
    <height>498</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="2" column="0" colspan="2">
     <widget class="QTabWidget" name="tabWidget">
      <property name="currentIndex">
       <number>0</number>
      </property>
      <widget class="QWidget" name="tab">
       <attribute name="title">
        <string>文本数据</string>
       </attribute>
       <layout class="QHBoxLayout" name="horizontalLayout_3">
        <item>
         <widget class="QTextEdit" name="textEdit"/>
        </item>
       </layout>
      </widget>
      <widget class="QWidget" name="tab_2">
       <attribute name="title">
        <string>图片数据</string>
       </attribute>
       <layout class="QHBoxLayout" name="horizontalLayout_4">
        <item>
         <widget class="QLabel" name="labelImg">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
     </widget>
    </item>
    <item row="1" column="0">
     <widget class="QPushButton" name="btnShareData">
      <property name="text">
       <string>共享数据</string>
      </property>
     </widget>
    </item>
    <item row="0" column="0" colspan="2">
     <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
      <item>
       <widget class="QLineEdit" name="lineEditKey">
        <property name="placeholderText">
         <string>请输入指定的共享内存Key</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnBindShareKey">
        <property name="text">
         <string>绑定共享内存Key</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item row="1" column="1">
     <widget class="QPushButton" name="btnReadData">
      <property name="text">
       <string>读取数据</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>417</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>

总结

在界面操作中,还差一部分的提示/功能逻辑(比如说内存大小在共享数据出现之前就创建了,更好的是共享数据出现时正式创建),有兴趣的小伙伴可自行添加;总的来说,共享内存还是非常实用后续会写其他内存共享方式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP的三次握手以及四次断开

TCP的三次握手和四次断开&#xff0c;就是TCP通信建立连接以及断开的过程 目录 【1】TCP的三次握手过程 ---- TCP建立连接的过程 【2】TCP的四次挥手 ---- TCP会话的断开 注意&#xff1a; 【1】TCP的三次握手过程 ---- TCP建立连接的过程 三次握手的过程&#xff1a…

1.初识typescript

在很多地方的示例代码中使用的都是ts而不是js&#xff0c;为了使用那些示例&#xff0c;学习ts还是有必要的 JS有的TS都有&#xff0c;JS与TS的关系很像css与less ts在运行前需要先编译为js&#xff0c;浏览器不能直接运行ts 目录 1 编译TS的工具包 1.1 安装 1.2 基本…

【BASH】回顾与知识点梳理(二)

【BASH】回顾与知识点梳理 二 二. Shell 的变量功能2.1 什么是变量&#xff1f;2.2 变量的取用与设定: echo, 变量设定规则: set/unset2.3 环境变量的功能用 set 观察所有变量 (含环境变量与自定义变量)export&#xff1a; 自定义变量转成环境变量那如何将环境变量转成自定义变…

Openlayers实战:loadstart和loadend事件

Openlayers开发的项目中,loadstart和loadend事件能很好的显示地图的加载状态,给人明示地图的加载情况,做到不急躁。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhuanlan),还是大剑师兰特(CSDN) * @此源代码版权归大剑师兰特所有,可供学习或商业项目中借鉴,未经授…

Pycharm配置Qt Designer、PyUIC、qrcTOpy

Pycharm配置Qt Designer、PyUIC、qrcTOpy 1 Pycharm配置Qt Designer2 Pycharm配置PyUIC3 Pycharm配置qrcTOpy 1 Pycharm配置Qt Designer 打开pycharm的设置栏&#xff0c;点击外部工具&#xff0c;点击“”&#xff1a; 名称&#xff1a;Qt Designer 程序&#xff1a;.....\e…

从0到1框架搭建,Python+Pytest+Allure+Git+Jenkins接口自动化框架(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口测试是对系统…

纯css实现九宫格图片

本篇文章所分享的内容主要涉及到结构伪类选择器&#xff0c;不熟悉的小伙伴可以了解一下&#xff0c;在常用的css选择器中我也有分享相关内容。 话不多说&#xff0c;接下来我们直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"><head>&l…

Java8实战-总结10

Java8实战-总结10 Lambda表达式类型检查、类型推断以及限制类型检查同样的Lambda,不同的函数式接口类型推断使用局部变量对局部变量的限制 Lambda表达式 类型检查、类型推断以及限制 Lambda表达式可以为函数式接口生成一个实例。然而&#xff0c;Lambda表达式本身并不包含它在…

Android如何用系统签名打包应用

前言 应用使用系统签名可以在用户不需要手动授权的情况下自动获取权限。适合一些定制系统中集成apk的方案商。 步骤 需要在AndroidManifest.xml中添加共享系统进程属性&#xff1a; android:sharedUserId"android.uid.system"如下图所示&#xff1a; 找到系统定制…

给中间固定位置的盒子左右两边添加对称图标方法

给中间固定位置的盒子左右两边添加对称图标方法 【html代码】&#xff1a; <div class"news-content-style mb40px ">//左侧图标盒子<div class"news-arrow left"><a href""><img src"assets/images/index/left-icon…

Nacos配置中心设置Mongodb

目录 1.common模块导入nacos config依赖 2.common模块新建bootstrap.yaml 3.在自己的模块导入common模块依赖 4.打开nacos新建配置&#xff0c;发布 5.运行服务并测试 效果&#xff1a;在部署完成后&#xff0c;其他人可以自动连接到你本地mongoDB数据库&#xff0c;无需再…

自定义el-slider 滑块的样式

最近用到了element组件中的滑块&#xff0c;翻看了官网和网上一些案例&#xff0c;感觉和我要的样式都不太一样&#xff0c;下面记录一下我用到的两种自定义滑块。 效果图 第一种自定义画过的间断点样式 起始样式 滑动的样式 第二种自定义拖动滑块的样式 起始样式 滑动的样…

将程序打包成单一一个可执行文件

最近做了一个界面交互渲染的小项目&#xff0c;项目主要的功能是通过TCP接收数据然后在界面中渲染出对应的状态。由于用户的最大需求是炫酷&#xff0c;于是为了方便实现特殊的交互逻辑&#xff0c;我选择用freeglut自行实现了界面的交互和渲染&#xff0c;又用OpenCV做了部分图…

小区智能电动汽车充电桩如何收费盈利?

摘要&#xff1a;智能用电小区是国家电网为了研究智能电网智能用电的先进技术如何运用于居民区&#xff0c;提高人民的生活水平&#xff0c;提高电网智能化水平以及提升用电服务质量而进行的一项尝试。电动汽车作为智能用电小区建设的一个组成部分同样也逐渐被纳入发展规划&…

kafka部署

1.kafka安装部署 1.1 kafaka下载 https://archive.apache.org/dist/kafka/2.4.0/kafka_2.12-2.4.0.tgz Binary downloads是指预编译的软件包,可供直接下载和安装,无需手动编译。在计算机领域中,二进制下载通常指预构建的软件分发包,可以直接安装在系统上并使用 "2.…

最长连续序列——力扣128

文章目录 题目描述法一 哈希表 题目描述 法一 哈希表 用一个哈希表存储数组中的数&#xff0c;这样查看一个数是否存在即能优化至 O(1) 的时间复杂度 每次在哈希表中检查是否存在 x−1 即能判断是否需要跳过 int longestConsecutive(vector<int>& nums){unordered_s…

CPU利用率过高解决思路

文章目录 问题场景问题定位问题解决 本文参考&#xff1a; Linux服务器之CPU过高解决思路_linux cpu温度过高_Jeremy_Lee123的博客-CSDN博客 Java程序员必备&#xff1a;jstack命令解析 - 掘金 (juejin.cn) 重点问题&#xff01;CPU利用率过高排查思路&#xff5c;原创 (qq.…

【CSS】ios上fixed固定定位的input输入框兼容问题

需求 &#xff1a; 实现一个简单的需求&#xff0c;上方是搜索框并且固定顶部&#xff0c;下方是滚动的内容list 问题 : 若如图上方使用固定定位, 下方用scroll-view, 在安卓上是没有问题的, 但是发现ios上会出现兼容问题 : 问题1: 当content list滚动到中间时再去搜索, 展…

CEC2022:CEC2022测试函数及多种智能优化算法求解CEC2022对比

目录 一、CEC2022测试函数 二、多种智能优化算法求解CEC2022 2.1 本文参与求解CEC2022的智能优化算法 2.2 部分测试函数运行结果与收敛曲线 三、带标记收敛曲线代码(获得代码后可自行更改&#xff09; 一、CEC2022测试函数 CEC2022测试集共有12个单目标测试函数&#x…

实战 01|「编写互动式界面」

前言 实践是最好的学习方式&#xff0c;技术也如此。 文章目录 前言一、功能需求&#xff08;一&#xff09;1、功能需求描述2、知识点3、布局与程序设计 二、功能需求&#xff08;二&#xff09;1、功能需求描述2、知识点1&#xff09;LinearLayout2&#xff09;RelativeLayou…