#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> 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 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 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 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() }); 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 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 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 func) { callback = func; } /////////////////////////////////////////////////////////////////////////////////////////////////// // === PageTable === PageTable::PageTable(PARAM* p) : PvMask(p) { table = std::make_shared(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(p, 0, 20, 780, 20); pagination->setCallback([=](int index) { pageIndex = index; this->updateDataFromDB(); }); } std::shared_ptr PageTable::addPop(int w, int h, int w0, std::string name, std::vector primaryKeys) { std::shared_ptr pop = std::make_shared(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 result; PageInfo pageInfo; pageInfo.size = pageSize; pageInfo.index = pageIndex; this->onQueryTable(pageInfo, result); for (int i = 0; irowCount(); ++i) { if (isetRow(i, result[i]); } else { table->setRowVisible(i, false); } } pagination->setPage(pageInfo.index, pageInfo.total/pageInfo.size + int(pageInfo.total%pageInfo.size != 0)); }