Qt项目中使用 FieldManager 实现多进程间的字段数据管理
使用场景:
串口收到数据,会将所需要的字段写入FieldManager模块中即更新数据。可能多个接收的报文中分散多个不同字段的结果,实现了解耦。
再一个定时进程中从FieldManager中调用数值,并将数值写入log中实现log的记录。
✅ 一、文件结构
YourProject/
├── FieldManager.h ✅ 字段管理器头文件(线程安全版)
├── main.cpp ✅ 主函数
├── mainwindow.h/cpp ✅ 主窗口类
├── mainwindow.ui ✅ 界面设计(按钮 + 文本框)
├── YourProject.pro ✅ Qt 项目文件
✅ 二、1. FieldManager.h
线程安全版本 FieldManager.h
#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
✅ 示例测试(支持多线程)
#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;
}
✅ 输出示例
线程中设置电压: 3.14
线程中设置电压: 3.14
主线程读取电压: 3.14
如何使用:
确保你已经在文件顶部添加:
#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
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic: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
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "FieldManager.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 注册一个字段 "电压",类型为 FLOATFieldManager::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
#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)
QT += core gui widgetsgreaterThan(QT_MAJOR_VERSION, 5): QT += widgetsTARGET = YourProject
TEMPLATE = appCONFIG += c++17SOURCES += main.cpp \mainwindow.cppHEADERS += mainwindow.h \FieldManager.hFORMS += mainwindow.ui
✅ 八、编译运行效果
- 输入一个数字点击【设置】 → 会将其存入
FieldManager
的"电压"
字段。 - 点击【获取】 → 会显示之前设置的值。