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"电压" 字段。
  • 点击【获取】 → 会显示之前设置的值。
相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript