diff --git a/applications/EmsShower/EmsShower.pro b/applications/EmsShower/EmsShower.pro index 97c7e88..9713d93 100644 --- a/applications/EmsShower/EmsShower.pro +++ b/applications/EmsShower/EmsShower.pro @@ -10,18 +10,19 @@ CONFIG += c++17 SOURCES += \ appinit.cpp \ - customwidget.cpp \ + customdisplaypanel.cpp \ formserialportsettingdialog.cpp \ libmodbus/modbus-data.c \ libmodbus/modbus-rtu.c \ libmodbus/modbus-tcp.c \ libmodbus/modbus.c \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + openjson.cpp HEADERS += \ appinit.h \ - customwidget.h \ + customdisplaypanel.h \ formserialportsettingdialog.h \ libmodbus/config.h \ libmodbus/modbus-private.h \ @@ -32,6 +33,7 @@ HEADERS += \ libmodbus/modbus-version.h \ libmodbus/modbus.h \ mainwindow.h \ + openjson.h \ slave_define.h FORMS += \ diff --git a/applications/EmsShower/customdisplaypanel.cpp b/applications/EmsShower/customdisplaypanel.cpp new file mode 100644 index 0000000..9ec8c9b --- /dev/null +++ b/applications/EmsShower/customdisplaypanel.cpp @@ -0,0 +1,417 @@ +#include "customdisplaypanel.h" +#include +#include +#include +#include +#include + +CustomDisplayPanel::CustomDisplayPanel(QWidget *parent) + : QWidget{parent} + ,m_label_count{5},m_table_rows_count{10},m_table_rols_count{2},m_imagePath{":/icons/home.png"},m_pTableWidget(nullptr) +{ +} + +void CustomDisplayPanel::Build() +{ + // **让 CustomWidget 背景透明,避免遮挡阴影** + this->setAttribute(Qt::WA_TranslucentBackground); + this->setStyleSheet("background: transparent;"); + + // **创建一个子容器 `containerWidget`,所有内容都放在这里** + QWidget *containerWidget = new QWidget(this); + containerWidget->setStyleSheet("background-color: white; border-radius: 10px;"); + + // **为 `containerWidget` 添加阴影** + QGraphicsDropShadowEffect *shadowMain = new QGraphicsDropShadowEffect(this); + shadowMain->setBlurRadius(10); + shadowMain->setOffset(5, 5); + shadowMain->setColor(QColor(0, 0, 0, 120)); // 半透明黑色 + containerWidget->setGraphicsEffect(shadowMain); + + // **主垂直布局** + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(containerWidget); + mainLayout->setContentsMargins(10, 10, 10, 10); // 预留空间显示阴影 + this->setLayout(mainLayout); + + // **容器内的布局** + QVBoxLayout *containerLayout = new QVBoxLayout(containerWidget); + + // **上半部分(固定高度 200)** + QWidget *topWidget = new QWidget(containerWidget); + topWidget->setFixedHeight(200); + topWidget->setStyleSheet("background-color: white; border-radius: 10px;"); + + // **应用阴影效果** + QGraphicsDropShadowEffect *shadowTop = new QGraphicsDropShadowEffect(this); + shadowTop->setBlurRadius(10); + shadowTop->setOffset(5, 5); + shadowTop->setColor(QColor(0, 0, 0, 100)); // 半透明黑色 + topWidget->setGraphicsEffect(shadowTop); + + QHBoxLayout *topLayout = new QHBoxLayout(topWidget); + + // **左侧布局(上下排列:64x64 图片 + QLabel)** + QVBoxLayout *leftLayout = new QVBoxLayout(); + QLabel *imageLabel = new QLabel(topWidget); + QPixmap pixmap(m_imagePath); // 替换为实际图片路径 + imageLabel->setPixmap(pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + int width = 120; + imageLabel->setFixedSize(width, 64); + imageLabel->setAlignment(Qt::AlignCenter); + + QLabel *textLabel = new QLabel(m_mainLabel, topWidget); + textLabel->setAlignment(Qt::AlignCenter); + textLabel->setStyleSheet( + "QLabel {" + //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + " font-family: 'Arial';" + " font-size: 20px;" + " color: #13396E;" + " font-weight: bold;" + " font-style: black;" + "}" + ); + textLabel->setFixedSize(width, 40); + + leftLayout->addWidget(imageLabel); + leftLayout->addWidget(textLabel); + leftLayout->setAlignment(Qt::AlignCenter); + + // **右侧布局:包含 5 个 QLabel** + // 加载字体 + int fontId = QFontDatabase::addApplicationFont(":/fonts/Alimama_DongFangDaKai_Regular.ttf"); + if (fontId == -1) + { + qDebug() << "字体加载失败"; + } + + QVBoxLayout *rightLayout = new QVBoxLayout(); + for (int i = 0; i < m_label_count; ++i) + { + QLabel *label = new QLabel(m_lables[i], topWidget); + label->setStyleSheet( + "QLabel {" + //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + " font-family: 'Arial';" + " font-size: 20px;" + " color: #2E86C1;" + " font-weight: bold;" + "}" + ); + label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + rightLayout->addWidget(label); + m_txtLabels.push_back(label); + } + + // **组装上半部分布局** + QWidget *leftWidget = new QWidget(topWidget); + leftWidget->setLayout(leftLayout); + + QWidget *rightWidget = new QWidget(topWidget); + rightWidget->setLayout(rightLayout); + + topLayout->addWidget(leftWidget, 1); // 1/3 宽度 + topLayout->addWidget(rightWidget, 4); // 2/3 宽度 + containerLayout->addWidget(topWidget,1); + + // **下半部分:2 列 5 行表格** + QTableWidget *tableWidget = new QTableWidget(m_table_rows_count, m_table_rols_count, containerWidget); + m_pTableWidget = tableWidget; + + // tableWidget->setHorizontalHeaderLabels({"Signal", "Value"}); + + // 设置列宽策略 + tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); + tableWidget->setColumnWidth(0, 150); + tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + //tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + //tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + // 隐藏所有表头 + tableWidget->horizontalHeader()->setVisible(false); // 隐藏水平表头[1](@ref) + tableWidget->verticalHeader()->setVisible(false); // 隐藏垂直表头[1](@ref) + + // 禁止表格编辑 + tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // 设置网格线样式 + tableWidget->setShowGrid(true); // 默认显示网格线[3](@ref) + tableWidget->setStyleSheet("QTableWidget { gridline-color: #e0e0e0; background-color: white; border-radius: 10px;}"); + + // **应用阴影效果** + QGraphicsDropShadowEffect *shadowTable = new QGraphicsDropShadowEffect(this); + shadowTable->setBlurRadius(10); + shadowTable->setOffset(5, 5); + shadowTable->setColor(QColor(0, 0, 0, 100)); + tableWidget->setGraphicsEffect(shadowTable); + + containerLayout->addWidget(tableWidget,1); + m_pTableWidget->setRowCount(0); +} + + +void CustomDisplayPanel::UpdateData(OpenJson& json) +{ + int panel_type = json["panel_type"].i32(); + qDebug() << panel_type; + + if (panel_type == CustomDisplayPanel::PANEL_TEMPERATURE) + { + UpdateTemperature(json); + } + else if (panel_type == CustomDisplayPanel::PANEL_ALARM) + { + UpdateAlarm(json); + } +} + +void CustomDisplayPanel::UpdateTemperature(OpenJson &json) +{ + auto& nodeLabel = json["text_panel"]; + qDebug() << nodeLabel.size(); + for(size_t i=0; isetText(disp); + } + + auto& nodeTable = json["table"]; + qDebug() << nodeTable.size(); + + m_pTableWidget->clear(); + m_pTableWidget->setRowCount(nodeTable.size()); + + // 加载字体 + int fontId = QFontDatabase::addApplicationFont(":/fonts/Alimama_DongFangDaKai_Regular.ttf"); + if (fontId == -1) + { + qDebug() << "字体加载失败"; + } + + for(size_t i=0; isetTextAlignment(Qt::AlignRight | Qt::AlignVCenter); // 右对齐+垂直居中 + // 仅第一列设置特殊字体 + + QFont font; + //font.setFamily("Alimama DongFangDaKai"); + font.setFamily("Arial"); + font.setPixelSize(20); + font.setBold(true); + item->setFont(font); + //item->setForeground(QBrush(QColor("#2E86C1"))); + item->setForeground(QColor(Qt::red)); + + m_pTableWidget->setItem(i, col++, item); + + // **填充表格数据** + QTableWidgetItem *item2 = new QTableWidgetItem(QString::fromStdString(value)); + item2->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); // 右对齐+垂直居中 + + QFont font2; + font2.setFamily("Arial"); + font2.setPixelSize(20); + //font2.setBold(true); + item2->setFont(font2); + //item2->setForeground(QBrush(QColor("#2E86C1"))); + item2->setForeground(QColor(Qt::red)); + + m_pTableWidget->setItem(i, col, item2); + } +} + +void CustomDisplayPanel::UpdateAlarm(OpenJson &json) +{ + qDebug() << "更新告警信息"; + QFont font; + font.setFamily("Arial"); + font.setPixelSize(16); + font.setBold(false); + + auto& nodeTable = json["alarm"]; + qDebug() << nodeTable.size(); + for(size_t i=0; iinsertRow(0); + auto& node = nodeTable[i]; + std::string s = node["time"].s(); + std::string title = node["signal"].s(); + std::string value = node["value"].s(); + + int col = 0; + QTableWidgetItem *item0 = new QTableWidgetItem(QString::fromStdString(s)); + item0->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); // 右对齐+垂直居中 + QTableWidgetItem *item1 = new QTableWidgetItem(QString::fromStdString(title)); + QTableWidgetItem *item2 = new QTableWidgetItem(QString::fromStdString(value)); + + item0->setFont(font); + item1->setFont(font); + item2->setFont(font); + item0->setForeground(QBrush(QColor("#2E86C1"))); + item1->setForeground(QBrush(QColor("#2E86C1"))); + item2->setForeground(QBrush(QColor("#2E86C1"))); + + m_pTableWidget->setItem(0, 0, item0); + m_pTableWidget->setItem(0, 1, item1); + m_pTableWidget->setItem(0, 2, item2); + } +} + +//////// +/// \brief CustomWarningPanel::CustomWarningPanel +/// +/// +CustomWarningPanel::CustomWarningPanel(QWidget *parent) + : CustomDisplayPanel{parent} +{ + +} + +void CustomWarningPanel::Build() +{ + // **让 CustomWidget 背景透明,避免遮挡阴影** + this->setAttribute(Qt::WA_TranslucentBackground); + this->setStyleSheet("background: transparent;"); + + // **创建一个子容器 `containerWidget`,所有内容都放在这里** + QWidget *containerWidget = new QWidget(this); + containerWidget->setStyleSheet("background-color: white; border-radius: 10px;"); + + // **为 `containerWidget` 添加阴影** + QGraphicsDropShadowEffect *shadowMain = new QGraphicsDropShadowEffect(this); + shadowMain->setBlurRadius(10); + shadowMain->setOffset(5, 5); + shadowMain->setColor(QColor(0, 0, 0, 120)); // 半透明黑色 + containerWidget->setGraphicsEffect(shadowMain); + + // **主垂直布局** + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(containerWidget); + mainLayout->setContentsMargins(10, 10, 10, 10); // 预留空间显示阴影 + this->setLayout(mainLayout); + + // **容器内的布局** + QVBoxLayout *containerLayout = new QVBoxLayout(containerWidget); + + // **上半部分(固定高度 200)** + QWidget *topWidget = new QWidget(containerWidget); + topWidget->setFixedHeight(100); + topWidget->setStyleSheet("background-color: white; border-radius: 10px;"); + + // **应用阴影效果** + QGraphicsDropShadowEffect *shadowTop = new QGraphicsDropShadowEffect(this); + shadowTop->setBlurRadius(10); + shadowTop->setOffset(5, 5); + shadowTop->setColor(QColor(0, 0, 0, 100)); // 半透明黑色 + topWidget->setGraphicsEffect(shadowTop); + + QHBoxLayout *topLayout = new QHBoxLayout(topWidget); + + // **左侧布局(上下排列:64x64 图片 + QLabel)** + QVBoxLayout *leftLayout = new QVBoxLayout(); + QLabel *imageLabel = new QLabel(topWidget); + QPixmap pixmap(m_imagePath); // 替换为实际图片路径 + imageLabel->setPixmap(pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + int width = 80; + imageLabel->setFixedSize(width, 80); + imageLabel->setAlignment(Qt::AlignCenter); + + leftLayout->addWidget(imageLabel); + leftLayout->setAlignment(Qt::AlignCenter); + + // **右侧布局:包含 5 个 QLabel** + // 加载字体 + int fontId = QFontDatabase::addApplicationFont(":/fonts/Alimama_DongFangDaKai_Regular.ttf"); + if (fontId == -1) + { + qDebug() << "字体加载失败"; + } + + QVBoxLayout *rightLayout = new QVBoxLayout(); + for (int i = 0; i < m_label_count; ++i) + { + QLabel *label = new QLabel(m_lables[i], topWidget); + label->setStyleSheet( + "QLabel {" + //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + " font-family: 'Arial';" + " font-size: 20px;" + " color: #2E86C1;" + " font-weight: bold;" + "}" + ); + label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + rightLayout->addWidget(label); + m_txtLabels.push_back(label); + } + + // **组装上半部分布局** + QWidget *leftWidget = new QWidget(topWidget); + leftWidget->setLayout(leftLayout); + + QWidget *rightWidget = new QWidget(topWidget); + rightWidget->setLayout(rightLayout); + + topLayout->addWidget(leftWidget, 1); // 1/3 宽度 + topLayout->addWidget(rightWidget, 4); // 2/3 宽度 + containerLayout->addWidget(topWidget,1); + + // **下半部分:2 列 5 行表格** + QTableWidget *tableWidget = new QTableWidget(m_table_rows_count, m_table_rols_count, containerWidget); + m_pTableWidget = tableWidget; + + // tableWidget->setHorizontalHeaderLabels({"Signal", "Value"}); + + // 设置列宽策略 + tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); + tableWidget->setColumnWidth(0, 150); + tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + //tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + //tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + // 隐藏所有表头 + tableWidget->horizontalHeader()->setVisible(false); // 隐藏水平表头[1](@ref) + tableWidget->verticalHeader()->setVisible(false); // 隐藏垂直表头[1](@ref) + + // 禁止表格编辑 + tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // 设置网格线样式 + tableWidget->setShowGrid(true); // 默认显示网格线[3](@ref) + tableWidget->setStyleSheet("QTableWidget { gridline-color: #e0e0e0; background-color: white; border-radius: 10px;}"); + + // **应用阴影效果** + QGraphicsDropShadowEffect *shadowTable = new QGraphicsDropShadowEffect(this); + shadowTable->setBlurRadius(10); + shadowTable->setOffset(5, 5); + shadowTable->setColor(QColor(0, 0, 0, 100)); + tableWidget->setGraphicsEffect(shadowTable); + + containerLayout->addWidget(tableWidget,1); + m_pTableWidget->setRowCount(0); +} diff --git a/applications/EmsShower/customdisplaypanel.h b/applications/EmsShower/customdisplaypanel.h new file mode 100644 index 0000000..3e1ac55 --- /dev/null +++ b/applications/EmsShower/customdisplaypanel.h @@ -0,0 +1,106 @@ +#ifndef CUSTOMDISPLAYPANEL_H +#define CUSTOMDISPLAYPANEL_H + +#include +#include +#include +#include +#include +#include + +#include "openjson.h" + +/** + * 创建一个自定义的显示面板 + * 上半部分的左边为图示,右边为显示的主要KPI指标标签 + * 下半部为表格,显示主要的参数 +*/ + +class CustomDisplayPanel : public QWidget +{ + Q_OBJECT +public: + explicit CustomDisplayPanel(QWidget *parent = nullptr); + +protected: + int m_label_count; + int m_table_rows_count; + int m_table_rols_count; + QStringList m_lables; + QStringList m_rowItems; + QString m_imagePath; + QString m_mainLabel; + + QVector m_txtLabels; + QTableWidget* m_pTableWidget; + +public: + typedef enum _tagPanelType: int + { + PANEL_TEMPERATURE = 1, + PANEL_BATTERY, + PANEL_POWER, + PANEL_AC, + PANEL_PV, + PANEL_ALARM, + PANEL_INVERTER, + PANEL_HOME, + } PanelType; + +public: + void setImage(const QString& img) + { + m_imagePath = img; + } + + //设置显示指标和表格数据,其中显示的标签数量需要跟QStringlist的数量一致 + void setLableCount(int number = 5) + { + m_label_count = number; + }; + void setTableRolCount(int number = 2) + { + m_table_rols_count = number; + } + void setTableRowsCount(int number = 10) + { + m_table_rows_count = number; + }; + + void setLables(const QStringList& sl) + { + m_lables = sl; + } + void setRowItems(const QStringList& sl) + { + m_rowItems = sl; + } + //设置Panel的标题 + void setMainLabel(QString label) + { + m_mainLabel = label; + } + + virtual void Build(); + +public: + //更新数据 + void UpdateData(OpenJson& json); + + void UpdateTemperature(OpenJson& json); + void UpdateAlarm(OpenJson& json); +public: + + +signals: +}; + +class CustomWarningPanel : public CustomDisplayPanel +{ + Q_OBJECT +public: + explicit CustomWarningPanel(QWidget *parent = nullptr); + virtual void Build(); +}; + +#endif // CUSTOMDISPLAYPANEL_H diff --git a/applications/EmsShower/customwidget.cpp b/applications/EmsShower/customwidget.cpp deleted file mode 100644 index 1a47df8..0000000 --- a/applications/EmsShower/customwidget.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "customwidget.h" -#include -#include -#include -#include -#include -#include - -CustomWidget::CustomWidget(QWidget *parent) - : QWidget{parent} -{ - QWidget *centralWidget = new QWidget(); - QGridLayout *mainLayout = new QGridLayout(this); - mainLayout->addWidget(centralWidget); - - //实例阴影shadow - QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this); - //设置阴影距离 - shadow->setOffset(0, 0); - //设置阴影颜色 - shadow->setColor(QColor(44,44,44)); - //设置阴影圆角 - shadow->setBlurRadius(16); - //给嵌套QDialog设置阴影 - centralWidget->setGraphicsEffect(shadow); - - QVBoxLayout *mainLayout1 = new QVBoxLayout(centralWidget); - mainLayout1->setSpacing(5); // 控件间间隔 - - // 上1/3:QLabel - QWidget *topWidget = new QWidget(); - mainLayout1->addWidget(topWidget, 1); // 拉伸因子为1 - setupTopSection(topWidget); - - // 中1/3:两个垂直排列的QTextEdit - QWidget *midWidget = new QWidget(); - mainLayout1->addWidget(midWidget, 1); - setupMidSection(midWidget); - - // 下1/3:表格 - QWidget *bottomWidget = new QWidget(); - mainLayout1->addWidget(bottomWidget, 1); - setupBottomSection(bottomWidget); -} - -void CustomWidget::setupTopSection(QWidget *parent) -{ - //实例阴影shadow - QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this); - //设置阴影距离 - shadow->setOffset(0, 0); - //设置阴影颜色 - shadow->setColor(QColor(244,44,44)); - //设置阴影圆角 - shadow->setBlurRadius(16); - - QLabel *label = new QLabel("顶部标签", parent); - label->setAlignment(Qt::AlignCenter); - label->setGraphicsEffect(shadow); - - QVBoxLayout *layout = new QVBoxLayout(parent); - layout->addWidget(label); -} - -void CustomWidget::setupMidSection(QWidget *parent) -{ - //实例阴影shadow - QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this); - //设置阴影距离 - shadow->setOffset(0, 0); - //设置阴影颜色 - shadow->setColor(QColor(244,44,44)); - //设置阴影圆角 - shadow->setBlurRadius(16); - - QVBoxLayout *layout = new QVBoxLayout(parent); - QTextEdit *textEdit1 = new QTextEdit(); - QTextEdit *textEdit2 = new QTextEdit(); - layout->addWidget(textEdit1, 1); // 各占中间区域的一半高度 - layout->addWidget(textEdit2, 1); -} - -void CustomWidget::setupBottomSection(QWidget *parent) -{ - //实例阴影shadow - QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this); - //设置阴影距离 - shadow->setOffset(0, 0); - //设置阴影颜色 - shadow->setColor(QColor(244,44,44)); - //设置阴影圆角 - shadow->setBlurRadius(16); - - QTableWidget *table = new QTableWidget(5, 2, parent); // 5行2列 - table->setHorizontalHeaderLabels({"列1", "列2"}); - table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // 列宽自适应 - table->setGraphicsEffect(shadow); - QVBoxLayout *layout = new QVBoxLayout(parent); - layout->addWidget(table); -} diff --git a/applications/EmsShower/customwidget.h b/applications/EmsShower/customwidget.h deleted file mode 100644 index bc0a364..0000000 --- a/applications/EmsShower/customwidget.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CUSTOMWIDGET_H -#define CUSTOMWIDGET_H - -#include - - -class CustomWidget : public QWidget -{ - Q_OBJECT -public: - explicit CustomWidget(QWidget *parent = nullptr); - -private: - void setupTopSection(QWidget *parent); - void setupMidSection(QWidget *parent); - void setupBottomSection(QWidget *parent); - -signals: -}; - -#endif // CUSTOMWIDGET_H diff --git a/applications/EmsShower/icons/logo-en.png b/applications/EmsShower/icons/logo-en.png new file mode 100644 index 0000000..9e761e7 Binary files /dev/null and b/applications/EmsShower/icons/logo-en.png differ diff --git a/applications/EmsShower/icons/main_UPS.png b/applications/EmsShower/icons/main_UPS.png new file mode 100644 index 0000000..b98ce80 Binary files /dev/null and b/applications/EmsShower/icons/main_UPS.png differ diff --git a/applications/EmsShower/icons/main_ac.png b/applications/EmsShower/icons/main_ac.png new file mode 100644 index 0000000..01c74e3 Binary files /dev/null and b/applications/EmsShower/icons/main_ac.png differ diff --git a/applications/EmsShower/icons/main_alarm.png b/applications/EmsShower/icons/main_alarm.png new file mode 100644 index 0000000..9043bda Binary files /dev/null and b/applications/EmsShower/icons/main_alarm.png differ diff --git a/applications/EmsShower/icons/main_battery.png b/applications/EmsShower/icons/main_battery.png new file mode 100644 index 0000000..30fc98e Binary files /dev/null and b/applications/EmsShower/icons/main_battery.png differ diff --git a/applications/EmsShower/icons/main_cab.png b/applications/EmsShower/icons/main_cab.png new file mode 100644 index 0000000..11b0e3b Binary files /dev/null and b/applications/EmsShower/icons/main_cab.png differ diff --git a/applications/EmsShower/icons/main_door.png b/applications/EmsShower/icons/main_door.png new file mode 100644 index 0000000..9720c38 Binary files /dev/null and b/applications/EmsShower/icons/main_door.png differ diff --git a/applications/EmsShower/icons/main_fan.png b/applications/EmsShower/icons/main_fan.png new file mode 100644 index 0000000..de3e9e5 Binary files /dev/null and b/applications/EmsShower/icons/main_fan.png differ diff --git a/applications/EmsShower/icons/main_fire.png b/applications/EmsShower/icons/main_fire.png new file mode 100644 index 0000000..01fd599 Binary files /dev/null and b/applications/EmsShower/icons/main_fire.png differ diff --git a/applications/EmsShower/icons/main_invertor.png b/applications/EmsShower/icons/main_invertor.png new file mode 100644 index 0000000..340b9a8 Binary files /dev/null and b/applications/EmsShower/icons/main_invertor.png differ diff --git a/applications/EmsShower/icons/main_multi_cab.png b/applications/EmsShower/icons/main_multi_cab.png new file mode 100644 index 0000000..f5b123d Binary files /dev/null and b/applications/EmsShower/icons/main_multi_cab.png differ diff --git a/applications/EmsShower/icons/main_peidian.png b/applications/EmsShower/icons/main_peidian.png new file mode 100644 index 0000000..99c05ff Binary files /dev/null and b/applications/EmsShower/icons/main_peidian.png differ diff --git a/applications/EmsShower/icons/main_power.png b/applications/EmsShower/icons/main_power.png new file mode 100644 index 0000000..c23811e Binary files /dev/null and b/applications/EmsShower/icons/main_power.png differ diff --git a/applications/EmsShower/icons/main_pv.png b/applications/EmsShower/icons/main_pv.png new file mode 100644 index 0000000..13e3dbb Binary files /dev/null and b/applications/EmsShower/icons/main_pv.png differ diff --git a/applications/EmsShower/icons/main_temp.png b/applications/EmsShower/icons/main_temp.png new file mode 100644 index 0000000..0d11fff Binary files /dev/null and b/applications/EmsShower/icons/main_temp.png differ diff --git a/applications/EmsShower/icons/main_thunder.png b/applications/EmsShower/icons/main_thunder.png new file mode 100644 index 0000000..4016c55 Binary files /dev/null and b/applications/EmsShower/icons/main_thunder.png differ diff --git a/applications/EmsShower/icons/main_video.png b/applications/EmsShower/icons/main_video.png new file mode 100644 index 0000000..22db4ac Binary files /dev/null and b/applications/EmsShower/icons/main_video.png differ diff --git a/applications/EmsShower/icons/main_water.png b/applications/EmsShower/icons/main_water.png new file mode 100644 index 0000000..f0ab7d5 Binary files /dev/null and b/applications/EmsShower/icons/main_water.png differ diff --git a/applications/EmsShower/mainwindow.cpp b/applications/EmsShower/mainwindow.cpp index 4b2f918..45aeca2 100644 --- a/applications/EmsShower/mainwindow.cpp +++ b/applications/EmsShower/mainwindow.cpp @@ -12,10 +12,11 @@ #include #include #include +#include #include "libmodbus/modbus.h" -#include "customwidget.h" +#include "customdisplaypanel.h" #include "formserialportsettingdialog.h" @@ -91,7 +92,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) ,m_pModbus(nullptr) + ,m_pTimer(nullptr) ,m_pSettings(nullptr) + ,m_bInitializeModbus(false) + { ui->setupUi(this); @@ -203,9 +207,11 @@ void MainWindow::getConfiguration(QString iniFilePath) bool MainWindow::InitializeUI() { #ifndef NDEBUG - this->showMaximized(); + //this->showMaximized(); #endif + ui->statusbar->showMessage(tr("Ready"), 0); + //初始化窗口边框 QWidget *centralWidget = this->centralWidget(); // new QWidget(this); QGridLayout *mainLayout = new QGridLayout(centralWidget); @@ -231,27 +237,102 @@ bool MainWindow::InitializeUI() //给垂直布局器设置边距(此步很重要, 设置宽度为阴影的宽度) //->setMargin(12); - // 创建2行4列布局(共8个控件示例) - for(int row=0; row<2; ++row) - { - for(int col=0; col<4; ++col) - { -#if 0 - QLabel *label = new QLabel(QString("Cell %1-%2").arg(row).arg(col),this); - label->setStyleSheet("background-color: rgb(192,192,192);" - "color: #333333;" - "border-radius: 8px;" - "font: bold 14px;"); - label->setAutoFillBackground(true); - label->setAlignment(Qt::AlignCenter); - label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - mainLayout->addWidget(label, row, col); - m_panel_labels.push_back(label); -#endif - CustomWidget* pWidget = new CustomWidget(this); - mainLayout->addWidget(pWidget, row, col); - } - } + //温度 + m_pTemperaturePanel = new CustomDisplayPanel(this); + m_pTemperaturePanel->setImage(":/icons/main_temp.png"); + QStringList l1{tr("Online"),tr("Temperature"),tr("Humidity")}; + m_pTemperaturePanel->setLableCount(l1.count()); + m_pTemperaturePanel->setTableRowsCount(10); + m_pTemperaturePanel->setLables(l1); + m_pTemperaturePanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pTemperaturePanel->setMainLabel(tr("Temp&RH")); + m_pTemperaturePanel->Build(); + mainLayout->addWidget(m_pTemperaturePanel, 0, 0); + + //电源 + m_pPowerPanel = new CustomDisplayPanel(this); + m_pPowerPanel->setImage(":/icons/main_power.png"); + QStringList l2{QStringList{tr("Online"),tr("​Input Voltage"),tr("​Output Voltage"),tr("​Output Current"),tr("Module Temp.")}}; + m_pPowerPanel->setLableCount(l2.count()); + m_pPowerPanel->setTableRowsCount(10); + m_pPowerPanel->setLables(l2); + m_pPowerPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pPowerPanel->setMainLabel(tr("Power")); + m_pPowerPanel->Build(); + mainLayout->addWidget(m_pPowerPanel, 0, 1); + + //电池 + m_pBatteryPanel = new CustomDisplayPanel(this); + m_pBatteryPanel->setImage(":/icons/main_battery.png"); + QStringList l3{QStringList{tr("Online"),tr("SOC"),tr("SOH"),tr("Group Voltage"),tr("Cell V.Avg"),tr("Cell V.Max"),tr("Cell V.Min")}}; + m_pBatteryPanel->setLableCount(l3.count()); + m_pBatteryPanel->setTableRowsCount(10); + m_pBatteryPanel->setLables(l3); + m_pBatteryPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pBatteryPanel->setMainLabel(tr("​Battery Pack")); + m_pBatteryPanel->Build(); + mainLayout->addWidget(m_pBatteryPanel, 0, 2); + + //空调 + m_pACPanel = new CustomDisplayPanel(this); + m_pACPanel->setImage(":/icons/main_ac.png"); + QStringList l4{QStringList{tr("Online"),tr("Machine"),tr("IFM"),tr("Compressor"),tr("OFM"),tr("Discharge Temp."),tr("Room Temp.")}}; + m_pACPanel->setLableCount(l4.count()); + m_pACPanel->setTableRowsCount(10); + m_pACPanel->setLables(l4); + m_pACPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pACPanel->setMainLabel(tr("HVACR")); + m_pACPanel->Build(); + mainLayout->addWidget(m_pACPanel, 0, 3); + + //交流配电 + m_pInverterPanel = new CustomDisplayPanel(this); + m_pInverterPanel->setImage(":/icons/main_invertor.png"); + QStringList l5{QStringList{tr("Online"),tr("​Input Voltage"),tr("​Input Current"),tr("Module Temp.")}}; + m_pInverterPanel->setLableCount(l5.count()); + m_pInverterPanel->setTableRowsCount(10); + m_pInverterPanel->setLables(l5); + m_pInverterPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pInverterPanel->setMainLabel(tr("AC Power")); + m_pInverterPanel->Build(); + mainLayout->addWidget(m_pInverterPanel, 1, 0); + + //PV太阳能 + m_pPVPanel = new CustomDisplayPanel(this); + m_pPVPanel->setImage(":/icons/main_pv.png"); + QStringList l6{QStringList{tr("Online"),tr("​Output Voltage"),tr("​Output Current"),tr("Module Temp.")}}; + m_pPVPanel->setLableCount(l6.count()); + m_pPVPanel->setTableRowsCount(10); + m_pPVPanel->setLables(l6); + m_pPVPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pPVPanel->setMainLabel(tr("​PV Module")); + m_pPVPanel->Build(); + mainLayout->addWidget(m_pPVPanel, 1, 1); + + //门禁 + m_pHomePanel = new CustomDisplayPanel(this); + m_pHomePanel->setImage(":/icons/main_cab.png"); + QStringList l7{QStringList{tr("Online"),tr("​Output Voltage"),tr("​Output Current"),tr("Module Temp.")}}; + m_pHomePanel->setLableCount(l7.count()); + m_pHomePanel->setTableRowsCount(10); + m_pHomePanel->setLables(l7); + m_pHomePanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); + m_pHomePanel->setMainLabel(tr("Sensors")); + m_pHomePanel->Build(); + mainLayout->addWidget(m_pHomePanel, 1, 2); + + //告警 + m_pAlarmPanel = new CustomWarningPanel(this); + m_pAlarmPanel->setImage(":/icons/main_alarm.png"); + QStringList l8{QStringList{tr("Warning Board")}}; + m_pAlarmPanel->setLableCount(l8.count()); + m_pAlarmPanel->setTableRowsCount(10); + m_pAlarmPanel->setTableRolCount(3); + m_pAlarmPanel->setLables(l8); + m_pAlarmPanel->setRowItems(QStringList{tr("")}); + m_pAlarmPanel->setMainLabel(tr("Warning")); + m_pAlarmPanel->Build(); + mainLayout->addWidget(m_pAlarmPanel, 1, 3); // 设置布局的间距和边距 mainLayout->setSpacing(5); @@ -278,6 +359,13 @@ bool MainWindow::InitializeUI() QAction *actionClose = new QAction(QIcon(":/icons/close.png"), tr("Close"), this); actionClose->setToolTip(tr("Close Application")); + //添加logo + QLabel *logoLabel = new QLabel(this); + QPixmap pixmap(":/icons/logo-en.png"); // 替换为实际图片路径 + logoLabel->setPixmap(pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + toolBar->addWidget(logoLabel); + toolBar->addSeparator(); + //添加按钮 toolBar->addAction(actionRead); toolBar->addSeparator(); @@ -288,25 +376,26 @@ bool MainWindow::InitializeUI() toolBar->addWidget(spacer); // 添加中央标签 - QLabel *label = new QLabel(tr("综合电源柜监测"), this); + QLabel *label = new QLabel(tr("​Integrated Power Cabinet Monitoring"), this); - // 加载字体 - int fontId = QFontDatabase::addApplicationFont(":/fonts/Alimama_DongFangDaKai_Regular.ttf"); - if (fontId == -1) - { - qDebug() << "字体加载失败"; - return -1; - } + // // 加载字体 + // int fontId = QFontDatabase::addApplicationFont(":/fonts/Alimama_DongFangDaKai_Regular.ttf"); + // if (fontId == -1) + // { + // ui->statusbar->showMessage(tr("Failed to load font")); + // } //QFont customFont("Alimama DongFangDaKai", 48); //customFont.setBold(true); //label->setFont(customFont); //label->setStyleSheet("color: #2E86C1;"); label->setStyleSheet( "QLabel {" - " font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + " font-family: 'Arial';" // 需确保字体已加载 " font-size: 52px;" " color: #2E86C1;" - //" font-weight: bold;" + " font-weight: bold;" + " font-style: black;" "}" ); toolBar->addWidget(label); @@ -327,17 +416,20 @@ bool MainWindow::InitializeUI() connect(actionSetting, &QAction::triggered, this, &MainWindow::SettingSerialPort); connect(actionRead, &QAction::triggered, this, &MainWindow::ReadSerialPortData); - ui->statusbar->showMessage(tr("Ready"), 0); return true; } bool MainWindow::InitializeModbus() { + if (m_bInitializeModbus) + return true; + QString appDir = QCoreApplication::applicationDirPath(); QString iniFilePath = appDir + QString::fromStdString("/emsshower.ini"); getConfiguration(iniFilePath); + m_bInitializeModbus = true; switch (m_modbus_type) { case 0: @@ -413,7 +505,11 @@ bool MainWindow::InitializeTcp() bool MainWindow::readRegister(int addr,int nb,uint16_t* dest) { - InitializeModbus(); + if(!InitializeModbus()) //这里有问题,如果是虚拟串口,连接的通常会返回成功 + { + ui->statusbar->showMessage(tr("Failed to open Modbus device,Check modbus connection please!")); //打开MODBUS设备失败,请检查设备连接情况!")); + return false; + } for (auto it = m_SlaveData.begin(); it != m_SlaveData.end(); ++it) { @@ -443,16 +539,17 @@ bool MainWindow::readRegister(int addr,int nb,uint16_t* dest) { TemperatureData* pData = (TemperatureData*)pDevice; assert(pData); - QString value; - if (pData->bDecodeTemp) + OpenJson json; + if(CreateJson(pData,json)) { - //ui->txt_content_1->append(QString(tr("温度:%1\n湿度: %2\n露点: %3\n")).arg(pData->TempValue).arg(pData->HumidityValue).arg(pData->DewPointValue)); - //m_panel_labels[0]->setText(QString(tr("温度: %1\n湿度: %2\n露点: %3\n")).arg(pData->TempValue).arg(pData->HumidityValue).arg(pData->DewPointValue)); + if(pData->bDecodeTemp) + m_pTemperaturePanel->UpdateData(json); + if(pData->bDecodeAlarm) + m_pAlarmPanel->UpdateAlarm(json); } - if (pData->bDecodeAlarm) + else { - //ui->txt_content_1->append(QString(tr("高温告警: %1\n低温告警: %2\n湿度告警: %3\n")).arg(pData->TempHighAlarm).arg(pData->TempLowAlarm).arg(pData->HumidityHighAlarm)); - //m_panel_labels[0]->setText(QString(tr("高温告警: %1\n低温告警: %2\n湿度告警: %3\n")).arg(pData->TempHighAlarm).arg(pData->TempLowAlarm).arg(pData->HumidityHighAlarm)); + ui->statusbar->showMessage(tr("Failed to decode temperaure data")); //解析温度数据失败!")); } } @@ -500,6 +597,26 @@ void MainWindow::startAsyncProcess(const QVector& array,int slave_id,i void MainWindow::ReadSerialPortData() +{ + if (!m_pTimer) + m_pTimer = new QTimer(this); + + if (m_pTimer->isActive()) + { + m_pTimer->stop(); + ui->statusbar->showMessage(tr("Stop Reading")); + } + else + { + m_pTimer->setInterval(10000); // 10秒 + connect(m_pTimer, &QTimer::timeout, this, &MainWindow::onTimeout); + ui->statusbar->showMessage(tr("Begin Readding")); + m_pTimer->start(); + onTimeout(); + } +} + +void MainWindow::onTimeout() { readRegister(0,0,0); } @@ -508,11 +625,98 @@ void MainWindow::SettingSerialPort() { FormSerialPortSettingDialog* dlg = new FormSerialPortSettingDialog(this); dlg->setWindowFlags(dlg->windowFlags()&~(Qt::WindowMinMaxButtonsHint|Qt::WindowContextHelpButtonHint)); - //dlg.setModal(true); - //dlg->show(); - if(dlg->exec() == QDialog::Accepted) - QMessageBox::information(this, "OK Clicked", "Button 1 was clicked!"); - else - QMessageBox::information(this, "Cancel Clicked", "Cancel was clicked!"); + dlg->exec(); + + // if(dlg->exec() == QDialog::Accepted) + // QMessageBox::information(this, "OK Clicked", "Button 1 was clicked!"); + // else + // QMessageBox::information(this, "Cancel Clicked", "Cancel was clicked!"); } +bool MainWindow::CreateJson(DeviceData* pData,OpenJson& json) +{ + int data_type = pData->m_device_type; + if (data_type == 81) //根据协议定义的设备id进行分类,参考DeviceData定义 + { + TemperatureData* pTempData = (TemperatureData*)pData; + if (pTempData->bDecodeTemp) + { + json["panel_type"] = CustomDisplayPanel::PANEL_TEMPERATURE; + + auto& nodeLabel = json["text_panel"]; + nodeLabel[0]["value"] = pTempData->m_device_online_state; + nodeLabel[0]["title"] = "Online"; + + nodeLabel[1]["value"] = pTempData->TempValue; + nodeLabel[1]["title"] = "Temp(℃)"; + + nodeLabel[2]["value"] = pTempData->HumidityValue; + nodeLabel[2]["title"] = "RH (%) "; + + auto& nodeTable = json["table"]; + int i = 0; + + nodeTable[i]["value"] = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss").toStdString(); + nodeTable[i]["signal"] = "Time"; + + i++; + nodeTable[i]["value"] = pTempData->DewPointValue; + nodeTable[i]["signal"] = "Dew Point"; + + i++; + nodeTable[i]["value"] = pTempData->DO; + nodeTable[i]["signal"] = "DO"; + + i++; + nodeTable[i]["value"] = pTempData->DI1; + nodeTable[i]["signal"] = "DI1"; + + i++; + nodeTable[i]["value"] = pTempData->DI2; + nodeTable[i]["signal"] = "DI2"; + + std::string a = json.encode(); + + qDebug() << QString::fromStdString(a); + } + + if (pTempData->bDecodeAlarm) + { + json["panel_type"] = CustomDisplayPanel::PANEL_ALARM; + auto& nodeTable = json["alarm"]; + + int i = -1; + if(pTempData->TempHighAlarm != 0) + { + i++; + nodeTable[i]["time"] = QDateTime::currentDateTime().toString("MM-dd HH:mm:ss").toStdString(); + nodeTable[i]["signal"] = "High Temperature"; + nodeTable[i]["value"] = pTempData->TempHighAlarm; + } + + if(pTempData->TempLowAlarm != 0) + { + i++; + nodeTable[i]["time"] = QDateTime::currentDateTime().toString("MM-dd HH:mm:ss").toStdString(); + nodeTable[i]["signal"] = "High Temperature"; + nodeTable[i]["value"] = pTempData->TempLowAlarm; + } + + if(pTempData->HumidityHighAlarm != 0) + { + i++; + nodeTable[i]["time"] = QDateTime::currentDateTime().toString("MM-dd HH:mm:ss").toStdString(); + nodeTable[i]["signal"] = "High Humidity"; + nodeTable[i]["value"] = pTempData->HumidityHighAlarm; + } + + std::string a = json.encode(); + + qDebug() << QString::fromStdString(a); + } + + return true; + } + + return false; +} diff --git a/applications/EmsShower/mainwindow.h b/applications/EmsShower/mainwindow.h index da21cf9..4232257 100644 --- a/applications/EmsShower/mainwindow.h +++ b/applications/EmsShower/mainwindow.h @@ -5,10 +5,15 @@ #include #include #include +#include #include "libmodbus/modbus.h" #include "slave_define.h" +#include "openjson.h" + +class CustomDisplayPanel; + QT_BEGIN_NAMESPACE namespace Ui { @@ -65,7 +70,6 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); - protected: void getConfiguration(QString iniFilePath); bool InitializeUI(); @@ -80,7 +84,7 @@ private: protected: modbus_t *m_pModbus; SlaveData m_SlaveData; - + QTimer *m_pTimer; protected: QString m_version; int m_modbus_type; @@ -93,10 +97,8 @@ protected: int m_modbus_stop; int m_modbus_slave_id; QSettings* m_pSettings; + bool m_bInitializeModbus; - -protected: - QVector m_panel_labels; protected: //异步处理数据 void startAsyncProcess(const QVector& array,int slave_id,int start_addr,int quantity,DeviceData* pData); @@ -104,6 +106,20 @@ protected: private slots: void ReadSerialPortData(); void SettingSerialPort(); + void onTimeout(); + +private: + CustomDisplayPanel* m_pTemperaturePanel; //温湿度显示 + CustomDisplayPanel* m_pBatteryPanel; //电池显示 + CustomDisplayPanel* m_pPowerPanel; //电源显示 + CustomDisplayPanel* m_pACPanel; //空调显示 + CustomDisplayPanel* m_pHomePanel; //主柜子信息 + CustomDisplayPanel* m_pAlarmPanel; //告警 + CustomDisplayPanel* m_pInverterPanel; //逆变器 + CustomDisplayPanel* m_pPVPanel; //太阳能 + +private: + bool CreateJson(DeviceData* pData,OpenJson& json); signals: // 处理进度信号 diff --git a/applications/EmsShower/openjson.cpp b/applications/EmsShower/openjson.cpp new file mode 100644 index 0000000..1e3241b --- /dev/null +++ b/applications/EmsShower/openjson.cpp @@ -0,0 +1,1431 @@ +/*************************************************************************** + * Copyright (C) 2023-, openlinyou, + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openjson.h" + + +#define PRINTF printf +#if (defined(_MSC_VER) && (_MSC_VER >= 1400 )) +inline int SNPRINTF(char* buffer, size_t size, const char* format, ...) +{ + va_list va; + va_start(va, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, va); + va_end(va); + return result; +} +#define SSCANF sscanf_s +#else +#define SNPRINTF snprintf +#define SSCANF sscanf +#endif + +static inline void doubleToStr(double v, char* buffer, int size) +{ + double tmp = floor(v); + if (tmp == v) + SNPRINTF(buffer, size, "%ld", (long)v); + else + SNPRINTF(buffer, size, "%g", v); +} + +static inline bool strToDouble(const char* str, double* value) +{ + return SSCANF(str, "%lf", value) == 1 ? true : false; +} + +static void int32ToStr(int32_t n, char* str, size_t size) +{ + if (str == 0 || size < 1) return; + str[size - 1] = 0; + if (size < 2) return; + if (n == 0) + { + str[0] = '0'; + return; + } + size_t i = 0; + char buf[128] = { 0 }; + int32_t tmp = n < 0 ? -n : n; + while (tmp && i < 128) + { + buf[i++] = (tmp % 10) + '0'; + tmp = tmp / 10; + } + size_t len = n < 0 ? ++i : i; + if (len > size) + { + len = size; + i = len - 1; + } + str[i] = 0; + while (1) + { + --i; + if (i < 0 || buf[len - i - 1] == 0) break; + str[i] = buf[len - i - 1]; + } + if (i == 0) str[i] = '-'; +} + +static void int64ToStr(int64_t n, char* str, size_t size) +{ + if (str == 0 || size < 1) return; + str[size - 1] = 0; + if (size < 2) return; + if (n == 0) + { + str[0] = '0'; + return; + } + size_t i = 0; + char buf[128] = { 0 }; + int64_t tmp = n < 0 ? -n : n; + while (tmp && i < 128) + { + buf[i++] = (tmp % 10) + '0'; + tmp = tmp / 10; + } + size_t len = n < 0 ? ++i : i; + if (len > size) + { + len = size; + i = len - 1; + } + str[i] = 0; + while (1) + { + --i; + if (i < 0 || buf[len - i - 1] == 0) break; + str[i] = buf[len - i - 1]; + } + if (i == 0) str[i] = '-'; +} + +static int32_t strToInt32(const char* str) +{ + const char* ptr = str; + if (*ptr == '-' || *ptr == '+') ptr++; + int32_t tmp = 0; + while (*ptr != 0) + { + if ((*ptr < '0') || (*ptr > '9')) break; + tmp = tmp * 10 + (*ptr - '0'); + ptr++; + } + if (*str == '-') tmp = -tmp; + return tmp; +} + +static int64_t strToInt64(const char* str) +{ + const char* ptr = str; + if (*ptr == '-' || *ptr == '+') ptr++; + int64_t temp = 0; + while (*ptr != 0) + { + if ((*ptr < '0') || (*ptr > '9')) break; + temp = temp * 10 + (*ptr - '0'); + ptr++; + } + if (*str == '-') temp = -temp; + return temp; +} + + +//JsonBox +OpenJson::Box::Box() +{ +} + +OpenJson::Box::~Box() +{ + for (size_t i = 0; i < childs_.size(); i++) + { + if (childs_[i]) + { + delete childs_[i]; + } + } + childs_.clear(); +} + +bool OpenJson::Box::remove(OpenJson* node) +{ + if (!node) return false; + std::vector::iterator iter; + for (iter = childs_.begin(); iter != childs_.end(); iter++) + { + if (*iter == node) + { + childs_.erase(iter); + delete node; + return true; + } + } + return false; +} + +//JsonContext +OpenJson::Context::Context() + : + offset_(0), + data_(0), + root_(0), + size_(0) +{ +} + +OpenJson::Context::~Context() +{ +} + +void OpenJson::Context::startRead() +{ + size_ = rbuffer_.size(); + data_ = (char*)rbuffer_.data(); + offset_ = 0; +} + +void OpenJson::Context::startWrite() +{ + wbuffer_.clear(); +} + +//Segment +OpenJson::Segment::Segment(SegmentType type) +{ + setType(type); +} +OpenJson::Segment::~Segment() +{ +} +void OpenJson::Segment::setType(SegmentType type) +{ + type_ = type; + value_.int64_ = 0; +} +void OpenJson::Segment::clear() +{ + value_.int64_ = 0; +} + +void OpenJson::Segment::toString() +{ + switch (type_) + { + case NIL: + content_ = "null"; + break; + case BOOL: + content_ = value_.bool_ ? "true" : "false"; + break; + case INT32: + { + char buffer[64] = { 0 }; + int32ToStr(value_.int32_, buffer, sizeof(buffer)); + content_ = buffer; + } + break; + case INT64: + { + char buffer[64] = { 0 }; + int64ToStr(value_.int64_, buffer, sizeof(buffer)); + content_ = buffer; + } + break; + case DOUBLE: + { + char buffer[64] = { 0 }; + doubleToStr(value_.double_, buffer, sizeof(buffer)); + content_ = buffer; + } + break; + case STRING: + break; + default: + content_.clear(); + break; + } +} + +//OpenJson +bool OpenJson::EnableLog_ = true; + +OpenJson OpenJson::NodeNull; +std::string OpenJson::StringNull; + +OpenJson::OpenJson(JsonType type) + :type_(type), + context_(0), + wcontext_(0), + box_(0), + idx_(0), + key_(0), + len_(0), + segment_(0) +{ +} + +OpenJson::~OpenJson() +{ + clear(); +} + +OpenJson* OpenJson::createNode(unsigned char code) +{ + JsonType ctype = UNKNOWN; + switch (code) + { + case '"': + case '\'': + ctype = STRING; + break; + case '{': + ctype = OBJECT; + break; + case '[': + ctype = ARRAY; + break; + default: + ctype = NUMBER; + break; + } + OpenJson* node = new OpenJson(ctype); + return node; +} + +OpenJson::JsonType OpenJson::codeToType(unsigned char code) +{ + JsonType ctype = UNKNOWN; + switch (code) + { + case '"': + case '\'': + ctype = STRING; + break; + case '{': + ctype = OBJECT; + break; + case '[': + ctype = ARRAY; + break; + default: + ctype = NUMBER; + break; + } + return ctype; +} + +const std::string& OpenJson::emptyString() +{ + if (context_) + { + context_->stringNull_.clear(); + return context_->stringNull_; + } + if (wcontext_) + { + wcontext_->stringNull_.clear(); + return wcontext_->stringNull_; + } + return OpenJson::StringNull; +} + +const std::string& OpenJson::key() +{ + if (key_) return key_->s(); + return emptyString(); +} + +const char* OpenJson::data() +{ + if (context_ && context_->data_) + { + if (idx_ < context_->size_) + { + return context_->data_ + idx_; + } + } + Log("JsonNode is Empty"); + return emptyString().c_str(); +} + +double OpenJson::stringToDouble() +{ + const char* str = data(); + double dval = 0; + if (!str || strlen(str) == 0) + dval = (float)(1e+300 * 1e+300) * 0.0F; + else if (strcmp(str, "true") == 0) + dval = 1.0; + else if (strcmp(str, "false") == 0) + dval = 0.0; + else + dval = atof(str); + return dval; +} + +int32_t OpenJson::stringToInt32() +{ + int32_t ret = atoi(data()); + return ret; +} + +int64_t OpenJson::stringToInt64() +{ + int64_t ret = atoll(data()); + return ret; +} + +const std::string& OpenJson::s() +{ + if (type_ == STRING) + { + if (!segment_) + { + segment_ = new Segment(Segment::STRING); + segment_->content_ = data(); + } + if (segment_->type_ == Segment::STRING) + { + return segment_->content_; + } + segment_->toString(); + return segment_->content_; + } + else if (type_ == NUMBER) + { + Log("JsonNode is no STRING"); + if (!segment_) + { + if (!context_ || !context_->data_ || len_ < 1) + { + return emptyString(); + } + segment_ = new Segment(Segment::NIL); + segment_->content_ = data(); + return segment_->content_; + } + if (segment_) + { + if (segment_->type_ != Segment::NIL) + { + segment_->toString(); + } + return segment_->content_; + } + } + else + { + Log("JsonNode is no STRING"); + } + return emptyString(); +} + +double OpenJson::d(double def) +{ + if (type_ != NUMBER) + { + Log("JsonNode is no NUMBER"); + return def; + } + if (segment_ == 0) + { + if (!context_ || !context_->data_ || len_ < 1) + { + return def; + } + segment_ = new Segment(Segment::DOUBLE); + segment_->value_.double_ = stringToDouble(); + } + if (segment_->type_ != Segment::DOUBLE) + { + if (!context_ || !context_->data_ || len_ < 1) + { + Log("JsonNode is no DOUBLE NUMBER"); + } + else + { + segment_->setType(Segment::DOUBLE); + segment_->value_.double_ = stringToDouble(); + } + } + switch (segment_->type_) + { + case OpenJson::Segment::BOOL: + return segment_->value_.bool_; + case OpenJson::Segment::INT32: + return (double)segment_->value_.int32_; + case OpenJson::Segment::INT64: + return (double)segment_->value_.int64_; + case OpenJson::Segment::DOUBLE: + return segment_->value_.double_; + case OpenJson::Segment::STRING: + return atof(segment_->content_.c_str()); + default: + break; + } + return def; +} + +bool OpenJson::b(bool def) +{ + if (type_ != NUMBER) + { + Log("JsonNode is no NUMBER"); + return def; + } + if (segment_ == 0) + { + if (!context_ || !context_->data_ || len_ < 1) + { + return def; + } + segment_ = new Segment(Segment::BOOL); + segment_->value_.bool_ = stringToDouble() != 0 ? true : false; + } + if (segment_->type_ != Segment::BOOL) + { + if (!context_ || !context_->data_ || len_ < 1) + { + Log("JsonNode is no BOOL NUMBER"); + } + else + { + segment_->setType(Segment::BOOL); + segment_->value_.bool_ = stringToDouble() != 0 ? true : false; + } + } + switch (segment_->type_) + { + case OpenJson::Segment::BOOL: + return segment_->value_.bool_; + case OpenJson::Segment::INT32: + return (bool)segment_->value_.int32_; + case OpenJson::Segment::INT64: + return (bool)segment_->value_.int64_; + case OpenJson::Segment::DOUBLE: + return (bool)segment_->value_.double_; + case OpenJson::Segment::STRING: + return segment_->content_.size() > 0; + default: + break; + } + return def; +} + +int32_t OpenJson::i32(int32_t def) +{ + if (type_ != NUMBER) + { + Log("JsonNode is no NUMBER"); + return def; + } + if (segment_ == 0) + { + if (!context_ || !context_->data_ || len_ < 1) + { + return def; + } + segment_ = new Segment(Segment::INT32); + segment_->value_.int32_ = stringToInt32(); + } + if (segment_->type_ != Segment::INT32) + { + if (!context_ || !context_->data_ || len_ < 1) + { + Log("JsonNode is no INT32 NUMBER"); + } + else + { + segment_->setType(Segment::INT32); + segment_->value_.int32_ = stringToInt32(); + } + } + switch (segment_->type_) + { + case OpenJson::Segment::BOOL: + return segment_->value_.bool_; + case OpenJson::Segment::INT32: + return segment_->value_.int32_; + case OpenJson::Segment::INT64: + return (int32_t)segment_->value_.int64_; + case OpenJson::Segment::DOUBLE: + return (int32_t)segment_->value_.double_; + case OpenJson::Segment::STRING: + return atoi(segment_->content_.c_str()); + default: + break; + } + return def; +} + +int64_t OpenJson::i64(int64_t def) +{ + if (type_ != NUMBER) + { + Log("JsonNode is no NUMBER"); + return def; + } + if (segment_ && segment_->type_ == Segment::NIL) + { + delete segment_; + segment_ = 0; + } + if (segment_ == 0) + { + if (!context_ || !context_->data_ || len_ < 1) + { + return def; + } + segment_ = new Segment(Segment::INT64); + segment_->value_.int64_ = stringToInt64(); + } + if (segment_->type_ != Segment::INT64) + { + Log("JsonNode is no INT64 NUMBER"); + } + switch (segment_->type_) + { + case OpenJson::Segment::BOOL: + return segment_->value_.bool_; + case OpenJson::Segment::INT32: + return segment_->value_.int32_; + case OpenJson::Segment::INT64: + return segment_->value_.int64_; + case OpenJson::Segment::DOUBLE: + return (int64_t)segment_->value_.double_; + case OpenJson::Segment::STRING: + return atoll(segment_->content_.c_str()); + default: + break; + } + return def; +} + +void OpenJson::operator=(const std::string& val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != STRING) type_ = STRING; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::STRING); + //const char* ptr = 0; + for (size_t i = 0; i < val.size(); ++i) + { + if (val[i] == '"' || val[i] == '\'') + { + segment_->content_.push_back('\\'); + } + segment_->content_.push_back(val[i]); + } +} + +void OpenJson::operator=(const char* val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != STRING) type_ = STRING; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::STRING); + segment_->content_.clear(); + const char* ptr = 0; + for (size_t i = 0; i < strlen(val); ++i) + { + ptr = val + i; + if (*ptr == '"' || *ptr == '\'') + { + segment_->content_.push_back('\\'); + } + segment_->content_.push_back(*ptr); + } +} + +void OpenJson::operator=(bool val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != NUMBER) type_ = NUMBER; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::BOOL); + segment_->value_.bool_ = val; +} + +void OpenJson::operator=(int32_t val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != NUMBER) type_ = NUMBER; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::INT32); + segment_->value_.int32_ = val; +} + +void OpenJson::operator=(uint32_t val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != NUMBER) type_ = NUMBER; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::INT32); + segment_->value_.int32_ = val; +} + +void OpenJson::operator=(int64_t val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != NUMBER) type_ = NUMBER; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::INT64); + segment_->value_.int64_ = val; +} + +void OpenJson::operator=(uint64_t val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container, not element"); + return; + } + if (type_ != NUMBER) type_ = NUMBER; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::INT64); + segment_->value_.int64_ = val; +} + +void OpenJson::operator=(double val) +{ + if (type_ == OBJECT || type_ == ARRAY) + { + Log("JsonNode is a container"); + return; + } + if (type_ != NUMBER) type_ = NUMBER; + if (segment_ == 0) segment_ = new Segment; + segment_->setType(Segment::DOUBLE); + segment_->value_.double_ = val; +} + +OpenJson& OpenJson::array(size_t idx) +{ + if (type_ != ARRAY) + { + if (type_ == OBJECT) + { + Log("JsonNode must be ARRAY, not OBJECT"); + } + type_ = ARRAY; + } + else + { + assert(box_); + } + if (!box_) box_ = new Box; + if (idx >= box_->childs_.size()) + { + box_->childs_.resize(idx + 1, 0); + } + OpenJson* child = box_->childs_[idx]; + if (!child) + { + child = new OpenJson(); + box_->childs_[idx] = child; + } + return *child; +} + +OpenJson& OpenJson::object(const char* key) +{ + if (!key) + { + return NodeNull; + } + if (type_ != OBJECT) + { + if (type_ == ARRAY) + { + Log("JsonNode must be OBJECT, not ARRAY"); + } + type_ = OBJECT; + } + else + { + assert(box_); + } + if (!box_) box_ = new Box; + + OpenJson* child = 0; + for (size_t i = 0; i < box_->childs_.size(); ++i) + { + child = box_->childs_[i]; + if (child == 0) continue; + if (strcmp(child->key().c_str(), key) == 0) + { + return *child; + } + } + OpenJson* keyNode = new OpenJson(STRING); + *keyNode = key; + child = new OpenJson(); + child->key_ = keyNode; + size_t i = 0; + for (; i < box_->childs_.size(); ++i) + { + if (!box_->childs_[i]) + { + box_->childs_[i] = child; + break; + } + } + if (i >= box_->childs_.size()) + { + box_->childs_.push_back(child); + } + return *child; +} + +void OpenJson::addNode(OpenJson* node) +{ + if (!node) return; + if (type_ != OBJECT && type_ != ARRAY) + { + Log("JsonNode must be OBJECT or ARRAY"); + type_ = node->key_ ? OBJECT : ARRAY; + } + if (box_ == 0) box_ = new Box; + box_->add(node); +} + +void OpenJson::removeNode(size_t idx) +{ + if (box_ == 0) return; + if (idx >= box_->childs_.size()) return; + box_->remove(box_->childs_[idx]); +} + +void OpenJson::removeNode(const char* key) +{ + if (box_ == 0) return; + OpenJson* child = 0; + for (size_t i = 0; i < box_->childs_.size(); ++i) + { + child = box_->childs_[i]; + if (child == 0) continue; + if (strcmp(child->key().c_str(), key) == 0) + { + box_->remove(child); + break; + } + } +} + +void OpenJson::clear() +{ + if (segment_) + { + delete segment_; + segment_ = 0; + } + if (key_) + { + delete key_; + key_ = 0; + } + if (box_) + { + assert(type_ == OBJECT || type_ == ARRAY); + delete box_; + box_ = 0; + } + if (context_ != 0 && context_->root_ == this) + { + context_->root_ = 0; + delete context_; + } + context_ = 0; + if (wcontext_ != 0 && wcontext_->root_ == this) + { + wcontext_->root_ = 0; + delete wcontext_; + } + wcontext_ = 0; + type_ = EMPTY; + idx_ = 0; + len_ = 0; +} + +void OpenJson::trimSpace() +{ + if (!context_) return; + char code = 0; + for (size_t i = idx_; i < context_->size_; ++i) + { + code = context_->data_[i]; + if (code > ' ') + { + idx_ = i; + break; + } + } +} + +unsigned char OpenJson::getCharCode() +{ + if (!context_) return 0; + if (idx_ < context_->size_) + { + unsigned char tmp = (unsigned char)context_->data_[idx_]; + return tmp; + } + return 0; +} + +unsigned char OpenJson::getChar() +{ + unsigned char code = getCharCode(); + if (code <= ' ') + { + trimSpace(); + code = getCharCode(); + } + return code; +} + +unsigned char OpenJson::checkCode(unsigned char charCode) +{ + unsigned char code = getCharCode(); + if (code != charCode) + { + trimSpace(); + code = getCharCode(); + if (code != charCode) return 0; + } + ++idx_; + return code; +} + +size_t OpenJson::searchCode(unsigned char code) +{ + char* data = context_->data_; + for (size_t i = idx_; i < context_->size_; i++) + { + if (data[i] == code) + { + if (i > 0 && data[i - 1] != '\\') return i; + } + } + return -1; +} + +bool OpenJson::makeRContext() +{ + if (type_ != EMPTY) + { + if (context_ && context_->root_ != this) + { + PRINTF("OpenJson warn:JsonNode is no root or empty!"); + return false; + } + } + else + { + if (context_ && context_->root_ != this) + { + PRINTF("OpenJson warn:JsonNode is no root or empty!"); + return false; + }; + } + clear(); + context_ = new Context(); + context_->root_ = this; + context_->offset_ = 0; + context_->rbuffer_.clear(); + return true; +} + +bool OpenJson::decode(const std::string& buffer) +{ + if (!makeRContext()) return false; + context_->rbuffer_ = buffer; + context_->startRead(); + type_ = codeToType(getChar()); + try + { + read(context_, true); + } + catch (const char* error) + { + PRINTF("OpenJson warn:decode catch exception %s", error); + } + return true; +} + +bool OpenJson::decodeFile(const std::string& filePath) +{ + if (!makeRContext()) return false; + FILE* fp = 0; +#ifdef _MSC_VER + fopen_s(&fp, filePath.c_str(), "rb"); +#else + fp = fopen(filePath.c_str(), "rb"); +#endif + if (fp == 0) + { +#ifdef _MSC_VER + char buffer[1024] = { 0 }; + strerror_s(buffer, sizeof(buffer), errno); + PRINTF("OpenJson warn:decodeFile error:%s,%s\n", buffer, filePath.c_str()); +#else + PRINTF("OpenJson warn:decodeFile error:%s,%s\n", strerror(errno), filePath.c_str()); +#endif + return false; + } + fseek(fp, 0, SEEK_END); + /*size_t size = */ + ftell(fp); + fseek(fp, 0, SEEK_SET); + + size_t ret = 0; + char buff[1024 * 8] = { 0 }; + while (true) + { + ret = fread(buff, 1, sizeof(buff), fp); + if (ret < 0) + { +#ifdef _MSC_VER + char buffer[1024] = { 0 }; + strerror_s(buffer, sizeof(buffer), errno); + PRINTF("OpenJson warn:decodeFile error:%s,%s\n", buffer, filePath.c_str()); +#else + PRINTF("OpenJson warn:decodeFile error:%s,%s\n", strerror(errno), filePath.c_str()); +#endif + fclose(fp); + return false; + } + else if(ret == 0) break; + context_->rbuffer_.append(buff, ret); + } + fclose(fp); + + context_->startRead(); + type_ = codeToType(getChar()); + try + { + read(context_, true); + } + catch (const char* error) + { + PRINTF("OpenJson warn:decodeFile catch exception %s", error); + } + return true; +} + +const std::string& OpenJson::encode() +{ + if (wcontext_ == 0) + { + wcontext_ = new Context(); + wcontext_->root_ = this; + } + wcontext_->startWrite(); + write(wcontext_, true); + return wcontext_->wbuffer_; +} + +void OpenJson::encodeFile(const std::string& filePath) +{ + FILE* fp = 0; +#ifdef _MSC_VER + fopen_s(&fp, filePath.c_str(), "wb"); +#else + fp = fopen(filePath.c_str(), "wb"); +#endif + if (fp == 0) + { +#ifdef _MSC_VER + char buffer[1024] = { 0 }; + strerror_s(buffer, sizeof(buffer), errno); + PRINTF("OpenJson warn:encodeFile error:%s,%s\n", buffer, filePath.c_str()); +#else + PRINTF("OpenJson warn:encodeFile error:%s,%s\n", strerror(errno), filePath.c_str()); +#endif + return; + } + fseek(fp, 0, SEEK_SET); + const std::string& buffer = encode(); + fwrite(buffer.data(), buffer.size(), 1, fp); + fclose(fp); +} + +void OpenJson::read(Context* context, bool isRoot) +{ + if (context_) + { + if (isRoot) + { + assert(context_ == context); + assert(context_->root_ == this); + } + else + { + assert(context_->root_ != this); + if (context_->root_ == this) return; + } + } + len_ = 0; + context_ = context; + idx_ = context->offset_; + switch (type_) + { + case EMPTY: + break; + case STRING: + readString(); + break; + case NUMBER: + readNumber(); + break; + case OBJECT: + readObject(); + break; + case ARRAY: + readArray(); + break; + case UNKNOWN: + break; + default: + break; + } +} +void OpenJson::readNumber() +{ + assert(type_ == NUMBER); + unsigned char code = 0; + size_t sidx = idx_; + size_t len = context_->size_; + char* data = context_->data_; + for (; idx_ < len; idx_++) + { + code = data[idx_]; + if (code == ',' || code == '}' || code == ']') + { + idx_--; + break; + } + } + if (idx_ < sidx) + { + throwError("lost number value"); + return; + } + len_ = idx_ - sidx + 1; + idx_ = sidx; +} +void OpenJson::readString() +{ + assert(type_ == STRING); + unsigned char code = '"'; + if (!checkCode(code)) + { + code = '\''; + if (!checkCode(code)) + { + throwError("lost '\"' or \"'\""); + return; + } + } + size_t sidx = idx_; + size_t eidx = searchCode(code); + if (eidx < 0) + { + throwError("lost '\"' or \"'\""); + return; + } + idx_ = sidx; + len_ = eidx - sidx + 1; + context_->data_[eidx] = 0; +} +void OpenJson::readObject() +{ + assert(type_ == OBJECT); + if (!checkCode('{')) + { + throwError("lost '{'"); + return; + } + unsigned char code = 0; + OpenJson* keyNode = 0; + OpenJson* valNode = 0; + size_t oidx = idx_; + while (idx_ < context_->size_) + { + code = getChar(); + if (code == 0) + { + throwError("lost '}'"); + return; + } + if (checkCode('}')) break; + keyNode = createNode(code); + if (keyNode->type_ != STRING) + { + throwError("lost key"); + return; + } + context_->offset_ = idx_; + keyNode->read(context_); + idx_ = keyNode->idx_ + keyNode->len_; + if (!checkCode(':')) + { + throwError("lost ':'"); + return; + } + code = getChar(); + valNode = createNode(code); + valNode->key_ = keyNode; + context_->offset_ = idx_; + valNode->read(context_); + idx_ = valNode->idx_ + valNode->len_; + addNode(valNode); + + if (checkCode('}')) + { + context_->data_[idx_ - 1] = 0; + break; + } + if (!checkCode(',')) + { + throwError("lost ','"); + return; + } + context_->data_[idx_ - 1] = 0; + } + len_ = idx_ - oidx; + idx_ = oidx; +} +void OpenJson::readArray() +{ + assert(type_ == ARRAY); + if (!checkCode('[')) + { + throwError("lost '['"); + return; + } + unsigned char code = 0; + OpenJson* valNode = 0; + size_t oidx = idx_; + while (idx_ < context_->size_) + { + code = getChar(); + if (code == 0) + { + throwError("lost ']'"); + return; + } + if (checkCode(']')) break; + valNode = createNode(code); + context_->offset_ = idx_; + valNode->read(context_); + idx_ = valNode->idx_ + valNode->len_; + addNode(valNode); + + if (checkCode(']')) + { + context_->data_[idx_ - 1] = 0; + break; + } + if (!checkCode(',')) + { + throwError("lost ','"); + return; + } + context_->data_[idx_ - 1] = 0; + } + len_ = idx_ - oidx; + idx_ = oidx; +} + +void OpenJson::write(Context* context, bool isRoot) +{ + if (wcontext_) + { + if (isRoot) + { + assert(wcontext_ == context); + assert(wcontext_->root_ == this); + } + else + { + assert(wcontext_->root_ != this); + if (wcontext_->root_ == this) return; + } + } + wcontext_ = context; + switch (type_) + { + case EMPTY: + break; + case STRING: + writeString(); + break; + case NUMBER: + writeNumber(); + break; + case OBJECT: + writeObject(); + break; + case ARRAY: + writeArray(); + break; + case UNKNOWN: + break; + default: + break; + } +} +void OpenJson::writeNumber() +{ + assert(type_ == NUMBER); + if (key_) + { + wcontext_->wbuffer_.append("\"" + key() + "\":"); + } + if (segment_) + { + segment_->toString(); + wcontext_->wbuffer_.append(segment_->content_); + } + else + { + wcontext_->wbuffer_.append(data()); + } +} +void OpenJson::writeString() +{ + assert(type_ == STRING); + if (key_) + { + wcontext_->wbuffer_.append("\"" + key() + "\":"); + } + wcontext_->wbuffer_.append("\"" + s() + "\""); +} +void OpenJson::writeObject() +{ + assert(type_ == OBJECT); + if (key_) + wcontext_->wbuffer_.append("\"" + key() + "\":{"); + else + wcontext_->wbuffer_.append("{"); + + if (box_ != 0) + { + size_t idx = 0; + size_t size = box_->size(); + for (size_t i = 0; i < size; ++i) + { + if (!(*box_)[i]) continue; + if (idx > 0) + { + wcontext_->wbuffer_.append(","); + } + (*box_)[i]->write(wcontext_); + ++idx; + } + } + wcontext_->wbuffer_.append("}"); +} +void OpenJson::writeArray() +{ + assert(type_ == ARRAY); + if (key_) + wcontext_->wbuffer_.append("\"" + key() + "\":["); + else + wcontext_->wbuffer_.append("["); + + if (box_ != 0) + { + size_t idx = 0; + size_t size = box_->size(); + for (size_t i = 0; i < size; ++i) + { + if (!(*box_)[i]) continue; + if (idx > 0) + { + wcontext_->wbuffer_.append(","); + } + (*box_)[i]->write(wcontext_); + ++idx; + } + } + wcontext_->wbuffer_.append("]"); +} + +void OpenJson::EnableLog(bool enable) +{ + EnableLog_ = enable; +} + +void OpenJson::Log(const char* format, ...) +{ + if (!EnableLog_) return; + va_list ap; + va_start(ap, format); + char tmp[1024] = { 0 }; + vsnprintf(tmp, sizeof(tmp), format, ap); + va_end(ap); + PRINTF("OpenJson WARN:%s\n", tmp); +} +void OpenJson::throwError(const char* errMsg) +{ + static const char* InfoTags[6] = { "EMPTY", "STRING", "NUMBER", "OBJECT", "ARRAY", "UNKNOWN" }; + size_t len = sizeof(InfoTags) / sizeof(InfoTags[0]); + const char* tab = type_ < len ? InfoTags[type_] : InfoTags[5]; + PRINTF("OpenJson:throwError [%s] Error: %s\n", tab, errMsg); + + char tmp[126] = { 0 }; + len = context_->size_ - context_->offset_; + len = len > 64 ? 64 : len; + memcpy(tmp, context_->data_ + idx_, len); + PRINTF("OpenJson:throwError content:%s\n", tmp); + throw errMsg; +} + diff --git a/applications/EmsShower/openjson.h b/applications/EmsShower/openjson.h new file mode 100644 index 0000000..ff27449 --- /dev/null +++ b/applications/EmsShower/openjson.h @@ -0,0 +1,276 @@ +/*************************************************************************** + * Copyright (C) 2023-, openlinyou, + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + ***************************************************************************/ + +#ifndef HEADER_OPEN_JSON_H +#define HEADER_OPEN_JSON_H + +#include +#include +#include +#include + + +#ifdef _MSC_VER +#pragma warning( disable: 4251 ) +# if _MSC_VER >= 1600 || defined(__MINGW32__) +# else +# if (_MSC_VER < 1300) +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +# else +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +# endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +# endif +#endif // _MSC_VER + +//#pragma warning(disable:4100) + +class OpenJson +{ + enum JsonType + { + EMPTY = 0, + STRING, + NUMBER, + OBJECT, + ARRAY, + UNKNOWN + }; + class Box + { + std::vector childs_; + public: + Box(); + ~Box(); + inline void clear() + { + childs_.clear(); + } + inline bool empty() + { + return childs_.empty(); + } + inline size_t size() + { + return childs_.size(); + } + inline void add(OpenJson* node) + { + childs_.push_back(node); + } + inline OpenJson* operator[](size_t idx) + { + return childs_[idx]; + } + bool remove(OpenJson* node); + friend class OpenJson; + }; + class Context + { + char* data_; + size_t size_; + size_t offset_; + + OpenJson* root_; + std::string rbuffer_; + std::string wbuffer_; + + std::string stringNull_; + public: + Context(); + ~Context(); + void startRead(); + void startWrite(); + friend class OpenJson; + }; + class Segment + { + public: + enum SegmentType + { + NIL = 0, + BOOL, + INT32, + INT64, + DOUBLE, + STRING + }; + SegmentType type_; + std::string content_; + union + { + bool bool_; + int32_t int32_; + int64_t int64_; + double double_; + } value_; + + Segment(SegmentType type = NIL); + ~Segment(); + + void clear(); + void toString(); + void setType(SegmentType type); + }; + + JsonType type_; + Context* context_; + Context* wcontext_; + size_t idx_; + size_t len_; + Box* box_; + OpenJson* key_; + Segment* segment_; + + void trimSpace(); + bool makeRContext(); + OpenJson* createNode(unsigned char code); + JsonType codeToType(unsigned char code); + unsigned char getCharCode(); + unsigned char getChar(); + unsigned char checkCode(unsigned char charCode); + size_t searchCode(unsigned char charCode); + void throwError(const char* errMsg); + + const char* data(); + int32_t stringToInt32(); + int64_t stringToInt64(); + double stringToDouble(); + + OpenJson& array(size_t idx); + OpenJson& object(const char* key); + void addNode(OpenJson* node); + void removeNode(size_t idx); + void removeNode(const char* key); + OpenJson(OpenJson& /*json*/) {} + OpenJson(const OpenJson& /*json*/) {} + void operator=(OpenJson& /*json*/) {} + void operator=(const OpenJson& /*json*/) {} + + const std::string& emptyString(); + static OpenJson NodeNull; + static std::string StringNull; +public: + OpenJson(JsonType type = EMPTY); + ~OpenJson(); + + inline size_t size() + { + return box_ ? box_->size() : 0; + } + inline bool empty() + { + return box_ ? box_->empty() : true; + } + inline OpenJson& operator[] (int idx) + { + return array(idx); + } + inline OpenJson& operator[] (size_t idx) + { + return array(idx); + } + inline OpenJson& operator[] (const char* key) + { + return object(key); + } + inline OpenJson& operator[] (const std::string& key) + { + return object(key.c_str()); + } + + inline void remove(int idx) + { + removeNode(idx); + } + inline void remove(size_t idx) + { + removeNode(idx); + } + inline void remove(const char* key) + { + removeNode(key); + } + inline void remove(const std::string& key) + { + removeNode(key.c_str()); + } + void clear(); + + inline bool isNull() + { + return type_ == EMPTY; + } + inline bool isNumber() + { + return type_ == NUMBER; + } + inline bool isString() + { + return type_ == STRING; + } + inline bool isObject() + { + return type_ == OBJECT; + } + inline bool isArray() + { + return type_ == ARRAY; + } + + bool b(bool def = false); + int32_t i32(int32_t def = 0); + int64_t i64(int64_t def = 0); + double d(double def = 0); + const std::string& s(); + const std::string& key(); + + void operator=(bool val); + void operator=(int32_t val); + void operator=(uint32_t val); + void operator=(int64_t val); + void operator=(uint64_t val); + void operator=(double val); + void operator=(const char* val); + void operator=(const std::string& val); + + bool decode(const std::string& buffer); + bool decodeFile(const std::string& filePath); + const std::string& encode(); + void encodeFile(const std::string& filePath); + + static void EnableLog(bool enable); +private: + static bool EnableLog_; + static void Log(const char* format, ...); + void read(Context* context, bool isRoot = false); + void readNumber(); + void readString(); + void readObject(); + void readArray(); + + void write(Context* context, bool isRoot = false); + void writeNumber(); + void writeString(); + void writeObject(); + void writeArray(); +}; + + +#endif /* HEADER_OPEN_JSON_H */ diff --git a/applications/EmsShower/qss.qrc b/applications/EmsShower/qss.qrc index c7a43a6..1261ddb 100644 --- a/applications/EmsShower/qss.qrc +++ b/applications/EmsShower/qss.qrc @@ -25,5 +25,23 @@ icons/up.png icons/warn.png fonts/Alimama_DongFangDaKai_Regular.ttf + icons/main_power.png + icons/main_ac.png + icons/main_battery.png + icons/main_cab.png + icons/main_door.png + icons/main_fan.png + icons/main_fire.png + icons/main_invertor.png + icons/main_multi_cab.png + icons/main_peidian.png + icons/main_pv.png + icons/main_temp.png + icons/main_thunder.png + icons/main_video.png + icons/main_water.png + icons/main_UPS.png + icons/main_alarm.png + icons/logo-en.png diff --git a/applications/examples/quc_demo/frmgaugeweather.cpp b/applications/examples/quc_demo/frmgaugeweather.cpp new file mode 100644 index 0000000..b16a2ab --- /dev/null +++ b/applications/examples/quc_demo/frmgaugeweather.cpp @@ -0,0 +1,41 @@ +#include "frmgaugeweather.h" +#include "ui_frmgaugeweather.h" + +frmGaugeWeather::frmGaugeWeather(QWidget *parent) : QWidget(parent), ui(new Ui::frmGaugeWeather) +{ + ui->setupUi(this); + this->initForm(); +} + +frmGaugeWeather::~frmGaugeWeather() +{ + delete ui; +} + +void frmGaugeWeather::initForm() +{ + ui->widget->setStyleSheet("QWidget#widget{border-image: url(:/image/bg2.jpg);}"); + ui->horizontalSlider1->setValue(65); + ui->horizontalSlider2->setValue(28); +} + +void frmGaugeWeather::on_horizontalSlider1_valueChanged(int value) +{ + ui->gaugeWeather->setOuterValue(value); +} + +void frmGaugeWeather::on_horizontalSlider2_valueChanged(int value) +{ + ui->gaugeWeather->setInnerValue(value); +} + +void frmGaugeWeather::on_comboBox_currentIndexChanged(int index) +{ + GaugeWeather::WeatherType type = (GaugeWeather::WeatherType)index; + ui->gaugeWeather->setWeatherType(type); +} + +void frmGaugeWeather::on_ckAnimation_stateChanged(int arg1) +{ + ui->gaugeWeather->setAnimation(arg1 != 0); +} diff --git a/applications/examples/quc_demo/frmgaugeweather.h b/applications/examples/quc_demo/frmgaugeweather.h new file mode 100644 index 0000000..1d97a35 --- /dev/null +++ b/applications/examples/quc_demo/frmgaugeweather.h @@ -0,0 +1,30 @@ +#ifndef FRMGAUGEWEATHER_H +#define FRMGAUGEWEATHER_H + +#include +#include + +namespace Ui { +class frmGaugeWeather; +} + +class frmGaugeWeather : public QWidget +{ + Q_OBJECT + +public: + explicit frmGaugeWeather(QWidget *parent = 0); + ~frmGaugeWeather(); + +private: + Ui::frmGaugeWeather *ui; + +private slots: + void initForm(); + void on_horizontalSlider1_valueChanged(int value); + void on_horizontalSlider2_valueChanged(int value); + void on_comboBox_currentIndexChanged(int index); + void on_ckAnimation_stateChanged(int arg1); +}; + +#endif // FRMGAUGEWEATHER_H diff --git a/applications/examples/quc_demo/frmgaugeweather.ui b/applications/examples/quc_demo/frmgaugeweather.ui new file mode 100644 index 0000000..5961dfb --- /dev/null +++ b/applications/examples/quc_demo/frmgaugeweather.ui @@ -0,0 +1,135 @@ + + + frmGaugeWeather + + + + 0 + 0 + 455 + 278 + + + + Form + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + 100 + + + 1 + + + Qt::Horizontal + + + + + + + -50 + + + 50 + + + Qt::Horizontal + + + + + + + 动画 + + + + + + + + + + + + + + + + + + + + + + + 多云 + + + + + + + + + + 雪雨 + + + + + 冰雹 + + + + + 闪电 + + + + + + + + + + 局部多云 + + + + + + + + + + + GaugeWeather + QWidget +
gaugeweather.h
+
+
+ + +
diff --git a/applications/examples/quc_demo/gaugeweather.cpp b/applications/examples/quc_demo/gaugeweather.cpp new file mode 100644 index 0000000..354cf41 --- /dev/null +++ b/applications/examples/quc_demo/gaugeweather.cpp @@ -0,0 +1,951 @@ +#pragma execution_character_set("utf-8") + +#include "gaugeweather.h" +//#include "qsvgrenderer.h" +#include "qpainter.h" +#include +#include "qmath.h" +#include "qtimer.h" +#include "qfile.h" +#include "qdebug.h" + +GaugeWeather::GaugeWeather(QWidget *parent) : QWidget(parent) +{ + outerMinValue = 0; + outerMaxValue = 100; + outerStartAngle = 0; + outerEndAngle = 0; + outerValue = 0; + outerCurrValue = 0; + outerRingBgColor = QColor(250, 250, 250); + outerRingColor = QColor(34, 163, 169); + + innerValue = 0; + innerMinValue = -50; + innerMaxValue = 50; + innerStartAngle = 0; + innerEndAngle = 135; + innerCurrValue = 0; + innerRingBgColor = QColor(250, 250, 250); + innerRingNegativeColor = QColor(214, 5, 5); + innerRingPositiveColor = QColor(0, 254, 254); + + innerScaleMajor = 2; + innerScaleMinor = 0; + innerScaleColor = QColor(255, 255, 255); + innerScaleNumColor = QColor(255, 255, 255); + + centerPixMapNegativeColor = QColor(214, 5, 5); + centerPixMapPositiveColor = QColor(0, 254, 254); + + outerValueTextColor = QColor(34, 163, 169); + innerNegativeValueTextColor = QColor(214, 5, 5); + innerPositiveValueTextColor = QColor(0, 254, 254); + + precision = 0; + outerReverse = false; + innerReverse = false; + clockWise = true; + + animation = false; + animationStep = 2; + weatherType = WeatherType_SUNNY; + centerSvgPath = ":/svg/weather-sunny.svg"; + + outerTimer = new QTimer(this); + outerTimer->setInterval(10); + connect(outerTimer, SIGNAL(timeout()), this, SLOT(updateOuterValue())); + + innerTimer = new QTimer(this); + innerTimer->setInterval(10); + connect(innerTimer, SIGNAL(timeout()), this, SLOT(updateInnerValue())); + + setFont(QFont("Arial", 9)); +} + +GaugeWeather::~GaugeWeather() +{ + if (outerTimer->isActive()) + { + outerTimer->stop(); + } + + if(innerTimer->isActive()) + { + innerTimer->stop(); + } +} + +void GaugeWeather::paintEvent(QPaintEvent *) +{ + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + + //绘制准备工作,启用反锯齿,平移坐标轴中心,等比例缩放 + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.translate(width / 2, height / 2); + painter.scale(side / 200.0, side / 200.0); + + //绘制外环背景 + drawOuterRingBg(&painter); + //绘制外环进度 + drawOuterRing(&painter); + //绘制内环背景 + drawInnerRingBg(&painter); + //绘制内环进度 + drawInnerRing(&painter); + //绘制内环刻度值 + drawInnerRingScaleNum(&painter); + //绘制中心图片 + drawCenterPixMap(&painter); + //绘制数值 + drawValue(&painter); +} + +void GaugeWeather::drawOuterRingBg(QPainter *painter) +{ + int radius = 99; + painter->save(); + painter->setBrush(Qt::NoBrush); + + //绘制圆弧方法绘制圆环 + int penWidth = 13; + QRectF rect(-radius + penWidth / 2, -radius + penWidth / 2, radius * 2 - penWidth, radius * 2 - penWidth); + //可以自行修改画笔的后三个参数,形成各种各样的效果,例如Qt::FlatCap改为Qt::RoundCap可以产生圆角效果 + QPen pen(outerRingBgColor, penWidth, Qt::SolidLine, Qt::FlatCap, Qt::MPenJoinStyle); + + //计算总范围角度 + double angleAll = 360.0 - outerStartAngle - outerEndAngle; + + //绘制总范围角度圆弧 + pen.setColor(outerRingBgColor); + painter->setPen(pen); + painter->drawArc(rect, (270 - outerStartAngle - angleAll) * 16, angleAll * 16); + painter->restore(); +} + +void GaugeWeather::drawOuterRing(QPainter *painter) +{ + int radius = 99; + painter->save(); + painter->setBrush(Qt::NoBrush); + + //绘制圆弧方法绘制圆环 + int penWidth = 13; + QRectF rect(-radius + penWidth / 2, -radius + penWidth / 2, radius * 2 - penWidth, radius * 2 - penWidth); + //可以自行修改画笔的后三个参数,形成各种各样的效果,例如Qt::FlatCap改为Qt::RoundCap可以产生圆角效果 + QPen pen(outerRingColor, penWidth, Qt::SolidLine, Qt::FlatCap, Qt::MPenJoinStyle); + + //计算总范围角度,当前值范围角度,剩余值范围角度 + double angleAll = 360.0 - outerStartAngle - outerEndAngle; + double angleCurrent = angleAll * ((outerCurrValue - outerMinValue) / (outerMaxValue - outerMinValue)); + + //绘制当前值饼圆 + painter->setPen(pen); + painter->drawArc(rect, (270 - outerStartAngle - angleCurrent) * 16, angleCurrent * 16); + painter->restore(); +} + +void GaugeWeather::drawInnerRingBg(QPainter *painter) +{ + int radius = 77; + painter->save(); + painter->setBrush(Qt::NoBrush); + + double penWidth = 13; + QPen pen(innerRingBgColor, penWidth, Qt::SolidLine, Qt::FlatCap, Qt::MPenJoinStyle); + painter->setPen(pen); + + double angleAll = 360.0 - innerStartAngle - innerEndAngle; + QRectF rect = QRectF(-radius, -radius, radius * 2, radius * 2); + + //零值以上背景 + painter->drawArc(rect, (270 - innerStartAngle - angleAll) * 16, angleAll * 16); + //零值以下背景 + //painter->drawArc(rect,(270 -innerStartAngle - null2MinAngle) * 16 ,null2MinAngle * 16); + + painter->restore(); +} + +void GaugeWeather::drawInnerRing(QPainter *painter) +{ + int radius = 77; + painter->save(); + painter->setBrush(Qt::NoBrush); + + int penWidth = 13.5; + QPen pen(innerRingPositiveColor, penWidth, Qt::SolidLine, Qt::FlatCap, Qt::MPenJoinStyle); + + double angleAll = 360.0 - innerStartAngle - innerEndAngle; + double null2MinAngle = angleAll * (( 0 - innerMinValue) / (innerMaxValue - innerMinValue)); //零点所占的角度 + double nullUpAllAngle = angleAll - null2MinAngle; //正基本角度 + double currAngle; + if(innerCurrValue >= 0) + { + //正角度 + pen.setColor(innerRingNegativeColor); + currAngle = nullUpAllAngle * (innerCurrValue / innerMaxValue) * -1; + } + else + { + //负角度 + pen.setColor(innerRingPositiveColor); + currAngle = null2MinAngle * (innerCurrValue / innerMinValue); + } + + painter->setPen(pen); + + QRectF rect = QRectF(-radius, -radius, radius * 2, radius * 2); + painter->drawArc(rect, (270 - innerStartAngle - null2MinAngle) * 16, currAngle * 16); + + painter->restore(); + +} + +void GaugeWeather::drawInnerRingScale(QPainter *painter) +{ + int radius = 76; + painter->save(); + painter->setPen(innerScaleColor); + + painter->rotate(innerStartAngle); + int steps = (innerScaleMajor * innerScaleMinor); + double angleStep = (360.0 - innerStartAngle - innerEndAngle) / steps; + QPen pen = painter->pen(); + pen.setCapStyle(Qt::RoundCap); + + for (int i = 0; i <= steps; i++) + { + if (i % innerScaleMinor == 0) + { + pen.setWidthF(1.5); + painter->setPen(pen); + painter->drawLine(0, radius - 12, 0, radius); + } + else + { + pen.setWidthF(1.0); + painter->setPen(pen); + painter->drawLine(0, radius - 5, 0, radius); + } + + painter->rotate(angleStep); + } + + painter->restore(); +} + +void GaugeWeather::drawInnerRingScaleNum(QPainter *painter) +{ + int radius = 60; + painter->save(); + painter->setPen(innerScaleNumColor); + + double startRad = (360 - innerStartAngle - 90) * (M_PI / 180); + double deltaRad = (360 - innerStartAngle - innerEndAngle) * (M_PI / 180) / innerScaleMajor; + + for (int i = 0; i <= innerScaleMajor; i++) + { + double sina = sin(startRad - i * deltaRad); + double cosa = cos(startRad - i * deltaRad); + double value = 1.0 * i * ((innerMaxValue - innerMinValue) / innerScaleMajor) + innerMinValue; + + QString strValue = QString("%1").arg((double)value, 0, 'f', precision); + double textWidth = fontMetrics().size(Qt::TextSingleLine,strValue).width();// .width(strValue); + double textHeight = fontMetrics().height(); + int x = radius * cosa - textWidth / 2; + int y = -radius * sina + textHeight / 4; + painter->drawText(x, y, strValue); + } + + painter->restore(); +} + +void GaugeWeather::drawCenterPixMap(QPainter *painter) +{ + painter->save(); + // int width_svg = 50; + // int height_svg = 50; + + // QFile file(centerSvgPath); + // file.open(QIODevice::ReadOnly); + // QByteArray baData = file.readAll(); + // //QDomDocument doc; + // //doc.setContent(baData); + // file.close(); + + // //正负角度切换不同颜色 + // if(innerCurrValue >= 0) + // { + // setColor(doc.documentElement(), "path", "fill", rgb2HexStr(centerPixMapNegativeColor)); + // } + // else + // { + // setColor(doc.documentElement(), "path", "fill", rgb2HexStr(centerPixMapPositiveColor)); + // } + + // QSvgRenderer m_svgRender(doc.toByteArray()); + // m_svgRender.render(painter, QRectF(-width_svg, -height_svg / 2, width_svg, height_svg)); + painter->restore(); +} + +void GaugeWeather::drawValue(QPainter *painter) +{ + painter->save(); + + QFont font; + font.setPixelSize(12); + painter->setFont(font); + painter->setPen(outerValueTextColor); + + QFontMetrics fm = painter->fontMetrics(); + QString outerStrValue = QString(tr("湿度: %1%")).arg(QString::number(outerCurrValue, 'f', 0)); + QRectF titleRect(5, 0, fm.size(Qt::TextSingleLine,outerStrValue).width(),fm.height());//width(outerStrValue), fm.height()); + painter->drawText(titleRect, Qt::AlignHCenter | Qt::AlignTop, outerStrValue); + if(innerCurrValue >= 0 ) + { + painter->setPen(innerNegativeValueTextColor); + } + else + { + painter->setPen(innerPositiveValueTextColor); + } + + QString innerDir = innerCurrValue >= 0 ? "+" : "-"; + QString innerStrValue = QString(tr("温度: %1%2 ℃")).arg(innerDir).arg(QString::number(abs(innerCurrValue), 'f', 0)); + QRectF innerTitleRect(5, 0, fm.size(Qt::TextSingleLine,innerStrValue).width(),-fm.height()); //fm.width(innerStrValue), -fm.height()); + painter->drawText(innerTitleRect, Qt::AlignHCenter | Qt::AlignVCenter, innerStrValue); + + painter->restore(); +} + +QString GaugeWeather::rgb2HexStr(const QColor &color) +{ + QString redStr = QString("%1").arg(color.red(), 2, 16, QChar('0')); + QString greenStr = QString("%1").arg(color.green(), 2, 16, QChar('0')); + QString blueStr = QString("%1").arg(color.blue(), 2, 16, QChar('0')); + QString key = "#" + redStr + greenStr + blueStr; + return key; +} + +// void GaugeWeather::setColor(QDomElement elem, QString strtagname, QString strattr, QString strattrval) +// { +// if (elem.tagName().compare(strtagname) == 0) { +// elem.setAttribute(strattr, strattrval); +// } + +// for (int i = 0; i < elem.childNodes().count(); i++) { +// if (!elem.childNodes().at(i).isElement()) { +// continue; +// } + +// setColor(elem.childNodes().at(i).toElement(), strtagname, strattr, strattrval); +// } +// } + +double GaugeWeather::getOuterValue() const +{ + return this->outerValue; +} + +double GaugeWeather::getOuterMinValue() const +{ + return this->outerMinValue; +} + +double GaugeWeather::getOuterMaxValue() const +{ + return this->outerMaxValue; +} + +int GaugeWeather::getOuterStartAngle() const +{ + return this->outerStartAngle; +} + +int GaugeWeather::getOuterEndAngle() const +{ + return this->outerEndAngle; +} + +QColor GaugeWeather::getOuterRingBgColor() const +{ + return this->outerRingBgColor; +} + +QColor GaugeWeather::getOuterRingColor() const +{ + return this->outerRingColor; +} + +QColor GaugeWeather::getOuterValueTextColor() const +{ + return this->outerValueTextColor; +} + +double GaugeWeather::getInnerMinValue() const +{ + return this->innerMinValue; +} + +double GaugeWeather::getInnerMaxValue() const +{ + return this->innerMaxValue; +} + +double GaugeWeather::getInnerValue() const +{ + return this->innerValue; +} + +int GaugeWeather::getInnerStartAngle() const +{ + return this->innerStartAngle; +} + +int GaugeWeather::getInnerEndAngle() const +{ + return this->innerEndAngle; +} + +QColor GaugeWeather::getInnerRingBgColor() const +{ + return this->innerRingBgColor; +} + +QColor GaugeWeather::getInnerNegativeColor() const +{ + return this->innerRingNegativeColor; +} + +QColor GaugeWeather::getInnerPositiveColor() const +{ + return this->innerRingPositiveColor; +} + +int GaugeWeather::getInnerScaleMajor() const +{ + return this->innerScaleMajor; +} + +int GaugeWeather::getInnerScaleMinor() const +{ + return this->innerScaleMinor; +} + +QColor GaugeWeather::getInnerScaleColor() const +{ + return this->innerScaleColor; +} + +QColor GaugeWeather::getInnerScaleNumColor() const +{ + return this->innerScaleNumColor; +} + +QColor GaugeWeather::getInnerNegativeValueTextColor() const +{ + return this->innerNegativeValueTextColor; +} + +QColor GaugeWeather::getInnerPositiveValueTextColor() const +{ + return this->innerPositiveValueTextColor; +} + +QColor GaugeWeather::getCenterPixMapNegativeColor() const +{ + return this->centerPixMapNegativeColor; +} + +QColor GaugeWeather::getCenterPixMapPositiveColor() const +{ + return this->centerPixMapPositiveColor; +} + +bool GaugeWeather::getAnimation() const +{ + return this->animation; +} + +double GaugeWeather::getAnimationStep() const +{ + return this->animationStep; +} + +GaugeWeather::WeatherType GaugeWeather::getWeatherType() const +{ + return this->weatherType; +} + +QSize GaugeWeather::sizeHint() const +{ + return QSize(200, 200); +} + +QSize GaugeWeather::minimumSizeHint() const +{ + return QSize(50, 50); +} + +void GaugeWeather::setOuterRange(double minValue, double maxValue) +{ + //如果最小值大于或者等于最大值则不设置 + if (minValue >= maxValue) + { + return; + } + + this->outerMinValue = minValue; + this->outerMaxValue = maxValue; + + //如果目标值不在范围值内,则重新设置目标值 + if (outerValue < minValue || outerValue > maxValue) + { + setOuterValue(outerValue); + } + + this->update(); +} + +void GaugeWeather::setOuterMinValue(double value) +{ + setOuterRange(value, outerMaxValue); +} + +void GaugeWeather::setOuterMaxValue(double value) +{ + setOuterRange(outerMinValue, value); +} + +void GaugeWeather::setOuterValue(double value) +{ + //值小于最小值或者值大于最大值或者值和当前值一致则无需处理 + if (value < outerMinValue || value > outerMaxValue || value == this->outerValue) + { + return; + } + + if (value > this->outerValue) + { + outerReverse = false; + } + else if (value < this->outerValue) + { + outerReverse = true; + } + + this->outerValue = value; + emit valueChanged(value); + + if (!animation) + { + outerCurrValue = this->outerValue; + this->update(); + } + else + { + outerTimer->start(); + } +} + +void GaugeWeather::setOuterStartAngle(int startAngle) +{ + if (this->outerStartAngle != startAngle) + { + this->outerStartAngle = startAngle; + this->update(); + } +} + +void GaugeWeather::setOuterEndAngle(int endAngle) +{ + if (this->outerEndAngle != endAngle) + { + this->outerEndAngle = endAngle; + this->update(); + } +} + +void GaugeWeather::setOuterRingBgColor(const QColor &color) +{ + if (this->outerRingBgColor != color) + { + this->outerRingBgColor = color; + this->update(); + } +} + +void GaugeWeather::setOuterRingColor(const QColor &color) +{ + if (this->outerRingColor != color) + { + this->outerRingColor = color; + this->update(); + } +} + +void GaugeWeather::setOuterValueTextColor(const QColor &color) +{ + if (this->outerValueTextColor != color) + { + this->outerValueTextColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerRange(double minValue, double maxValue) +{ + //如果最小值大于或者等于最大值则不设置 + if (minValue >= maxValue) + { + return; + } + + this->innerMinValue = minValue; + this->innerMaxValue = maxValue; + + //如果目标值不在范围值内,则重新设置目标值 + if (innerValue < minValue || innerValue > maxValue) + { + setInnerValue(innerValue); + } + + this->update(); +} + +void GaugeWeather::setInnerMinValue(double value) +{ + setInnerRange(value, innerMaxValue); +} + +void GaugeWeather::setInnerMaxValue(double value) +{ + setInnerRange(innerMinValue, value); +} + +void GaugeWeather::setInnerValue(double value) +{ + //值小于最小值或者值大于最大值或者值和当前值一致则无需处理 + if (value < innerMinValue || value > innerMaxValue || value == this->innerValue) + { + return; + } + + if(this->innerValue >= 0) + { + clockWise = true; + if(value < this->innerValue) + { + //如果要设置的值小于已有的值,需要回滚 + innerReverse = true; + } + else + { + innerReverse = false; + } + } + else + { + clockWise = false; + if(value > this->innerValue) + { + //如果要设置的值大于已有的值,需要回滚 + innerReverse = true; + } + else + { + innerReverse = false; + } + } + + this->innerValue = value; + emit valueChanged(value); + + if (!animation) + { + innerCurrValue = this->innerValue; + this->update(); + } + else + { + innerTimer->start(); + } +} + +void GaugeWeather::setInnerStartAngle(int startAngle) +{ + if (this->innerStartAngle != startAngle) + { + this->innerStartAngle = startAngle; + this->update(); + } +} + +void GaugeWeather::setInnerEndAngle(int endAngle) +{ + if (this->innerEndAngle != endAngle) + { + this->innerEndAngle = endAngle; + this->update(); + } +} + +void GaugeWeather::setInnerRingBgColor(const QColor &color) +{ + if (this->innerRingBgColor != color) + { + this->innerRingBgColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerNegativeColor(const QColor &color) +{ + if (this->innerRingNegativeColor != color) + { + this->innerRingNegativeColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerPositiveColor(const QColor &color) +{ + if (this->innerRingPositiveColor != color) + { + this->innerRingPositiveColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerScaleMajor(int value) +{ + if (this->innerScaleMajor != value) + { + this->innerScaleMajor = value; + this->update(); + } +} + +void GaugeWeather::setInnerScaleMinor(int value) +{ + if (this->innerScaleMinor != value) + { + this->innerScaleMinor = value; + this->update(); + } +} + +void GaugeWeather::setInnerScaleColor(const QColor &color) +{ + if (this->innerScaleColor != color) + { + this->innerScaleColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerScaleNumColor(const QColor &color) +{ + if (this->innerScaleNumColor != color) + { + this->innerScaleNumColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerNegativeValueTextColor(const QColor &color) +{ + if (this->innerNegativeValueTextColor != color) + { + this->innerNegativeValueTextColor = color; + this->update(); + } +} + +void GaugeWeather::setInnerPositiveValueTextColor(const QColor &color) +{ + if (this->innerPositiveValueTextColor != color) + { + this->innerPositiveValueTextColor = color; + this->update(); + } +} + +void GaugeWeather::setCenterPixMapNegativeColor(const QColor &color) +{ + if (this->centerPixMapNegativeColor != color) + { + this->centerPixMapNegativeColor = color; + this->update(); + } +} + +void GaugeWeather::setCenterPixMapPositiveColor(const QColor &color) +{ + if (this->centerPixMapPositiveColor != color) + { + this->centerPixMapPositiveColor = color; + this->update(); + } +} + +void GaugeWeather::setAnimation(bool animation) +{ + if (this->animation != animation) + { + this->animation = animation; + this->update(); + } +} + +void GaugeWeather::setAnimationStep(double animationStep) +{ + if (this->animationStep != animationStep) + { + this->animationStep = animationStep; + this->update(); + } +} + +void GaugeWeather::setWeatherType(GaugeWeather::WeatherType &type) +{ + if(this->weatherType != type) + { + this->weatherType = type; + switch (type) + { + case WeatherType_SUNNY: + centerSvgPath = ":/svg/weather-sunny.svg"; + break; + case WeatherType_RAINY: + centerSvgPath = ":/svg/weather-rainy.svg"; + break; + case WeatherType_SNOWY: + centerSvgPath = ":/svg/weather-snowy.svg"; + break; + case WeatherType_CLOUDY: + centerSvgPath = ":/svg/weather-cloudy.svg"; + break; + case WeatherType_WINDY: + centerSvgPath = ":/svg/weather-windy.svg"; + break; + case WeatherType_SNOWY_RAINY: + centerSvgPath = ":/svg/weather-snowy-rainy.svg"; + break; + case WeatherType_HAIL: + centerSvgPath = ":/svg/weather-hail.svg"; + break; + case WeatherType_LIGHTNING: + centerSvgPath = ":/svg/weather-lightning.svg"; + break; + case WeatherType_FOG: + centerSvgPath = ":/svg/weather-fog.svg"; + break; + case WeatherType_PARTLYCLOUDY: + centerSvgPath = ":/svg/weather-partlycloudy.svg"; + break; + default: + centerSvgPath = ":/svg/weather-sunny.svg"; + break; + } + + this->update(); + } +} + +void GaugeWeather::updateOuterValue() +{ + if (!outerReverse) + { + if (outerCurrValue >= outerValue) + { + outerCurrValue = outerValue; + outerTimer->stop(); + } + else + { + outerCurrValue += animationStep; + } + } + else + { + if (outerCurrValue <= outerValue) + { + outerCurrValue = outerValue; + outerTimer->stop(); + } + else + { + outerCurrValue -= animationStep; + } + } + + this->update(); +} + +void GaugeWeather::updateInnerValue() +{ + //currValue变成Value的过程 + if(clockWise) + { + //针对value大于零时,当currValue 小于 Value,需要回滚,否则前滚, + if (!innerReverse) + { + if (innerCurrValue >= innerValue) + { + innerCurrValue = innerValue; + innerTimer->stop(); + } + else + { + innerCurrValue += animationStep; + } + } + else + { + if (innerCurrValue <= innerValue) + { + innerCurrValue = innerValue; + innerTimer->stop(); + } + else + { + innerCurrValue -= animationStep; + } + } + + } + else + { + //针对value大于零时,当currValue 大于 Value,需要回滚,否则前滚, + if (!innerReverse) + { + if (innerCurrValue <= innerValue) + { + innerCurrValue = innerValue; + innerTimer->stop(); + } + else + { + innerCurrValue -= animationStep; + } + } + else + { + if (innerCurrValue >= innerValue) + { + innerCurrValue = innerValue; + innerTimer->stop(); + } + else + { + innerCurrValue += animationStep; + } + } + } + + this->update(); +} diff --git a/applications/examples/quc_demo/gaugeweather.h b/applications/examples/quc_demo/gaugeweather.h new file mode 100644 index 0000000..a6f9c7d --- /dev/null +++ b/applications/examples/quc_demo/gaugeweather.h @@ -0,0 +1,252 @@ +#ifndef GAUGEWEATHER_H +#define GAUGEWEATHER_H + +/** + * 天气仪表盘控件 作者:东门吹雪(QQ:709102202) 整理:飞扬青云(QQ:517216493) 2019-4-23 + * 1:可设置两种值,比如温度+湿度 + * 2:可设置两种值的背景颜色+文字颜色 + * 3:可设置零度值左侧右侧两种颜色 + * 4:可设置圆的起始角度和结束角度 + * 5:可设置10种天气,晴天+雨天+阴天+大风等 + * 6:可设置各种其他颜色 + * 7:科设置是否启用动画显示进度以及动画步长 + */ + +#include +#include +//#include + +#ifdef quc +#if (QT_VERSION < QT_VERSION_CHECK(5,7,0)) +#include +#else +#include +#endif + +class QDESIGNER_WIDGET_EXPORT GaugeWeather : public QWidget +#else +class GaugeWeather : public QWidget +#endif + +{ + Q_OBJECT + Q_ENUMS(WeatherType) + + Q_PROPERTY(double outerValue READ getOuterValue WRITE setOuterValue) + Q_PROPERTY(double outerMinValue READ getOuterMinValue WRITE setOuterMinValue) + Q_PROPERTY(double outerMaxValue READ getOuterMaxValue WRITE setOuterMaxValue) + + Q_PROPERTY(int outerStartAngle READ getOuterStartAngle WRITE setOuterStartAngle) + Q_PROPERTY(int outerEndAngle READ getOuterEndAngle WRITE setOuterEndAngle) + + Q_PROPERTY(QColor outerRingBgColor READ getOuterRingBgColor WRITE setOuterRingBgColor) + Q_PROPERTY(QColor outerRingColor READ getOuterRingColor WRITE setOuterRingColor) + + Q_PROPERTY(double innerValue READ getInnerValue WRITE setInnerValue) + Q_PROPERTY(double innerMinValue READ getInnerMinValue WRITE setInnerMinValue) + Q_PROPERTY(double innerMaxValue READ getInnerMaxValue WRITE setInnerMaxValue) + + Q_PROPERTY(int innerStartAngle READ getInnerStartAngle WRITE setInnerStartAngle) + Q_PROPERTY(int innerEndAngle READ getInnerEndAngle WRITE setInnerEndAngle) + + Q_PROPERTY(QColor innerRingBgColor READ getInnerRingBgColor WRITE setInnerRingBgColor) + Q_PROPERTY(QColor innerRingNegativeColor READ getInnerNegativeColor WRITE setInnerNegativeColor) + Q_PROPERTY(QColor innerRingPositiveColor READ getInnerPositiveColor WRITE setInnerPositiveColor) + + Q_PROPERTY(int innerScaleMajor READ getInnerScaleMajor WRITE setInnerScaleMajor) + Q_PROPERTY(int innerScaleMinor READ getInnerScaleMinor WRITE setInnerScaleMinor) + Q_PROPERTY(QColor innerScaleColor READ getInnerScaleColor WRITE setInnerScaleColor) + Q_PROPERTY(QColor innerScaleNumColor READ getInnerScaleNumColor WRITE setInnerScaleNumColor) + + Q_PROPERTY(QColor centerPixMapNegativeColor READ getCenterPixMapNegativeColor WRITE setCenterPixMapNegativeColor) + Q_PROPERTY(QColor centerPixMapPositiveColor READ getCenterPixMapPositiveColor WRITE setCenterPixMapPositiveColor) + + Q_PROPERTY(QColor outerValueTextColor READ getOuterValueTextColor WRITE setOuterValueTextColor) + Q_PROPERTY(QColor innerNegativeValueTextColor READ getInnerNegativeValueTextColor WRITE setInnerNegativeValueTextColor) + Q_PROPERTY(QColor innerPositiveValueTextColor READ getInnerPositiveValueTextColor WRITE setInnerPositiveValueTextColor) + + Q_PROPERTY(bool animation READ getAnimation WRITE setAnimation) + Q_PROPERTY(double animationStep READ getAnimationStep WRITE setAnimationStep) + Q_PROPERTY(WeatherType weatherType READ getWeatherType WRITE setWeatherType) + +public: + enum WeatherType + { + WeatherType_SUNNY = 0, //晴天 + WeatherType_RAINY = 1, //雨天 + WeatherType_SNOWY = 2, //雪天 + WeatherType_CLOUDY = 3, //多云 + WeatherType_WINDY = 4, //风 + WeatherType_SNOWY_RAINY = 5, //雪雨 + WeatherType_HAIL = 6, //冰雹 + WeatherType_LIGHTNING = 7, //闪电 + WeatherType_FOG = 8, //雾 + WeatherType_PARTLYCLOUDY = 9 //局部多云 + }; + + explicit GaugeWeather(QWidget *parent = 0); + ~GaugeWeather(); + +protected: + void paintEvent(QPaintEvent *); + void drawOuterRingBg(QPainter *painter); + void drawOuterRing(QPainter *painter); + void drawInnerRingBg(QPainter *painter); + void drawInnerRing(QPainter *painter); + void drawInnerRingScale(QPainter *painter); + void drawInnerRingScaleNum(QPainter *painter); + void drawCenterPixMap(QPainter *painter); + void drawValue(QPainter *painter); + +private slots: + void updateOuterValue(); //更新外圈数值 + void updateInnerValue(); //更新内圈数值 + +private: + double outerMaxValue; //外圈最大值 + double outerMinValue; //外圈最小值 + double outerValue; //外圈值 + double outerCurrValue; //外圈当前值 + + int outerStartAngle; //外环开始旋转角度 + int outerEndAngle; //外环结束旋转角度 + + QColor outerRingBgColor; //外圈背景色 + QColor outerRingColor; //外圈当前色 + + double innerMaxValue; //内圈最大值 + double innerMinValue; //内圈最小值 + double innerValue; //内圈值 + double innerCurrValue; //内圈当前值 + + int innerStartAngle; //内环开始旋转角度 + int innerEndAngle; //内环结束旋转角度 + + QColor innerRingBgColor; //内圈背景色 + QColor innerRingNegativeColor; //内圈负值当前色 + QColor innerRingPositiveColor; //内圈正值当前色 + + int innerScaleMajor; //内环大刻度数量 + int innerScaleMinor; //内环小刻度数量 + QColor innerScaleColor; //内环刻度颜色 + QColor innerScaleNumColor; //内环刻度值颜色 + int precision; //精确度,小数点后几位 + + QColor centerPixMapNegativeColor; //中心图片颜色 + QColor centerPixMapPositiveColor; //中心图片颜色 + QString centerSvgPath; //当前svg图片路径 + WeatherType weatherType; //天气类型 + + QColor outerValueTextColor; //外环值文本颜色 + QColor innerNegativeValueTextColor; //内环正值文本颜色 + QColor innerPositiveValueTextColor; //内环负值文本颜色 + + bool animation; //是否启用动画显示 + double animationStep; //动画显示时步长 + + bool outerReverse; //是否往回走 + bool innerReverse; //是否往回走 + bool clockWise; //顺时针 + + QTimer *outerTimer; //外环定时器绘制动画 + QTimer *innerTimer; //内环定时器绘制动画 + + //将svg文件中的xml数据颜色替换 + //void setColor(QDomElement elem, QString strtagname, QString strattr, QString strattrval); + QString rgb2HexStr(const QColor &color); + +public: + double getOuterMinValue() const; + double getOuterMaxValue() const; + double getOuterValue() const; + int getOuterStartAngle() const; + int getOuterEndAngle() const; + + QColor getOuterRingBgColor() const; + QColor getOuterRingColor() const; + + double getInnerMaxValue() const; + double getInnerMinValue() const; + double getInnerValue() const; + int getInnerStartAngle() const; + int getInnerEndAngle() const; + + QColor getInnerRingBgColor() const; + QColor getInnerNegativeColor() const; + QColor getInnerPositiveColor() const; + + int getInnerScaleMajor() const; + int getInnerScaleMinor() const; + QColor getInnerScaleColor() const; + QColor getInnerScaleNumColor() const; + + bool getAnimation() const; + double getAnimationStep() const; + + WeatherType getWeatherType() const; + + QColor getCenterPixMapNegativeColor() const; + QColor getCenterPixMapPositiveColor() const; + + QColor getOuterValueTextColor() const; + QColor getInnerNegativeValueTextColor() const; + QColor getInnerPositiveValueTextColor() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public Q_SLOTS: + void setWeatherType(WeatherType &type); + + //设置范围值 + void setOuterRange(double minValue, double maxValue); + //设置外环最大最小值 + void setOuterMinValue(double value); + void setOuterMaxValue(double value); + + //设置外环值 + void setOuterValue(double value); + //设置外环开始旋转角度 + void setOuterStartAngle(int startAngle); + //设置外环结束旋转角度 + void setOuterEndAngle(int endAngle); + //设置外环背景色 + void setOuterRingBgColor(const QColor &color); + //设置外环进度色 + void setOuterRingColor(const QColor &color); + + //设置范围值 + void setInnerRange(double minValue, double maxValue); + void setInnerMinValue(double value); + void setInnerMaxValue(double value); + void setInnerValue(double value); + void setInnerStartAngle(int startAngle); + void setInnerEndAngle(int endAngle); + + void setInnerRingBgColor(const QColor &color); + void setInnerNegativeColor(const QColor &color); + void setInnerPositiveColor(const QColor &color); + + void setInnerScaleMajor(int value); + void setInnerScaleMinor(int value); + void setInnerScaleColor(const QColor &color); + void setInnerScaleNumColor(const QColor &color); + + //设置中心图标颜色 + void setCenterPixMapNegativeColor(const QColor &color); + void setCenterPixMapPositiveColor(const QColor &color); + + void setOuterValueTextColor(const QColor &color); + void setInnerNegativeValueTextColor(const QColor &color); + void setInnerPositiveValueTextColor(const QColor &color); + + //设置是否启用动画显示 + void setAnimation(bool animation); + //设置动画显示的步长 + void setAnimationStep(double animationStep); + +Q_SIGNALS: + void valueChanged(double value); +}; + +#endif // GAUGEWEATHER_H diff --git a/applications/examples/quc_demo/image/bg2.png b/applications/examples/quc_demo/image/bg2.png new file mode 100644 index 0000000..7a06659 Binary files /dev/null and b/applications/examples/quc_demo/image/bg2.png differ diff --git a/applications/examples/quc_demo/res.qrc b/applications/examples/quc_demo/res.qrc new file mode 100644 index 0000000..8b3d815 --- /dev/null +++ b/applications/examples/quc_demo/res.qrc @@ -0,0 +1,5 @@ + + + image/bg2.png + +