/** * @file ModelStatusWidget.cpp * @author jinchao * @brief 模型状态窗口实现 * @version 1.0 * @date 2025-03-10 * * @copyright Copyright (c) 2025 COMAC * */ #include "ModelStatusWidget.h" #include #include #include #include #include #include #include #include "../XNCustomPlot.h" ModelStatusWidget::ModelStatusWidget(QWidget *parent, SystemStatusWidget *systemStatusWidget) : QWidget(parent) { //初始化系统运行状态窗口指针 this->systemStatusWidget = systemStatusWidget; //初始化模型状态标签页 setupTabModelStatus(); //初始化模型信息线程 InitialModelInfoThread(); } ModelStatusWidget::~ModelStatusWidget() { //释放模型信息线程 if (modelThread != nullptr) { modelThread->onThreadQuit(); modelThread->quit(); modelThread->wait(); delete modelThread; modelThread = nullptr; } } void ModelStatusWidget::onSetCurrentTabIndex(int index) { this->currentTabIndex = index; if (currentTabIndex == 1) { //如果当前选中的标签页为1,则显示模型状态窗口 this->show(); //控制模型信息线程 emit controlModelInfoThread(true); } else { //如果当前选中的标签页为0,则隐藏模型状态窗口 this->hide(); //控制模型信息线程 emit controlModelInfoThread(false); } } void ModelStatusWidget::InitialModelInfoThread() { //初始化模型信息线程 modelThread = new ModelInfoUpdateThread(this); //连接模型信息线程的更新模型信息信号到槽函数 connect(modelThread, &ModelInfoUpdateThread::updateModelInfo, this, &ModelStatusWidget::updateModelData); //连接模型信息线程的控制信号到槽函数 connect(this, &ModelStatusWidget::controlModelInfoThread, modelThread, &ModelInfoUpdateThread::onThreadController); //初始化模型信息线程 modelThread->Initialize(); //启动模型信息线程 modelThread->start(); } // 设置模型状态标签页 void ModelStatusWidget::setupTabModelStatus() { QHBoxLayout *mainLayout = new QHBoxLayout(this); // 创建水平布局 // 使用QSplitter进行左右栏的动态分割 QSplitter *horizontalSplitter = new QSplitter(Qt::Horizontal); // 创建一个水平方向的QSplitter // 左栏布局 setupTabModelStatusLeftPanel(horizontalSplitter); // 右栏布局 setupTabModelStatusRightPanel(horizontalSplitter); // 设置QSplitter的初始分割比例为1:4,即左栏占五分之一,右栏占五分之四 QList sizes; sizes << 1 << 4; horizontalSplitter->setSizes(sizes); // 将QSplitter添加到主布局中 mainLayout->addWidget(horizontalSplitter); } // 设置模型状态左栏的布局和组件 void ModelStatusWidget::setupTabModelStatusLeftPanel(QSplitter *splitter) { // 左栏布局 QVBoxLayout *leftMainLayout = new QVBoxLayout(); QWidget *leftMainWidget = new QWidget(); leftMainLayout->addWidget(leftMainWidget); QVBoxLayout *leftLayout = new QVBoxLayout(); leftLayout->setObjectName("ModelStatusLeftLayout"); leftMainWidget->setLayout(leftLayout); leftMainLayout->addStretch(); // 添加一个伸展项,使得布局可以自动调整大小 // 创建一个QWidget作为左栏的容器,并设置其布局为leftLayout QWidget *leftPanel = new QWidget(); leftPanel->setLayout(leftMainLayout); // 创建一个QScrollArea来包含左栏的布局 QScrollArea *leftScrollArea = new QScrollArea(this); leftScrollArea->setWidget(leftPanel); leftScrollArea->setWidgetResizable(true); leftScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); leftScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 将QScrollArea添加到QSplitter中 splitter->addWidget(leftScrollArea); } // 创建线程信息页面 QWidget *ModelStatusWidget::createThreadInfoPage(unsigned int threadIndex) { QWidget *page = new QWidget(); QGridLayout *gridLayout = new QGridLayout(page); // 定义运行信息的标签和初始值 // 定义标签列表 const QStringList labels = {"线程ID", "运行状态", "运行周期数", "实时频率", "设定频率", "最小频率", "最大频率", "平均频率", "实时周期", "设定周期", "最小周期", "最大周期", "平均周期"}; // 定义值列表 const QStringList values = {"0", "未运行", "0", "0Hz", "0Hz", "0Hz", "0Hz", "0Hz", "0ms", "0ms", "0ms", "0ms", "0ms"}; // 遍历标签和值列表,为每个条目创建两个QLabel for (int i = 0; i < labels.size(); ++i) { QLabel *label = new QLabel(labels[i], page); QLabel *valueLabel = new QLabel(values[i], page); // 设置QLabel的对齐方式 // 设置标签对齐方式为左对齐 label->setAlignment(Qt::AlignLeft); // 设置值标签对齐方式为右对齐 valueLabel->setAlignment(Qt::AlignRight); // 设置valueLabel的objectName,以便后续可以通过findChild找到并更新它 valueLabel->setObjectName(QString("threadRunValueLabel%1_%2").arg(threadIndex).arg(i)); // 将QLabel添加到网格布局中 // 在第i行第0列放置标签 gridLayout->addWidget(label, i, 0); // 在第i行第1列放置值 gridLayout->addWidget(valueLabel, i, 1); } return page; } // 切换线程信息页面的显示状态 void ModelStatusWidget::toggleThreadInfo() { QPushButton *button = qobject_cast(sender()); if (button) { QString buttonText = button->objectName(); int threadIndex = extractTrailingNumbers(buttonText, 3); // 提取数字部分作为线程索引 if (threadIndex < 0) return; QWidget *coreInfoPage = findChild(QString("threadInfoPage%1").arg(threadIndex)); if (!coreInfoPage) return; coreInfoPage->setVisible(!coreInfoPage->isVisible()); } } // 设置模型状态右栏的布局和组件 void ModelStatusWidget::setupTabModelStatusRightPanel(QSplitter *splitter) { // 右栏布局 QVBoxLayout *rightLayout = new QVBoxLayout(); // 创建一个新的QSplitter用于垂直分割右栏的上下两部分 QSplitter *verticalSplitter = new QSplitter(Qt::Vertical); // 垂直方向的QSplitter // 表格布局 QTableWidget *modelStatusTableWidget = new QTableWidget(); setupModelStatusTableWidget(modelStatusTableWidget); // 图表绘制区域 QWidget *modelChartWidget = new QWidget(); // 图表区域,可以使用XNCustomPlot等库进行绘制 setupModelStatusChartWidget(modelChartWidget); // 配置图表区域 // 将表格和图表添加到垂直分割器中 verticalSplitter->addWidget(modelStatusTableWidget); verticalSplitter->addWidget(modelChartWidget); QList sizes; sizes << 3 << 2; verticalSplitter->setSizes(sizes); // 将垂直分割器添加到右栏布局中 rightLayout->addWidget(verticalSplitter); // 创建一个QWidget作为右栏的容器,并设置其布局为rightLayout QWidget *rightPanel = new QWidget(); rightPanel->setLayout(rightLayout); // 将右栏容器添加到QSplitter中 splitter->addWidget(rightPanel); } // 设置模型状态表格 void ModelStatusWidget::setupModelStatusTableWidget(QTableWidget *tableWidget) { tableWidget->setRowCount(10); // 设置表格的行数 tableWidget->setColumnCount(15); // 设置表格的列数 QFont headerFont("Arial", 16, QFont::Bold); tableWidget->horizontalHeader()->setFont(headerFont); // 设置表格的列标题 tableWidget->setHorizontalHeaderLabels( {"模型名称", "模型ID", "运行状态", "所属线程", "运行节点", "优先级", "设定频率", "最小频率", "最大频率", "平均频率", "周期数", "设定周期", "最小周期", "最大周期", "平均周期"}); // 设置表格列宽自适应 tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // 设置表格的选择行为为按行选择 tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); // 设置表格的选择模式为单选 tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); // 设置表格不可编辑 tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); // 设置表格的objectName,以便后续可以通过findChild找到并更新它 tableWidget->setObjectName("modelStatusTableWidget"); // 连接 threadStatusTableWidget 的 itemSelectionChanged 信号到槽函数 connect(tableWidget, &QTableWidget::itemSelectionChanged, this, &ModelStatusWidget::onModelStatusTableSelectionChanged); } // 处理模型状态表格的选择变化 void ModelStatusWidget::onModelStatusTableSelectionChanged() { if (currentTabIndex != 1) return; QTableWidget *modelStatusTableWidget = findChild("modelStatusTableWidget"); if (!modelStatusTableWidget) { return; } int rowIndex = modelStatusTableWidget->currentRow(); if (rowIndex < 0 || rowIndex >= modelDataVec.size()) { return; } XNCustomPlot *customPlot = findChild("modelStatusCustomPlot"); if (!customPlot) return; // 确保找到了XNCustomPlot对象 const XNRuntimeData &data = modelDataVec[rowIndex]; QVector xData, yData; if (freqOrPeriodIndex == 0 && data.m_CurrentPeriod.size() > 0) { customPlot->xAxis->setRange(0, data.m_CurrentPeriod.size()); customPlot->yAxis->setRange(data.m_MinPeriod - 1, data.m_MaxPeriod + 1); for (int i = 0; i < data.m_CurrentPeriod.size(); ++i) { xData.push_back(i); yData.push_back(data.m_CurrentPeriod[i]); } } else if (freqOrPeriodIndex == 1 && data.m_CurrentFrequency.size() > 0) { customPlot->xAxis->setRange(0, data.m_CurrentFrequency.size()); customPlot->yAxis->setRange(data.m_MinFrequency - 1, data.m_MaxFrequency + 1); for (int i = 0; i < data.m_CurrentFrequency.size(); ++i) { xData.push_back(i); yData.push_back(data.m_CurrentFrequency[i]); } } else { return; } updatePlotData(xData, yData); } // 设置模型状态图表 void ModelStatusWidget::setupModelStatusChartWidget(QWidget *modelStatusChartWidget) { modelStatusChartWidget->setObjectName("modelStatusChartWidget"); XNCustomPlot *customPlot = new XNCustomPlot(modelStatusChartWidget); customPlot->setObjectName("modelStatusCustomPlot"); // 设置图表布局 modelStatusChartWidget->setLayout(new QVBoxLayout()); modelStatusChartWidget->layout()->addWidget(customPlot); // 配置图表 customPlot->setBackground(QBrush(QColor("#19232D"))); customPlot->xAxis->setLabelColor(Qt::white); customPlot->yAxis->setLabelColor(Qt::white); customPlot->xAxis->setLabel("时间(s)"); customPlot->yAxis->setLabel("周期(ms)"); customPlot->xAxis->setTickLabelRotation(60); customPlot->xAxis->setTickLabelColor(Qt::white); customPlot->yAxis->setTickLabelColor(Qt::white); customPlot->xAxis->grid()->setVisible(true); customPlot->yAxis->grid()->setVisible(true); // 添加图表并设置绿色 customPlot->addGraph(); QPen greenPen(Qt::green); greenPen.setWidth(2); customPlot->graph(0)->setPen(greenPen); // 为chartWidget设置上下文菜单 connect(customPlot, &XNCustomPlot::customContextMenuRequested, this, &ModelStatusWidget::showChartContextMenu); // 连接信号 connect(this, &ModelStatusWidget::updateModelCutomPlotData, this, &ModelStatusWidget::onModelStatusTableSelectionChanged); customPlot->replot(); } // 处理图表中的右键点击事件 void ModelStatusWidget::showChartContextMenu(const QPoint &pos) { XNCustomPlot *customPlot = findChild("modelStatusCustomPlot"); if (!customPlot) return; // 确保找到了XNCustomPlot对象 QMenu contextMenu(this); QAction *actionFrequency = contextMenu.addAction("显示频率"); QAction *actionPeriod = contextMenu.addAction("显示周期"); // 连接QAction的triggered信号到相应的槽函数 connect(actionFrequency, &QAction::triggered, this, &ModelStatusWidget::onShowFrequency); connect(actionPeriod, &QAction::triggered, this, &ModelStatusWidget::onShowPeriod); // 在chartWidget上显示上下文菜单 QAction *selectedAction = contextMenu.exec(customPlot->mapToGlobal(pos)); } // 处理右键菜单中的"显示频率"选项 void ModelStatusWidget::onShowFrequency() { XNCustomPlot *customPlot = nullptr; if (currentTabIndex == 1) { customPlot = findChild("modelStatusCustomPlot"); } else return; if (!customPlot) return; // 确保找到了XNCustomPlot对象 // 实现显示频率的逻辑 customPlot->yAxis->setLabel("频率(Hz)"); freqOrPeriodIndex = 1; customPlot->replot(); // 重新绘制图表以显示新数据 emit updateModelCutomPlotData(); } // 处理右键菜单中的"显示周期"选项 void ModelStatusWidget::onShowPeriod() { XNCustomPlot *customPlot = nullptr; if (currentTabIndex == 1) { customPlot = findChild("modelStatusCustomPlot"); } else return; if (!customPlot) return; // 确保找到了XNCustomPlot对象 // 实现显示周期的逻辑 customPlot->yAxis->setLabel("周期(ms)"); freqOrPeriodIndex = 0; customPlot->replot(); // 重新绘制图表以显示新数据 emit updateModelCutomPlotData(); } // 更新线程信息页面 void ModelStatusWidget::updateThreadInfoPage(unsigned int threadIndex, const XNRuntimeData &data) { if (currentTabIndex != 1) return; QPushButton *threadInfoButton = findChild(QString("threadInfoButton%1").arg(threadIndex)); if (NULL == threadInfoButton) { QVBoxLayout *leftLayout = findChild("ModelStatusLeftLayout"); if (NULL == leftLayout) return; threadInfoButton = new QPushButton(data.m_name); threadInfoButton->setObjectName(QString("threadInfoButton%1").arg(threadIndex)); connect(threadInfoButton, &QPushButton::clicked, this, &ModelStatusWidget::toggleThreadInfo); leftLayout->addWidget(threadInfoButton); QWidget *threadInfoPage = createThreadInfoPage(threadIndex); threadInfoPage->setObjectName(QString("threadInfoPage%1").arg(threadIndex)); leftLayout->addWidget(threadInfoPage); } threadInfoButton->setText(data.m_name); QWidget *threadInfoPage = findChild(QString("threadInfoPage%1").arg(threadIndex)); if (threadInfoPage) { updateThreadInfoValue(threadIndex, 0, QString::number(data.m_id)); switch (data.m_RunningState) { case 0: updateThreadInfoValue(threadIndex, 1, "未运行"); break; case 1: updateThreadInfoValue(threadIndex, 1, "运行中"); break; case 2: updateThreadInfoValue(threadIndex, 1, "暂停中"); break; case 3: updateThreadInfoValue(threadIndex, 1, "错误"); break; default: updateThreadInfoValue(threadIndex, 1, "未知状态"); break; } updateThreadInfoValue(threadIndex, 2, QString::number(data.m_CycleCount)); updateThreadInfoValue( threadIndex, 3, QString("%1 Hz").arg(data.m_CurrentFrequency[data.m_CurrentFrequency.size() - 1], 0, 'f', 2)); updateThreadInfoValue(threadIndex, 4, QString("%1 Hz").arg(data.m_SetFrequency, 0, 'f', 2)); updateThreadInfoValue(threadIndex, 5, QString("%1 Hz").arg(data.m_MinFrequency, 0, 'f', 2)); updateThreadInfoValue(threadIndex, 6, QString("%1 Hz").arg(data.m_MaxFrequency, 0, 'f', 2)); updateThreadInfoValue(threadIndex, 7, QString("%1 Hz").arg(data.m_AvgFrequency, 0, 'f', 2)); updateThreadInfoValue( threadIndex, 8, QString("%1 ms").arg(data.m_CurrentPeriod[data.m_CurrentPeriod.size() - 1], 0, 'f', 2)); updateThreadInfoValue(threadIndex, 9, QString("%1 ms").arg(data.m_SetPeriod, 0, 'f', 2)); updateThreadInfoValue(threadIndex, 10, QString("%1 ms").arg(data.m_SetPeriod, 0, 'f', 2)); updateThreadInfoValue(threadIndex, 11, QString("%1 ms").arg(data.m_MaxPeriod, 0, 'f', 2)); updateThreadInfoValue(threadIndex, 12, QString("%1 ms").arg(data.m_AvgPeriod, 0, 'f', 2)); } } // 更新线程信息值 void ModelStatusWidget::updateThreadInfoValue(unsigned int threadIndex, int index, const QString &newValue) { if (currentTabIndex != 1) return; QLabel *valueLabel = findChild(QString("threadRunValueLabel%1_%2").arg(threadIndex).arg(index)); if (valueLabel) { valueLabel->setText(newValue); if (1 == index) { if (newValue == "未运行") { valueLabel->setStyleSheet("color: gray;"); } else if (newValue == "运行中") { valueLabel->setStyleSheet("color: green;"); } else if (newValue == "暂停中") { valueLabel->setStyleSheet("color: orange;"); } else if (newValue == "错误") { valueLabel->setStyleSheet("color: red;"); } else { valueLabel->setText("未知状态"); valueLabel->setStyleSheet("color: red;"); } } } } // 更新图表数据 void ModelStatusWidget::updatePlotData(const QVector &newXData, const QVector &newYData) { XNCustomPlot *customPlot = nullptr; if (currentTabIndex == 1) customPlot = findChild("modelStatusCustomPlot"); else if (currentTabIndex == 2) customPlot = findChild("dataMonitoringCustomPlot"); else return; if (!customPlot) return; // 确保找到了XNCustomPlot对象 if (customPlot->graphCount() == 1) { // 假设我们只有一个图形 customPlot->graph(0)->setData(newXData, newYData); customPlot->replot(); // 重新绘制图表以显示新数据 } else { customPlot->addGraph(); // 添加一个新的图形 customPlot->graph(0)->setData(newXData, newYData); customPlot->replot(); // 重新绘制图表以显示新数据 } } // 更新模型数据 void ModelStatusWidget::updateModelData(const XNRuntimeData &data) { // 如果当前标签页不是第二个,则直接返回 if (currentTabIndex != 1) return; // 遍历 modelDataVec 向量 for (size_t i = 0; i < modelDataVec.size(); i++) { // 如果当前遍历到的元素 ID 与传入数据的 ID 相同 if (data.m_id == modelDataVec[i].m_id) { double currectFrequency = data.m_CurrentFrequency[0]; // 当前频率 double currentPeriod = data.m_CurrentPeriod[0]; // 当前周期 // 更新模型运行状态 modelDataVec[i].m_RunningState = data.m_RunningState; // 如果模型不是运行状态,则不更新频率和周期数据 if (data.m_RunningState != 1) return; // 更新当前频率 modelDataVec[i].m_CurrentFrequency.push_back(currectFrequency); // 更新当前周期 modelDataVec[i].m_CurrentPeriod.push_back(currentPeriod); // 更新周期计数 modelDataVec[i].m_CycleCount = data.m_CycleCount; // 更新最小频率 modelDataVec[i].m_MinFrequency = std::min(currectFrequency, modelDataVec[i].m_MinFrequency); // 更新最大频率 modelDataVec[i].m_MaxFrequency = std::max(currectFrequency, modelDataVec[i].m_MaxFrequency); // 更新平均频率 modelDataVec[i].m_AvgFrequency = ((data.m_CycleCount - 1) * modelDataVec[i].m_AvgFrequency + currectFrequency) / data.m_CycleCount; // 更新最小周期 modelDataVec[i].m_MinPeriod = std::min(currentPeriod, modelDataVec[i].m_MinPeriod); // 更新最大周期 modelDataVec[i].m_MaxPeriod = std::max(currentPeriod, modelDataVec[i].m_MaxPeriod); // 更新平均周期 modelDataVec[i].m_AvgPeriod = ((data.m_CycleCount - 1) * modelDataVec[i].m_AvgPeriod + currentPeriod) / data.m_CycleCount; // 如果当前频率或周期向量的长度超过100,移除最前面的元素 if (modelDataVec[i].m_CurrentFrequency.size() > 100) { modelDataVec[i].m_CurrentFrequency.pop_front(); modelDataVec[i].m_CurrentPeriod.pop_front(); } // 检查当前选中的行是否是被更新的行 // 找到名为 "modelStatusTableWidget" 的子控件 QTableWidget *modelStatusTableWidget = findChild("modelStatusTableWidget"); if (modelStatusTableWidget) { int currentRow = modelStatusTableWidget->currentRow(); if (currentRow == static_cast(i)) { emit updateModelCutomPlotData(); } } // 更新表格数据 updateModelTableData(modelDataVec[i], i); return; } } // 如果未找到匹配的 ID,将传入的数据添加到 modelDataVec 向量末尾 modelDataVec.push_back(data); // 更新表格数据 updateModelTableData(data, modelDataVec.size() - 1); } // 更新模型表格数据 void ModelStatusWidget::updateModelTableData(const XNRuntimeData &data, int rowIndex) { // 如果当前标签页索引不为1,则直接返回 if (currentTabIndex != 1) return; // 查找名为"threadStatusTableWidget"的QTableWidget QTableWidget *modelStatusTableWidget = findChild("modelStatusTableWidget"); if (!modelStatusTableWidget) { // 如果没有找到,则直接返回 return; } // 设置表格的行数 modelStatusTableWidget->setRowCount(std::max(rowIndex + 1, (int)modelDataVec.size())); // 设置模型名称 modelStatusTableWidget->setItem(rowIndex, 0, new QTableWidgetItem(data.m_name)); // 设置模型ID modelStatusTableWidget->setItem(rowIndex, 1, new QTableWidgetItem(QString::number(data.m_id))); // 根据模型的运行状态设置不同的状态显示 switch (data.m_RunningState) { case 0: // 未运行状态 modelStatusTableWidget->setItem(rowIndex, 2, new QTableWidgetItem("未运行")); modelStatusTableWidget->item(rowIndex, 2)->setForeground(QBrush(Qt::gray)); break; case 1: // 运行中状态 modelStatusTableWidget->setItem(rowIndex, 2, new QTableWidgetItem("运行中")); modelStatusTableWidget->item(rowIndex, 2)->setForeground(QBrush(Qt::green)); break; case 2: // 暂停中状态 modelStatusTableWidget->setItem(rowIndex, 2, new QTableWidgetItem("暂停中")); modelStatusTableWidget->item(rowIndex, 2)->setForeground(QBrush(Qt::yellow)); break; default: // 未知状态 modelStatusTableWidget->setItem(rowIndex, 2, new QTableWidgetItem("未知状态")); modelStatusTableWidget->item(rowIndex, 2)->setForeground(QBrush(Qt::red)); break; } // 设置所属线程 if (systemStatusWidget != nullptr) { modelStatusTableWidget->setItem( rowIndex, 3, new QTableWidgetItem(systemStatusWidget->getThreadName(data.m_AffinityMask))); } else { modelStatusTableWidget->setItem(rowIndex, 3, new QTableWidgetItem("未知线程")); } // 设置节点号 modelStatusTableWidget->setItem(rowIndex, 4, new QTableWidgetItem(QString::number(data.m_NodeID))); // 设置模型优先级 modelStatusTableWidget->setItem(rowIndex, 5, new QTableWidgetItem(QString::number(data.m_Priority))); // 设置模型设置频率 modelStatusTableWidget->setItem( rowIndex, 6, new QTableWidgetItem(QString::number(data.m_SetFrequency, 'f', 2) + " Hz")); // 设置模型最小频率 modelStatusTableWidget->setItem( rowIndex, 7, new QTableWidgetItem(QString::number(data.m_MinFrequency, 'f', 2) + " Hz")); // 设置模型最大频率 modelStatusTableWidget->setItem( rowIndex, 8, new QTableWidgetItem(QString::number(data.m_MaxFrequency, 'f', 2) + " Hz")); // 设置模型平均频率 modelStatusTableWidget->setItem( rowIndex, 9, new QTableWidgetItem(QString::number(data.m_AvgFrequency, 'f', 2) + " Hz")); // 设置模型周期计数 modelStatusTableWidget->setItem(rowIndex, 10, new QTableWidgetItem(QString::number(data.m_CycleCount))); // 设置模型设置周期 modelStatusTableWidget->setItem( rowIndex, 11, new QTableWidgetItem(QString::number(data.m_SetPeriod, 'f', 2) + " ms")); // 设置模型最小周期 modelStatusTableWidget->setItem( rowIndex, 12, new QTableWidgetItem(QString::number(data.m_MinPeriod, 'f', 2) + " ms")); // 设置模型最大周期 modelStatusTableWidget->setItem( rowIndex, 13, new QTableWidgetItem(QString::number(data.m_MaxPeriod, 'f', 2) + " ms")); // 设置模型平均周期 modelStatusTableWidget->setItem( rowIndex, 14, new QTableWidgetItem(QString::number(data.m_AvgPeriod, 'f', 2) + " ms")); } // 提取字符串末尾的数字 int ModelStatusWidget::extractTrailingNumbers(const QString &str, int count) { QRegularExpression re("\\d{1," + QString::number(count) + "}$"); QRegularExpressionMatch match = re.match(str); if (match.hasMatch()) { return match.captured().toInt(); } else { return -1; // 如果没有找到匹配项,则返回一个-1 } }