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"电压" 字段。
  • 点击【获取】 → 会显示之前设置的值。
相关推荐
一只小风华~2 小时前
JavaScript 函数
开发语言·前端·javascript·ecmascript·web
苕皮蓝牙土豆2 小时前
Qt 分裂布局:QSplitter 使用指南
开发语言·qt
Brookty5 小时前
Java线程安全与中断机制详解
java·开发语言·后端·学习·java-ee
從南走到北6 小时前
JAVA东郊到家按摩服务同款同城家政服务按摩私教茶艺师服务系统小程序+公众号+APP+H5
android·java·开发语言·微信小程序·小程序
遇见尚硅谷7 小时前
C语言:20250728学习(指针)
c语言·开发语言·数据结构·c++·笔记·学习·算法
☆璇7 小时前
【C++】C/C++内存管理
c语言·开发语言·c++
愿你天黑有灯下雨有伞7 小时前
枚举策略模式实战:优雅消除支付场景的if-else
java·开发语言·策略模式
网络安全打工人7 小时前
CentOS7 安装 rust 1.82.0
开发语言·后端·rust
楚轩努力变强7 小时前
前端工程化常见问题总结
开发语言·前端·javascript·vue.js·visual studio code
梦想的初衷~8 小时前
MATLAB近红外光谱分析技术及实践技术应用
开发语言·支持向量机·matlab