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"电压" 字段。
  • 点击【获取】 → 会显示之前设置的值。
相关推荐
飞剑神4 分钟前
qt svg缺失元素, 原因是不支持 rgba
开发语言·qt
诗书画唱34 分钟前
【前端面试题】JavaScript 核心知识点解析(第二十二题到第六十一题)
开发语言·前端·javascript
冬天vs不冷35 分钟前
Java基础(九):Object核心类深度剖析
java·开发语言·python
TS的美梦36 分钟前
【1:1复刻R版】python版火山图函数一键出图
开发语言·python·r语言·scanpy·火山图
眠りたいです1 小时前
Qt音频播放器项目实践:文件过滤、元数据提取与动态歌词显示实现
c++·qt·ui·音视频·媒体·qt5·mime
陈天伟教授1 小时前
(二)Python + 地球信息科学与技术 (GeoICT)=?
开发语言·python
七七&55610 小时前
2024年08月13日 Go生态洞察:Go 1.23 发布与全面深度解读
开发语言·网络·golang
java坤坤10 小时前
GoLand 项目从 0 到 1:第八天 ——GORM 命名策略陷阱与 Go 项目启动慢问题攻坚
开发语言·后端·golang
元清加油10 小时前
【Golang】:函数和包
服务器·开发语言·网络·后端·网络协议·golang
健康平安的活着11 小时前
java之 junit4单元测试Mockito的使用
java·开发语言·单元测试