mirror of
https://gitee.com/js-yhsec/energy_storage.git
synced 2026-05-28 03:09:24 +08:00
实现服务端的QT应用界面
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "MainApp.h"
|
||||
#include "common/Spdlogger.h"
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtWidgets/QApplication>
|
||||
@@ -10,19 +11,25 @@
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QPalette>
|
||||
#include <QGroupBox>
|
||||
#include <QTableWidget>
|
||||
#include <QHeaderView>
|
||||
|
||||
#include "app/Config.h"
|
||||
#include "common/Utils.h"
|
||||
#include "QUI.h"
|
||||
#include "app/Application.h"
|
||||
#include "app/AppData.h"
|
||||
#include "app/Station.h"
|
||||
#include "protocol/MqttEntity.h"
|
||||
|
||||
static const std::string QSS_BTN_MENU =
|
||||
"QPushButton{background:rgba(50,128,218,200);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
|
||||
"QPushButton:hover{background-color:rgb(32,164,128);}"
|
||||
"QPushButton:pressed{border-width:3px 0 0 3px;background-color:rgb(1,32,54);border-style:inset;}"
|
||||
"QPushButton:disabled{color:rgb(150,150,150);}";
|
||||
"QPushButton {background:rgba(50,128,218,200);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
|
||||
"QPushButton:hover {background-color:rgb(32,164,128);}"
|
||||
"QPushButton:pressed {border-width:3px 0 0 3px;background-color:rgb(1,32,54);border-style:inset;}"
|
||||
"QPushButton:disabled {color:rgb(150,150,150);}";
|
||||
|
||||
static const std::string QSS_LINE =
|
||||
"QLineEdit { background-color: rgb(14, 49, 66); color: #ffffff; border: 1px solid rgb(18, 251, 255); border-radius: 5px; font: normal 13px; }";
|
||||
|
||||
static const std::string QSS_GROUP =
|
||||
"QGroupBox { border: 1px solid gray; margin-top: 8px; border-radius: 5px;}"
|
||||
"QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left:10px; margin-left: 0px; padding:0 1px; }";
|
||||
"QLineEdit { background-color: rgb(14, 49, 66); color: #ffffff; border: 1px solid gray; border-radius: 5px; font: bold 13px; }";
|
||||
|
||||
MainApp::MainApp()
|
||||
{
|
||||
@@ -45,8 +52,14 @@ MainApp::MainApp()
|
||||
//ui.weburl->setValue("http://www.baidu.com");
|
||||
|
||||
this->setMyLayout();
|
||||
|
||||
timer = std::make_shared<QTimer>(this);
|
||||
connect(timer.get(), &QTimer::timeout, this, &MainApp::onTimer);
|
||||
timer->start(1000); // 每隔1000毫秒更新一次
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MyMenu : public QWidget
|
||||
{
|
||||
public:
|
||||
@@ -61,7 +74,7 @@ public:
|
||||
layout.setContentsMargins(2, 2, 2, 2);
|
||||
|
||||
this->addMenuItem("系统总览");
|
||||
this->addMenuItem("运行监控");
|
||||
//this->addMenuItem("运行监控");
|
||||
}
|
||||
|
||||
void addMenuItem(std::string name)
|
||||
@@ -95,8 +108,9 @@ void PairLine(QWidget* parent, int x, int y, string k, string v)
|
||||
key->setGeometry(x, y, 80, 26);
|
||||
auto value = new QLineEdit(parent);
|
||||
value->setText(v.c_str());
|
||||
value->setGeometry(x+80, y, 180, 26);
|
||||
value->setGeometry(x+80, y, 260, 26);
|
||||
value->setStyleSheet(QSS_LINE.c_str());
|
||||
value->setReadOnly(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,36 +124,144 @@ public:
|
||||
|
||||
int x=10, y=10;
|
||||
{
|
||||
QGroupBox* groupBox = new QGroupBox("HTTP", this);
|
||||
groupBox->setGeometry(x, y, 300, 120);
|
||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
||||
|
||||
PairLine(groupBox, 20, 20, "服务类型: ", "服务端");
|
||||
PairLine(groupBox, 20, 50, "服务地址: ", "92.168.0.13:17900");
|
||||
PairLine(groupBox, 20, 80, "服务状态: ", "---");
|
||||
this->groupSys = UI::GroupBox(this, x, y, 1190, 120, "系统");
|
||||
auto pw = groupSys.get();
|
||||
}
|
||||
{
|
||||
x += 320;
|
||||
QGroupBox* groupBox = new QGroupBox("MQTT", this);
|
||||
groupBox->setGeometry(x, y, 300, 120);
|
||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
||||
|
||||
PairLine(groupBox, 20, 20, "服务类型: ", "客户端");
|
||||
PairLine(groupBox, 20, 50, "服务地址: ", "92.168.0.13:17800");
|
||||
PairLine(groupBox, 20, 80, "服务状态: ", "---");
|
||||
x = 10, y += 130;
|
||||
this->groupHttp = UI::GroupBox(this, x, y, 390, 120, "HTTP");
|
||||
auto pw = groupHttp.get();
|
||||
PairLine(pw, 20, 20, "服务类型: ", "服务端");
|
||||
PairLine(pw, 20, 50, "服务端口: ", Utils::toStr(Config::option.http.port));
|
||||
PairLine(pw, 20, 80, "服务状态: ", "运行");
|
||||
}
|
||||
{
|
||||
x += 320;
|
||||
QGroupBox* groupBox = new QGroupBox("数据库", this);
|
||||
groupBox->setGeometry(x, y, 300, 120);
|
||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
||||
x += 400;
|
||||
this->groupMqtt = UI::GroupBox(this, x, y, 390, 120, "MQTT");
|
||||
auto pw = groupMqtt.get();
|
||||
PairLine(pw, 20, 20, "服务类型: ", "客户端");
|
||||
PairLine(pw, 20, 50, "服务地址: ", Config::option.mqtt.host);
|
||||
PairLine(pw, 20, 80, "服务状态: ", "---");
|
||||
}
|
||||
{
|
||||
x += 400;
|
||||
this->groupDB = UI::GroupBox(this, x, y, 390, 120, "数据库");
|
||||
auto pw = groupDB.get();
|
||||
PairLine(pw, 20, 20, "数据库名: ", Config::option.database.dbname);
|
||||
PairLine(pw, 20, 50, "主机地址: ", Config::option.database.host);
|
||||
PairLine(pw, 20, 80, "用 户 名: ", Config::option.database.user);
|
||||
|
||||
PairLine(groupBox, 20, 20, "数据库名: ", "ees");
|
||||
PairLine(groupBox, 20, 50, "主机地址: ", "92.168.0.13:17800");
|
||||
PairLine(groupBox, 20, 80, "用 户 名: ", "root");
|
||||
|
||||
const std::string QSS_TABLE = // 表格整体样式
|
||||
"QTableWidget {"
|
||||
" background-color: transparent;" // 背景色
|
||||
" gridline-color: #C0C0C0;" // 网格线颜色
|
||||
" border: 1px solid gray;" // 边框
|
||||
" color: white;" // 文字颜色
|
||||
"}"
|
||||
// 表头样式
|
||||
"QHeaderView::section {"
|
||||
" background-color: #404040;" // 表头背景
|
||||
" padding: 4px;" // 内边距
|
||||
" border: 1px solid #505050;" // 边框
|
||||
" min-height: 25px;" // 最小高度
|
||||
"}"
|
||||
// 单元格样式
|
||||
"QTableWidget::item {"
|
||||
" padding-left: 5px;"
|
||||
" border-bottom: 1px solid gray;" // 底部边框
|
||||
"}"
|
||||
// 选中状态
|
||||
"QTableWidget::item:selected {"
|
||||
" background-color: #B8D6FF;" // 选中背景色
|
||||
" color: black;" // 选中文字颜色
|
||||
"}";
|
||||
|
||||
table = std::make_shared<QTableWidget>(this);
|
||||
table->setGeometry(10, y += 130, 1190, 300);
|
||||
table->setStyleSheet(QSS_TABLE.c_str());
|
||||
table->horizontalHeader()->setStretchLastSection(true); // 最后一列占满
|
||||
table->verticalHeader()->setVisible(false); // 不显示垂直表头
|
||||
table->setEditTriggers(QAbstractItemView::NoEditTriggers); // 单元格不可编辑
|
||||
table->setSelectionMode(QAbstractItemView::SingleSelection); // 设置为单选模式
|
||||
table->setSelectionBehavior(QAbstractItemView::SelectRows); // 设置为整行选中
|
||||
|
||||
|
||||
QTableWidgetItem* headerItem;
|
||||
QStringList headerText_Row, headerText_Col;
|
||||
headerText_Row << "站ID" << "站名称" << "站编号" << "站状态" << "MQTT状态" << "召测(秒)" << "说明";
|
||||
|
||||
// 设置为水平表头
|
||||
table->setColumnCount(headerText_Row.size());
|
||||
table->setHorizontalHeaderLabels(headerText_Row);
|
||||
}
|
||||
|
||||
textLog = std::make_shared<QTextEdit>(this);
|
||||
textLog->setGeometry(10, y += 310, 1190, 280);
|
||||
textLog->setStyleSheet("background-color: transparent; border: 1px solid gray; font-weight: 400;");
|
||||
textLog->setReadOnly(true);
|
||||
|
||||
{
|
||||
// 第二个参数是方法函数名称,即调用 QTextEdit的appeng函数;
|
||||
auto qtSink = std::make_shared<spdlog::sinks::qt_sink_mt>(textLog.get(), "append");
|
||||
spdlog::default_logger()->sinks().push_back(qtSink);
|
||||
}
|
||||
{
|
||||
//qtSink = std::make_shared<QtTextSink>(textLog.get());
|
||||
//connect(qtSink.get(), &QtTextSink::appendText, textLog.get(), &QTextEdit::append);
|
||||
}
|
||||
}
|
||||
|
||||
void setTableCell(int row, int col, std::string text, std::string style="")
|
||||
{
|
||||
auto item = table->item(row, col);
|
||||
if (!item)
|
||||
{
|
||||
item = new QTableWidgetItem();
|
||||
table->setItem(row, col, item);
|
||||
}
|
||||
item->setText(text.c_str());
|
||||
if (style == "OK") { item->setForeground(QBrush(Qt::green)); }
|
||||
else if (style == "ERR") { item->setForeground(QBrush(Qt::red)); }
|
||||
}
|
||||
|
||||
void onTimer()
|
||||
{
|
||||
auto& appdata = Application().data();
|
||||
int rowNo = 0;
|
||||
int tsNow = Utils::time();
|
||||
for (auto& item : appdata.mapStation)
|
||||
{
|
||||
auto& station = item.second;
|
||||
if (rowNo >= table->rowCount())
|
||||
{
|
||||
table->insertRow(rowNo);
|
||||
}
|
||||
bool isOpen = station->status > 0;
|
||||
bool isConnected = station->mqttCli->isConnected;
|
||||
|
||||
setTableCell(rowNo, 0, std::to_string(station->stationId));
|
||||
setTableCell(rowNo, 0, std::to_string(station->stationId));
|
||||
setTableCell(rowNo, 1, station->name);
|
||||
setTableCell(rowNo, 2, station->code);
|
||||
setTableCell(rowNo, 3, isOpen ? "启用" : "未启用", isOpen ? "OK" : "ERR");
|
||||
setTableCell(rowNo, 4, isConnected ? "连接成功" : "未连接", isConnected ? "OK" : "ERR");
|
||||
|
||||
int tsPolling = station->getPollingTS();
|
||||
setTableCell(rowNo, 5, tsPolling > 0 ? std::to_string(tsNow - tsPolling) + "/" + std::to_string(Config::option.mqtt.interval) : "--");
|
||||
rowNo++;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建自定义sink
|
||||
std::shared_ptr<QtTextSink> qtSink;
|
||||
|
||||
std::shared_ptr<QGroupBox> groupSys;
|
||||
std::shared_ptr<QGroupBox> groupHttp;
|
||||
std::shared_ptr<QGroupBox> groupMqtt;
|
||||
std::shared_ptr<QGroupBox> groupDB;
|
||||
std::shared_ptr<QTableWidget> table {};
|
||||
std::shared_ptr<QTextEdit> textLog;
|
||||
};
|
||||
|
||||
void MainApp::setMyLayout()
|
||||
@@ -162,9 +284,9 @@ void MainApp::setMyLayout()
|
||||
menu->setSizePolicy(sizePolicy);
|
||||
layout.main->addWidget(menu, 0, 0, 1, 1);
|
||||
|
||||
MyWorkspace* workspace = new MyWorkspace(this);
|
||||
workspace->setSizePolicy(sizePolicy);
|
||||
layout.main->addWidget(workspace, 0, 1, 1, 1);
|
||||
ui.workspace = std::make_shared<MyWorkspace>(this);
|
||||
ui.workspace->setSizePolicy(sizePolicy);
|
||||
layout.main->addWidget(ui.workspace.get(), 0, 1, 1, 1);
|
||||
|
||||
// 设置列宽和行高
|
||||
layout.main->setColumnMinimumWidth(0, 200); // 设置第0列的最小宽度为100像素
|
||||
@@ -175,4 +297,11 @@ void MainApp::setMyLayout()
|
||||
layout.main->setColumnStretch(1, 2); // 设置第1列的伸缩因子为2,使其更宽
|
||||
//gridLayout->setRowStretch(0, 1); // 设置第0行的伸缩因子为1
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MainApp::onTimer()
|
||||
{
|
||||
ui.workspace->onTimer();
|
||||
}
|
||||
@@ -2,9 +2,54 @@
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTableWidget>
|
||||
#include <QTimer>
|
||||
#include <QTextEdit>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QTextEdit>
|
||||
#include <mutex>
|
||||
|
||||
class QtTextSink : public QObject, public spdlog::sinks::base_sink<std::mutex>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// 构造函数接收一个 QTextEdit*,但用 QPointer 来包装它
|
||||
explicit QtTextSink(QTextEdit* textEdit, QObject* parent = nullptr)
|
||||
: QObject(parent), textEdit(textEdit) {
|
||||
}
|
||||
|
||||
signals:
|
||||
// 定义一个信号,用于在主线程中追加文本
|
||||
void appendText(const QString& message);
|
||||
|
||||
protected:
|
||||
// 重写 sink_it_ 方法,处理每条日志
|
||||
void sink_it_(const spdlog::details::log_msg& msg) override {
|
||||
if (textEdit.isNull()) {
|
||||
// 如果 QTextEdit 已经被删除,则忽略这条日志
|
||||
return;
|
||||
}
|
||||
spdlog::memory_buf_t formatted;
|
||||
formatter_->format(msg, formatted);
|
||||
QString logMessage = QString::fromStdString(fmt::to_string(formatted));
|
||||
// 发射信号,通过Qt的事件循环在主线程中安全地更新UI
|
||||
emit appendText(logMessage);
|
||||
}
|
||||
|
||||
void flush_() override {}
|
||||
|
||||
private:
|
||||
QPointer<QTextEdit> textEdit; // 关键:使用 QPointer
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class LabelPair
|
||||
{
|
||||
public:
|
||||
@@ -34,6 +79,9 @@ public:
|
||||
QLabel value;
|
||||
};
|
||||
|
||||
|
||||
class MyWorkspace;
|
||||
|
||||
class MainApp : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -42,12 +90,19 @@ public:
|
||||
|
||||
void setMyLayout();
|
||||
|
||||
|
||||
private slots:
|
||||
void onTimer();
|
||||
|
||||
public:
|
||||
struct {
|
||||
std::shared_ptr<LabelPair> weburl {};
|
||||
std::shared_ptr<MyWorkspace> workspace;
|
||||
} ui;
|
||||
|
||||
struct {
|
||||
std::shared_ptr<QGridLayout> main;
|
||||
} layout;
|
||||
|
||||
std::shared_ptr<QTimer> timer;
|
||||
};
|
||||
14
src/qt/QUI.cpp
Normal file
14
src/qt/QUI.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "QUI.h"
|
||||
|
||||
static const std::string QSS_GROUP =
|
||||
"QGroupBox { border: 1px solid gray; margin-top: 8px; border-radius: 5px;}"
|
||||
"QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left:10px; margin-left: 0px; padding:0 1px; }";
|
||||
|
||||
|
||||
std::shared_ptr<QGroupBox> UI::GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title)
|
||||
{
|
||||
auto groupBox = std::make_shared<QGroupBox>(title.c_str(), parent);
|
||||
groupBox->setGeometry(x, y, w, h);
|
||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
||||
return groupBox;
|
||||
}
|
||||
10
src/qt/QUI.h
Normal file
10
src/qt/QUI.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <QGroupBox>
|
||||
|
||||
|
||||
class UI
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<QGroupBox> GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title);
|
||||
};
|
||||
Reference in New Issue
Block a user