Files
energy_storage/src/pv/PvTable.cpp
2025-08-31 14:38:53 +08:00

402 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "PvTable.h"
#include "PvStyle.h"
#include "pv/PvApp.h"
#include "pv/PvPopWidget.h"
static const string STYLE_BKG =
"border-width:1 1 1 1px; border-style:outset solid; border-color:rgba(180,180,180,255);"
"background-color:rgba(80,80,80,0);";
//*********************************************************************************************************************
// PvTable
PvTable::PvTable(PARAM* p, int parent, int x, int y, int w, int irow, Options& opts)
//: PvWidget(p, parent, PvRect(x, y, w, opts.item_height* row + (opts.show_header ? (opts.header_height) : 0))),
: PvObject(p), option(opts), nrow(irow), ncol(0)
{
// 计算表格的显示区域
int h = opts.row_height* irow + (opts.show_header ? (opts.head_height) : 0);
rect.set(x, y, w, h);
// 表格的主窗体QWidget设置样式无效
pvid = PvApp::widget(p, parent, x, y, w, h+1);
// 表格的背景色和边框样式
PvApp::label(p, pvid, 0, 0, w, h+1, "", QSS::QSS_TABLE);
vecHeads.resize(0);
vecRows.resize(nrow);
vecData.resize(nrow);
// 创建行高亮显示背景
for (int row = 0; row < nrow; row++)
{
int y = row * option.row_height + (option.show_header ? option.head_height : 0);
string QSS = (row % 2 != 0) ? QSS::QSS_TABLE_ROW_0 : QSS::QSS_TABLE_ROW_1;
int widgetRow = PvApp::label(p, pvid, 1, y, rect.w-2, option.row_height, "", QSS);
pvHide(p, widgetRow);
vecRows[row].widget = widgetRow;
}
}
void PvTable::addHead(string id, string text, int width, vector<pair<string, string>> mapping)
{
vecHeads.push_back(Head(id, text, width, mapping));
ncol = vecHeads.size();
if (width <= -1) { width = rect.w-1 - posCol; }
int col = ncol - 1;
// 创建表头的标签
if (option.show_header)
{
vecHeads[col].pvid = PvApp::label(p, pvid, posCol, 0, width, option.head_height, text, QSS::QSS_TABLE_HEAD);
}
// 创建列的单元格
for (int row = 0; row < nrow; ++row)
{
int cellId = PvApp::label(p, vecRows[row].widget, posCol, 0, width, option.row_height, "", QSS::QSS_TABLE_CELL);
vecRows[row].vecCells.push_back(cellId);
PvApp::bind(p, MOUSE_OVER_EVENT, cellId, [=](string s) { highlight(row, (s == "1")); });
}
posCol += width;
}
void PvTable::addHead(vector<string> vec_text)
{
int colSize = vec_text.size();
int x = 0;
for (int i = 0; i < vec_text.size(); ++i)
{
int w = float(rect.w-1) * float(i+1) / float(colSize);
string text = vec_text[i];
this->addHead(text, text, w-x);
x = w;
}
}
void PvTable::setRowVisible(int irow, bool v)
{
if (irow < 0 || irow >= vecRows.size())
{
return;
}
auto& rowItem = vecRows[irow];
if (rowItem.visible != v)
{
rowItem.visible = v;
rowItem.visible ? pvShow(p, rowItem.widget) : pvHide(p, rowItem.widget);
if (!v)
{
for (int col = 0; col<rowItem.vecCells.size(); ++col)
{
pvSetText(p, rowItem.vecCells[col], "");
}
}
}
}
void PvTable::highlight(int irow, bool v)
{
string QSS = ((irow % 2 != 0) ? QSS::QSS_TABLE_ROW_0 : QSS::QSS_TABLE_ROW_1);
if (vecRows.size() > 0 && irow <= vecRows.size())
{
if (v) { QSS = "background-color:rgba(14,45,60,200);border:1px solid rgba(255,0,0,100);"; }
pvSetStyleSheet(p, vecRows[irow].widget, QSS.c_str());
}
}
void PvTable::addOperate(vector<string> vecOpt)
{
// 创建表头的标签
if (option.show_header)
{
PvApp::label(p, pvid, posCol, 0, rect.w - posCol, option.head_height, "操作", QSS::QSS_TABLE_HEAD);
}
for (int row = 0; row < nrow; ++row)
{
int cellWidget = PvApp::label(p, vecRows[row].widget, posCol, 0, rect.w - posCol, option.row_height, "", QSS::QSS_TABLE_CELL);
vecOper.push_back({ cellWidget, vector<int>() });
auto& vec_opt_btn_ = vecOper.back().second;
int x = 5, w = 60;
for (int i = 0; i < vecOpt.size(); i++)
{
auto& title = vecOpt[i];
w = 20 + 15 * title.size() / 3;
int btn = PvApp::button(p, cellWidget, x, 4, w, 24, title, QSS::button(14, "", "", "none; border-radius: 0px"));
PvApp::bind(p, PvEvent::BUTTON_EVENT, btn, [=](std::string) {
if (callbackOper) { callbackOper(row, 0, title); }
});
vec_opt_btn_.push_back(btn);
x += (w + 5);
}
}
}
void PvTable::setOperateCallback(CallbackTableOpt cb)
{
callbackOper = cb;
};
void PvTable::setRow(int irow, Fields& d)
{
if (irow >= nrow) { return; }
vecData[irow] = d;
for (int col = 0; col < vecHeads.size(); ++col)
{
auto& head = vecHeads[col];
string text = d.value(head.id);
text = head.getMapping(text);
pvSetText(p, vecRows[irow].vecCells[col], text.c_str());
}
setRowVisible(irow, true);
}
void PvTable::setRow(int irow, std::vector<std::string> vd)
{
if (irow >= nrow) { return; }
pvShow(p, vecRows[irow].widget);
for (int col = 0; col < vecHeads.size(); ++col)
{
if (col < vd.size()) {
auto& head = vecHeads[col];
string text = head.getMapping(vd[col]);
pvSetText(p, vecRows[irow].vecCells[col], text.c_str());
}
}
setRowVisible(irow, true);
}
Fields PvTable::row(int irow)
{
return (irow >= 0 && irow < vecData.size()) ? vecData[irow] : Fields();
}
Fields PvTable::rowMapping(int rowid)
{
Fields d = this->row(rowid);
this->mappingData(d);
return d;
}
void PvTable::mappingData(Fields& fields)
{
for (int i=0; i< vecHeads.size(); ++i)
{
auto& head = vecHeads[i];
if (fields.contains(head.id))
{
auto& val = fields.value(head.id);
val = head.getMapping(val);
}
}
}
int PvTable::rowCount()
{
return this->nrow;
}
int PvTable::colCount()
{
return this->ncol;
}
vector<Fields> PvTable::data()
{
return vecData;
}
PvTable::Head& PvTable::header(int col)
{
return vecHeads[col];
}
static const string STYLE_NORMAL =
"QPushButton { background-color:rgba(255,255,255,30);border-radius:0px;font:bold 16px ;color:white;border:1px solid #20a481;}"
"QPushButton:hover { background-color:rgba(32,164,128,255);color:white;}"
"QPushButton:pressed { border-width:3px 0 0 3px;border-style:inset;color:white;}"
"QPushButton:disabled { color: gray; border-color: gray; }";
static const string STYLE_ACTIVE =
"QPushButton { background-color:rgba(32,164,128,255);border-radius:0px;font:bold 16px ;color:white;border:1px solid #20a481;}"
"QPushButton:hover { background-color:rgba(32,164,128,255);color:white;}"
"QPushButton:pressed { border-width:3px 0 0 3px;border-style:inset;color:white;}"
"QPushButton:disabled { color: gray; border-color: gray; }";
PvPagination::PvPagination(PARAM* p, int parent, int x, int y, int n)
: PvObject(p)
{
pvid = PvApp::label(p, parent, x, y, 2*32 + 80, 30, "", "border: 0px solid gray;");
// 分页控件
btnPrev = PvApp::button(p, pvid, 0, 0, 30, 30, "<", STYLE_NORMAL);
for (int i = 0; i < n; i++)
{
int btn = PvApp::button(p, pvid, (i+1)*32, 0, 30, 30, std::to_string(i+1), STYLE_NORMAL);
pvHide(p, btn);
vecBtn.push_back({btn, i});
PvApp::bind(p, PvEvent::BUTTON_EVENT, btn, [=](string) { this->activePage(i, true); });
}
btnNext = PvApp::button(p, pvid, 32, 0, 30, 30, ">", STYLE_NORMAL);
labelInfo = PvApp::label(p, pvid, 2*32, 0, 80, 30, " 共0页", QSS::label(14, "rgb(27, 220, 224)"));
pvSetEnabled(p, btnPrev, 0);
pvSetEnabled(p, btnNext, 0);
PvApp::bind(p, PvEvent::BUTTON_EVENT, btnPrev, [=](string e) { this->activePage(--pageIndex, true); });
PvApp::bind(p, PvEvent::BUTTON_EVENT, btnNext, [=](string e) { this->activePage(++pageIndex, true); });
}
void PvPagination::setPage(int index, int count)
{
pageIndex = index;
pageCount = count;
for (int i = 0; i < vecBtn.size(); ++i)
{
auto& btn = vecBtn[i];
auto btnid = btn.first;
int idx = i + 1;
if (idx > pageCount)
{
pvHide(p, btnid);
}
else
{
pvShow(p, btnid);
}
}
int x = (count+1)*32;
pvMove(p, btnNext, x, 0);
pvMove(p, labelInfo, x+=32, 0);
pvSetText(p, labelInfo, ("" + std::to_string(count) + "").c_str());
pvResize(p, pvid, x += 81, 30);
this->activePage(pageIndex);
}
int PvPagination::page()
{
return pageIndex;
};
void PvPagination::activePage(int index, bool invoke)
{
if (pageCount == 0)
{
pageIndex = 0;
return;
}
pageIndex = index;
if (pageIndex < 0){ pageIndex = 0; }
if (pageIndex >= pageCount) { pageIndex = pageCount - 1; }
pvSetEnabled(p, btnPrev, pageIndex != 0);
pvSetEnabled(p, btnNext, (pageIndex != pageCount - 1));
if (btnActive != PV_ID_NUL) { pvSetStyleSheet(p, btnActive, STYLE_NORMAL.c_str()); }
btnActive = vecBtn[pageIndex].first;
pvSetStyleSheet(p, btnActive, STYLE_ACTIVE.c_str());
if (invoke)
{
if (callback) callback(pageIndex);
}
}
void PvPagination::setCallback(std::function<void(int index)> func)
{
callback = func;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// === PageTable ===
PageTable::PageTable(PARAM* p) : PvMask(p)
{
table = std::make_shared<PvTable>(p, 0, 20, 210, 1880, pageSize, option);
table->setOperateCallback([=](int irow, int icol, std::string text) {
curRowData = table->row(irow);
this->onOperate(irow, icol, text);
});
pagination = std::make_shared<PvPagination>(p, 0, 20, 780, 20);
pagination->setCallback([=](int index)
{
pageIndex = index;
this->updateDataFromDB();
});
}
std::shared_ptr<PvPopWidget> PageTable::addPop(int w, int h, int w0, std::string name, std::vector<std::string> primaryKeys)
{
std::shared_ptr<PvPopWidget> pop = std::make_shared<PvPopWidget>(p, w, h, name);
pop->lineValWidth = w0;
pop->setPrimaryKeys(primaryKeys);
pop->setCallbackConfirm([=]()
{
auto fields = pop->getData();
std::string err = this->onValidation(pop, fields);
pop->setMsg(err);
if (err.empty())
{
XLOGD() << "POP get: data=" << fields.toStr();
if (pop->status == POP_OPER_EDIT) { pop->checkChangedData(fields); }
XLOGD() << "POP get: data=" << fields.toStr();
table->mappingData(fields);
XLOGD() << "POP get: data=" << fields.toStr();
err = this->onPopConfirm(pop, fields);
pop->setMsg(err);
if (err.empty())
{
this->hidePop(0);
this->updateDataFromDB();
};
}
});
pop->show(0);
vecPop.push_back(pop);
return pop;
}
void PageTable::showPop(int index, std::string oper, Fields fields)
{
XLOGD() << "POP set: data=" << fields.toStr();
table->mappingData(fields);
XLOGD() << "POP set: data=" << fields.toStr();
if (index < vecPop.size())
{
auto& pop = vecPop[index];
pop->show(true);
pop->setStatus(oper);
pop->setData(fields);
pop->setMsg("");
}
}
void PageTable::hidePop(int index)
{
if (index < vecPop.size())
{
vecPop[index]->show(false);
}
}
void PageTable::updateDataFromDB()
{
std::vector<Fields> result;
PageInfo pageInfo;
pageInfo.size = pageSize;
pageInfo.index = pageIndex;
this->onQueryTable(pageInfo, result);
for (int i = 0; i<table->rowCount(); ++i)
{
if (i<result.size())
{
auto& fields = result[i];
table->setRow(i, result[i]);
}
else
{
table->setRowVisible(i, false);
}
}
pagination->setPage(pageInfo.index, pageInfo.total/pageInfo.size + int(pageInfo.total%pageInfo.size != 0));
}