Qt项目中使用 FieldManager 实现多进程间的字段数据管理

使用场景:

串口收到数据,会将所需要的字段写入FieldManager模块中即更新数据。可能多个接收的报文中分散多个不同字段的结果,实现了解耦。

再一个定时进程中从FieldManager中调用数值,并将数值写入log中实现log的记录。


✅ 一、文件结构

复制代码
YourProject/
├── FieldManager.h      ✅ 字段管理器头文件(线程安全版)
├── main.cpp            ✅ 主函数
├── mainwindow.h/cpp    ✅ 主窗口类
├── mainwindow.ui       ✅ 界面设计(按钮 + 文本框)
├── YourProject.pro     ✅ Qt 项目文件

✅ 二、1. FieldManager.h

线程安全版本 FieldManager.h

cpp 复制代码
#ifndef FIELDMANAGER_H
#define FIELDMANAGER_H

#include <string>
#include <unordered_map>
#include <any>
#include <mutex>
#include <stdexcept>
#include <type_traits>

enum class FieldType {
    U8, S8, U16, S16, U32, S32, FLOAT, DOUBLE
};

class FieldManager {
public:
    static FieldManager& getInstance() {
        static FieldManager instance;
        return instance;
    }

    // 注册字段
    void ADD(const std::string& tag, FieldType type) {
        std::lock_guard<std::mutex> lock(mtx);
        if (fields.find(tag) != fields.end()) return;

        switch (type) {
            case FieldType::U8:     fields[tag] = uint8_t(0); break;
            case FieldType::S8:     fields[tag] = int8_t(0); break;
            case FieldType::U16:    fields[tag] = uint16_t(0); break;
            case FieldType::S16:    fields[tag] = int16_t(0); break;
            case FieldType::U32:    fields[tag] = uint32_t(0); break;
            case FieldType::S32:    fields[tag] = int32_t(0); break;
            case FieldType::FLOAT:  fields[tag] = float(0); break;
            case FieldType::DOUBLE: fields[tag] = double(0); break;
        }
    }

    // 设置字段值
    template<typename T>
    void SET(const std::string& tag, FieldType type, T value) {
        checkTypeMatch<T>(type);
        std::lock_guard<std::mutex> lock(mtx);
        if (fields.find(tag) == fields.end()) throw std::runtime_error("标签未注册");
        fields[tag] = value;
    }

    // 获取字段值
    template<typename T>
    T GET(const std::string& tag, FieldType type) {
        checkTypeMatch<T>(type);
        std::lock_guard<std::mutex> lock(mtx);
        if (fields.find(tag) == fields.end()) throw std::runtime_error("标签未注册");
        return std::any_cast<T>(fields[tag]);
    }

private:
    std::unordered_map<std::string, std::any> fields;
    std::mutex mtx; // 互斥锁保护字段读写

    FieldManager() {}
    FieldManager(const FieldManager&) = delete;
    FieldManager& operator=(const FieldManager&) = delete;

    // 编译期类型检查
    template<typename T>
    void checkTypeMatch(FieldType type) {
        bool match = false;
        switch (type) {
            case FieldType::U8:     match = std::is_same_v<T, uint8_t>; break;
            case FieldType::S8:     match = std::is_same_v<T, int8_t>; break;
            case FieldType::U16:    match = std::is_same_v<T, uint16_t>; break;
            case FieldType::S16:    match = std::is_same_v<T, int16_t>; break;
            case FieldType::U32:    match = std::is_same_v<T, uint32_t>; break;
            case FieldType::S32:    match = std::is_same_v<T, int32_t>; break;
            case FieldType::FLOAT:  match = std::is_same_v<T, float>; break;
            case FieldType::DOUBLE: match = std::is_same_v<T, double>; break;
        }
        if (!match) throw std::invalid_argument("类型与FieldType不匹配");
    }
};

#endif // FIELDMANAGER_H

✅ 示例测试(支持多线程)

cpp 复制代码
#include "FieldManager.h"
#include <thread>
#include <iostream>

void threadFunc() {
    auto& fm = FieldManager::getInstance();
    fm.SET<float>("电压", FieldType::FLOAT, 3.14f);
    std::cout << "线程中设置电压: " << fm.GET<float>("电压", FieldType::FLOAT) << std::endl;
}

