#include "alarmEventDataView.h" #include "alarmEventDataService.h" #include #include #include #include #include #include //#include ///////------AlarmEventDataModel----- AlarmEventDataModel::AlarmEventDataModel(AlarmDataMode mode, QObject* parent) : QAbstractTableModel(parent) , m_dataMode(mode) , m_maxRealTimeEvents(5) { if(m_dataMode == RealTime) //创建几个“空事件”来初始化表格 { for(int i = 0; i < m_maxRealTimeEvents; ++i) { EventData event; event.id = ""; //空事件 event.timestamp = 0; //给一个初始值,后续的实际事件会根据时间排名,不给初始值的话这个时间值不确定 m_displayEvents.append(event); } } m_paginationInfo.entriesPerPage = 100; m_paginationInfo.currentPage = 1; m_paginationInfo.totalEntries = 0; iniHeaderData(); connect(AlarmEventDataService::instance(), &AlarmEventDataService::realTimeEventReceived, this, &AlarmEventDataModel::onRealTimeEventReceived); connect(AlarmEventDataService::instance(), &AlarmEventDataService::historicalQueryData, this, &AlarmEventDataModel::onHistoricalEventsReceived); AlarmEventDataService::instance()->start(); //实时数据测试 m_simulatedDataTimer = new QTimer(this); connect(m_simulatedDataTimer, &QTimer::timeout, this, &AlarmEventDataModel::onTimeoutSimulateData); m_simulatedDataTimer->start(3000); } AlarmEventDataModel::~AlarmEventDataModel() {} QModelIndex AlarmEventDataModel::index(int row, int column, const QModelIndex& parent) const { if(!hasIndex(row, column, parent)) return QModelIndex(); if(column > m_headerData.count()) return QModelIndex(); return createIndex(row, column); } QVariant AlarmEventDataModel::data(const QModelIndex& index, int role) const { if(!index.isValid()) return QVariant(); int row = index.row(); int col = index.column(); int globalRow = (m_paginationInfo.currentPage - 1) * m_paginationInfo.entriesPerPage + row; const EventData& event = m_displayEvents.at(row); if(event.id.isEmpty()) //无效事件 return QVariant(); //EventData event = m_displayEvents[row]; switch (role) { case Qt::DisplayRole: { switch(col) { case Index: return QString::number(globalRow + 1); case ReceiveTime: return QDateTime::fromMSecsSinceEpoch(event.timestamp).toString("yyyy-MM-dd hh:mm:ss"); case SOETime: return QDateTime::fromMSecsSinceEpoch(event.timestamp).toString("yyyy-MM-dd hh:mm:ss"); case Station: return event.stationName; case Bay: return event.bayName; case Description: return event.description; case Type: return event.type; case Severity: return event.severity; case Status: { if(event.status < 3) return QString("未确认"); else return QString("已确认"); } default: return QVariant(); } } case Qt::ForegroundRole: { switch(col) { case Severity: { if(event.severity == "事故") return QColor(255, 85, 0); else if(event.severity == "异常") return QColor(255, 170, 0); else if(event.severity == "预警") return QColor(0, 170, 255); else if(event.severity == "告知") return QColor(0, 170, 0); else return QColor(250, 250, 250); } case Status: { if(event.status < 3) return QColor(0, 170, 255); else return QColor(0, 170, 0); } default: return QColor(250, 250, 250); } } case Qt::TextAlignmentRole: return Qt::AlignCenter; //居中展示 default: return QVariant(); } } QVariant AlarmEventDataModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_headerData.at(section).text; return QAbstractItemModel::headerData(section, orientation, role); } int AlarmEventDataModel::rowCount(const QModelIndex& parent) const { return m_displayEvents.count(); } int AlarmEventDataModel::columnCount(const QModelIndex& parent) const { return m_headerData.isEmpty() ? 0 : m_headerData.count(); } Qt::ItemFlags AlarmEventDataModel::flags(const QModelIndex& index) const { if(!index.isValid()) return Qt::NoItemFlags; Qt::ItemFlags flags = QAbstractTableModel::flags(index); flags |= Qt::ItemIsEditable; //不可编辑 return flags; } void AlarmEventDataModel::iniHeaderData() { m_headerData.emplace_back(SectionData("序号", 90, Index)); m_headerData.emplace_back(SectionData("接收时间", 200, ReceiveTime)); m_headerData.emplace_back(SectionData("SOE时间", 200, SOETime)); m_headerData.emplace_back(SectionData("厂站", 200, Station)); m_headerData.emplace_back(SectionData("间隔", 200, Bay)); m_headerData.emplace_back(SectionData("信息", -1, Description)); m_headerData.emplace_back(SectionData("类型", 160, Type)); m_headerData.emplace_back(SectionData("等级", 160, Severity)); m_headerData.emplace_back(SectionData("确认状态", 150, Status)); m_headerData.emplace_back(SectionData("操作", 180, Operation)); } /*void AlarmEventDataModel::setMode(AlarmDataMode mode) { if(m_dataMode == mode) return; m_dataMode = mode; if(mode == Historical) {} else {} }*/ void AlarmEventDataModel::setMaxRealTimeEvents(int count) { if(m_dataMode == RealTime) //实时列表初始化表格 { if(m_maxRealTimeEvents == count) return; else if(m_maxRealTimeEvents < count) { int DValue = count - m_maxRealTimeEvents; for(int i = 0; i < DValue; ++i) m_displayEvents.removeLast(); } else if(m_maxRealTimeEvents > count) { int DValue = m_maxRealTimeEvents - count; for(int i = 0; i < DValue; ++i) { EventData event; event.id = ""; //空事件 m_displayEvents.append(event); } } m_maxRealTimeEvents = count; } } void AlarmEventDataModel::setFilter(const AlarmEventDataFilter& filter) { m_currentFilter = filter; refresh(); } void AlarmEventDataModel::applyFilter() { m_filteredEvents.clear(); if(m_allEvents.size() > 0) m_filteredEvents = m_currentFilter.apply(m_allEvents); } void AlarmEventDataModel::refresh() { m_allEvents.clear(); //1.通过服务获取当前时间段的所有事件-m_allEvents //2.过滤事件(时间区间作为服务请求参数,其它过滤在本地完成)-m_filteredEvents applyFilter(); //3.更新页码数据 updatePaginationInfo(); //4.根据页码刷新当前页面数据()-m_displayEvents updateCurPageData(); } void AlarmEventDataModel::updatePaginationInfo() { m_paginationInfo.totalEntries = m_filteredEvents.size(); m_paginationInfo.totalPages = qCeil(static_cast(m_paginationInfo.totalEntries) / m_paginationInfo.entriesPerPage); if(m_paginationInfo.totalPages == 0) m_paginationInfo.currentPage = 1; else if(m_paginationInfo.currentPage > m_paginationInfo.totalPages) m_paginationInfo.currentPage = m_paginationInfo.totalPages; } int AlarmEventDataModel::findEventDataIndexById(const QString& eventId) { for(int i = 0; i < m_displayEvents.size(); i++) { if(m_displayEvents.at(i).id == eventId) return i; } return -1; } void AlarmEventDataModel::updateEventData(int index, const EventData& updatedEvent) { if(index < 0 || index >= m_displayEvents.size()) return; m_displayEvents[index] = updatedEvent; //更新视图 QModelIndex modelIndex = createIndex(index, 0); emit dataChanged(modelIndex, modelIndex); } void AlarmEventDataModel::updateCurPageData() { m_displayEvents.clear(); int startIndex = (m_paginationInfo.currentPage - 1) * m_paginationInfo.entriesPerPage; int endIndex = qMin(startIndex + m_paginationInfo.entriesPerPage, m_filteredEvents.size()); beginResetModel(); if(startIndex < m_filteredEvents.size()) m_displayEvents = m_filteredEvents.mid(startIndex, endIndex - startIndex); endResetModel(); } void AlarmEventDataModel::onTimeoutSimulateData() { //模拟实时告警数据 static int id = 0, min = 0, max = 3; int randomInt = min + QRandomGenerator::global()->bounded(min, max + 1); QString strID = QString::number(++id); //qDebug() << id << " " << strID; EventData event; event.id = strID; event.name = QString("实时告警") + strID; event.timestamp = QDateTime::currentMSecsSinceEpoch(); event.stationName = QString("厂站") + QString::number(randomInt); event.bayName = QString("间隔") + QString::number(randomInt); event.description = ""; event.type = 5; QString severity = QString("预警"); if(randomInt == 0) severity = QString("告知"); else if(randomInt == 1) severity = QString("预警"); else if(randomInt == 2) severity = QString("异常"); else if(randomInt == 3) severity = QString("事故"); event.severity = severity; event.status = 0; onRealTimeEventReceived(event); } void AlarmEventDataModel::onRealTimeEventReceived(const EventData& event) { if(m_dataMode != RealTime) return; int index = findEventDataIndexById(event.id); if(index >= 0) { updateEventData(index, event); return; } beginResetModel(); //插入数据之前,先判断当前是否达到最大显示条目,达到的话删除最靠前(时间)的条目 if(m_displayEvents.size() >= m_maxRealTimeEvents) m_displayEvents.removeLast(); //按照时间顺序排序(从后到前) int insertPosition = 0; for(; insertPosition < m_displayEvents.size(); ++insertPosition) { if(event.timestamp > m_displayEvents.at(insertPosition).timestamp) break; } if(insertPosition < m_displayEvents.size()) m_displayEvents.insert(insertPosition, event); else m_displayEvents.append(event); endResetModel(); emit receivedNewAlarm(event); } void AlarmEventDataModel::onHistoricalEventsReceived(const QList& events) { } bool AlarmEventDataModel::setCurrentPage(int page) { if(m_paginationInfo.currentPage != page && page > 0 && page <= m_paginationInfo.totalPages) { m_paginationInfo.currentPage = page; updateCurPageData(); return true; } return false; } void AlarmEventDataModel::previousPage() { if(m_paginationInfo.currentPage == 1) return; int page = m_paginationInfo.currentPage; setCurrentPage(--page); } void AlarmEventDataModel::nextPage() { if(m_paginationInfo.currentPage == m_paginationInfo.totalPages) return; int page = m_paginationInfo.currentPage; setCurrentPage(++page); } void AlarmEventDataModel::firstPage() { if(m_paginationInfo.currentPage == 1) return; setCurrentPage(1); } void AlarmEventDataModel::lastPage() { if(m_paginationInfo.currentPage == m_paginationInfo.totalPages) return; setCurrentPage(m_paginationInfo.totalPages); } ///////------AlarmEventDataDelegate----- AlarmEventDataDelegate::AlarmEventDataDelegate(QTableView* view, QObject* parent) : QStyledItemDelegate(parent) ,m_tableView(view) { m_btnConfirm.text = QString("确 认"); m_btnConfirm.textColor = QColor(34, 177, 16); m_btnConfirm.borderColor = QColor(34, 177, 16); m_btnConfirm.borderWidth = 1; m_btnConfirm.borderRaius = 2; m_btnConfirm.normalBgColor = Qt::transparent; m_btnConfirm.hoverBgColor = QColor(34, 177, 16, 80); m_btnConfirm.pressedBgColor = Qt::transparent; m_btnReplay.text = QString("回 放"); m_btnReplay.textColor = QColor(86, 156, 214); m_btnReplay.borderColor = QColor(86, 156, 214); m_btnReplay.borderWidth = 1; m_btnReplay.borderRaius = 2; m_btnReplay.normalBgColor = Qt::transparent; m_btnReplay.hoverBgColor = QColor(86, 156, 214, 80); m_btnReplay.pressedBgColor = Qt::transparent; } AlarmEventDataDelegate::~AlarmEventDataDelegate() {} void AlarmEventDataDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { //根据行号设置交替颜色 QStyleOptionViewItem opt = option; initStyleOption(&opt, index); if((index.row() + 1) % 2 == 0) painter->fillRect(opt.rect, QColor(11, 26, 33, 100)); else painter->fillRect(opt.rect, QColor(11, 26, 33, 0)); if(m_tableView) { //绘制单元格边框(只绘制右边框,每行的最后一个单元格不绘制) painter->save(); QPen pen(QColor(60,60,60), 1, Qt::DotLine); painter->setPen(pen); if(index.column() != m_tableView->model()->columnCount() - 1) { //opt.rect.adjust(1, 0, 1, 0); painter->drawLine(opt.rect.topRight(), opt.rect.bottomRight()); } painter->restore(); //绘制操作按钮 QModelIndex numIndex = m_tableView->model()->index(index.row(), 0); QString number = numIndex.data(Qt::DisplayRole).toString(); if(!number.isEmpty() && index.column() == m_tableView->model()->columnCount() - 1) { int nMarginH = 10; int nMarginV = 5; int nSpacing = 10; int nBtnWidth = (opt.rect.width() - nMarginH * 2 - nSpacing) * 0.5; int nBtnHeight = opt.rect.height() - nMarginV * 2; /*QStyleOptionButton btnConfirm; btnConfirm.text = QString("确 认"); btnConfirm.rect = QRect(opt.rect.left() + nMarginH, opt.rect.top() + nMarginV, nBtnWidth, nBtnHeight); btnConfirm.state |= QStyle::State_Enabled; QStyleOptionButton btnReplay; btnReplay.text = QString("回 放"); btnReplay.rect = QRect(btnConfirm.rect.right() + nSpacing, opt.rect.top() + nMarginV, nBtnWidth, nBtnHeight); btnReplay.state |= QStyle::State_Enabled; // 绘制按钮 QApplication::style()->drawControl(QStyle::CE_PushButton, &btnConfirm, painter); QApplication::style()->drawControl(QStyle::CE_PushButtonLabel, &btnReplay, painter);*/ const_cast(this)->m_btnConfirm.rect = QRect(opt.rect.left() + nMarginH, opt.rect.top() + nMarginV, nBtnWidth, nBtnHeight); const_cast(this)->m_btnReplay.rect = QRect(m_btnConfirm.rect.right() + nSpacing, opt.rect.top() + nMarginV, nBtnWidth, nBtnHeight); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); //绘制‘确认’按钮 QPen rectPen; rectPen.setColor(m_btnConfirm.borderColor); rectPen.setWidth(m_btnConfirm.borderWidth); painter->setPen(rectPen); QColor brushColor = m_btnConfirm.normalBgColor; bool isHover = opt.state.testFlag(QStyle::State_MouseOver) && m_btnConfirm.rect.contains(m_mousePositon); //opt.state.testFlag(QStyle::State_MouseOver)表示鼠标是否悬停在该视图项(即单元格)上 //bool isPressed = opt.state.testFlag(QStyle::State_Sunken) && m_btnConfirm.rect.contains(m_mousePositon);//opt.state.testFlag(QStyle::State_Sunken)表示鼠标是否在该视图项(即单元格)上按下 bool isPressed = m_buttonColumnIsPress && m_btnConfirm.rect.contains(m_mousePositon); //opt.state.testFlag(QStyle::State_Sunken)总是为false且未找到原因 //注意判断顺序,一定要先先判断press if(isPressed) brushColor = m_btnConfirm.pressedBgColor; else if(isHover) brushColor = m_btnConfirm.hoverBgColor; painter->setBrush(brushColor); painter->drawRoundedRect(m_btnConfirm.rect, m_btnConfirm.borderRaius, m_btnConfirm.borderRaius); painter->setPen(m_btnConfirm.textColor); painter->drawText(m_btnConfirm.rect, Qt::AlignCenter, m_btnConfirm.text); //绘制‘回放’按钮 rectPen.setColor(m_btnReplay.borderColor); rectPen.setWidth(m_btnReplay.borderWidth); painter->setPen(rectPen); brushColor = m_btnReplay.normalBgColor; isHover = opt.state.testFlag(QStyle::State_MouseOver) && m_btnReplay.rect.contains(m_mousePositon); //isPressed = opt.state.testFlag(QStyle::State_Sunken) && isHover; isPressed = m_buttonColumnIsPress && m_btnReplay.rect.contains(m_mousePositon); if(isPressed) brushColor = m_btnReplay.pressedBgColor; else if(isHover) brushColor = m_btnReplay.hoverBgColor; painter->setBrush(brushColor); painter->drawRoundedRect(m_btnReplay.rect, m_btnReplay.borderRaius, m_btnReplay.borderRaius); painter->setPen(m_btnReplay.textColor); painter->drawText(m_btnReplay.rect, Qt::AlignCenter, m_btnReplay.text); painter->restore(); } } if(opt.state.testFlag(QStyle::State_Selected)) { //方法1:移除选中状态,防止选中改变文字颜色 //opt.state &= ~QStyle::State_Selected; //方法2 QColor originalTextColor = opt.palette.color(QPalette::Text); opt.palette.setColor(QPalette::HighlightedText, originalTextColor); } //绘制默认内容 QStyledItemDelegate::paint(painter, opt, index); } bool AlarmEventDataDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { if(m_tableView && index.column() == m_tableView->model()->columnCount() - 1) //只处理操作按钮所在列的事件 { QMouseEvent *mouseEvent = static_cast(event); m_mousePositon = mouseEvent->pos(); m_buttonColumnIsPress = false; if(mouseEvent->type() == QEvent::MouseMove) { emit m_tableView->model()->dataChanged(index, index); //触发重绘 } else if(mouseEvent->button() == Qt::LeftButton && mouseEvent->type() == QEvent::MouseButtonPress) { m_buttonColumnIsPress = true; emit m_tableView->model()->dataChanged(index, index); //触发重绘 //qDebug() << option.state.testFlag(QStyle::State_Sunken); } else if(mouseEvent->button() == Qt::LeftButton && mouseEvent->type() == QEvent::MouseButtonRelease) { emit m_tableView->model()->dataChanged(index, index); //触发重绘 if(m_btnConfirm.rect.contains(m_mousePositon)) emit confirmBtnClicked(index); else if(m_btnReplay.rect.contains(m_mousePositon)) emit replayBtnClicked(index); } } return QStyledItemDelegate::editorEvent(event, model, option, index); } ///////------AlarmEventDataView----- AlarmEventDataView::AlarmEventDataView(AlarmDataMode mode, QWidget* parent) : QWidget(parent) { m_tableView = new QTableView(this); m_tableView->verticalHeader()->setVisible(false); m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); //m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableView->setShowGrid(false); //m_tableView->setStyleSheet("QHeaderView{background-color: rgb(40, 40, 40);} QHeaderView::section{background-color:transparent;}"); m_tableModel = new AlarmEventDataModel(mode, this); m_tableView->setModel(m_tableModel); //设置表头 const QVector headerData = m_tableModel->headerData(); for(int i = 0; i < headerData.size(); i++) { if(headerData.at(i).width == -1) m_tableView->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Stretch); else m_tableView->setColumnWidth(i, headerData.at(i).width); } m_delegate = new AlarmEventDataDelegate(m_tableView, this); connect(m_delegate, &AlarmEventDataDelegate::confirmBtnClicked, this, &AlarmEventDataView::onBtnClicked_Confirm); connect(m_delegate, &AlarmEventDataDelegate::replayBtnClicked, this, &AlarmEventDataView::onBtnClicked_Replay); m_tableView->setItemDelegate(m_delegate); m_vLayout = new QVBoxLayout(this); m_vLayout->setSpacing(0); m_vLayout->setContentsMargins(0, 0, 0, 0); m_vLayout->addWidget(m_tableView); this->setLayout(m_vLayout); } AlarmEventDataView::~AlarmEventDataView() {} /*void AlarmEventDataView::setModelMode(AlarmDataMode mode, int maxRealTimeEvents) { if(m_tableModel) { m_tableModel->setMode(mode); if(mode == RealTime) m_tableModel->setMaxRealTimeEvents(maxRealTimeEvents); } }*/ void AlarmEventDataView::onBtnClicked_Confirm(const QModelIndex& index) { qDebug() << QString("confirmBtnClicked, row: %1").arg(index.row()); } void AlarmEventDataView::onBtnClicked_Replay(const QModelIndex& index) { qDebug() << QString("replayBtnClicked, row: %1").arg(index.row()); }