int main() {
    auto& fm = FieldManager::getInstance();
    fm.ADD("电压", FieldType::FLOAT);

    std::thread t1(threadFunc);
    std::thread t2(threadFunc);

    t1.join();
    t2.join();

    std::cout << "主线程读取电压: " << fm.GET<float>("电压", FieldType::FLOAT) << std::endl;

    return 0;
}

✅ 输出示例

cpp 复制代码
线程中设置电压: 3.14
线程中设置电压: 3.14
主线程读取电压: 3.14

如何使用:

确保你已经在文件顶部添加:

cpp 复制代码
#include <any>  // ✅ 必需
#include <mutex>
#include <unordered_map>
#include <string>

并确保 Qt 项目启用了 C++17。

CONFIG += c++17


✅ 三、2. mainwindow.ui 设计界面

在 Qt Designer 中拖入以下控件:

控件类型 ObjectName 说明
QPushButton btnSet 设置字段按钮
QPushButton btnGet 获取字段按钮
QLineEdit lineEditValue 输入值
QLabel labelResult 显示读取结果

✅ 四、3. mainwindow.h

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_btnSet_clicked();
    void on_btnGet_clicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

✅ 五、4. mainwindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "FieldManager.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 注册一个字段 "电压",类型为 FLOAT
    FieldManager::getInstance().ADD("电压", FieldType::FLOAT);

    connect(ui->btnSet, &QPushButton::clicked, this, &MainWindow::on_btnSet_clicked);
    connect(ui->btnGet, &QPushButton::clicked, this, &MainWindow::on_btnGet_clicked);
}

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

void MainWindow::on_btnSet_clicked() {
    bool ok;
    float value = ui->lineEditValue->text().toFloat(&ok);
    if (ok) {
        FieldManager::getInstance().SET<float>("电压", FieldType::FLOAT, value);
        ui->labelResult->setText("设置成功");
    } else {
        ui->labelResult->setText("请输入有效数字");
    }
}

void MainWindow::on_btnGet_clicked() {
    try {
        float v = FieldManager::getInstance().GET<float>("电压", FieldType::FLOAT);
        ui->labelResult->setText(QString("读取值: %1").arg(v));
    } catch (std::exception& e) {
        ui->labelResult->setText("读取失败: " + QString(e.what()));
    }
}

✅ 六、5. main.cpp

cpp 复制代码
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

✅ 七、6. YourProject.pro(开启 C++17)

pro 复制代码
QT += core gui widgets

greaterThan(QT_MAJOR_VERSION, 5): QT += widgets

TARGET = YourProject
TEMPLATE = app

CONFIG += c++17

SOURCES += main.cpp \
           mainwindow.cpp

HEADERS += mainwindow.h \
           FieldManager.h

FORMS += mainwindow.ui

✅ 八、编译运行效果

  • 输入一个数字点击【设置】 → 会将其存入 FieldManager"电压" 字段。
  • 点击【获取】 → 会显示之前设置的值。
相关推荐
蓝桉~MLGT7 分钟前
Python学习历程——组织结构(包含for、if、while等等)
开发语言·python·学习
共享家95271 小时前
QT 初识
开发语言·qt
共享家95271 小时前
QT文件解析与乱码问题
开发语言·qt
王嘉俊9251 小时前
Qt 入门:构建跨平台 GUI 应用的强大框架
c语言·开发语言·c++·qt·入门·cpp
小白学大数据1 小时前
Python爬虫技术:招标信息抓取与关键词过滤 (1)
开发语言·爬虫·python
Irene19912 小时前
URLSearchParams :处理 URL 查询参数的接口
开发语言·前端·javascript
Dontla2 小时前
Web典型路由结构之Next.js (App Router, v13+) )(文件系统驱动的路由:File-based Routing)声明式路由:文件即路由
开发语言·前端·javascript
~无忧花开~2 小时前
JavaScript学习笔记(十七):ES6生成器函数详解
开发语言·前端·javascript·笔记·学习·es6·js
电商API_180079052472 小时前
获取淘宝商品视频API接口解析:通过商品链接url获取商品视频item_video
开发语言·爬虫·python·数据挖掘·数据分析
Pocker_Spades_A3 小时前
Python快速入门专业版(五十):Python异常处理:try-except语句(捕获单一与多个异常)
开发语言·python