如何在QGraphicsView的边框上制作标尺

本文介绍如何在 QGraphicsView 的边框上实现一个尺子功能,通过使用 Qt 框架来定制视图边界的行为,适用于需要精确布局和尺寸测量的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include "AutoCuttingWidget.h" #include "AutoCuttingAlarmTable.h" #include "Component/LightLabel/lightLabel.h" #include "Component/QFlightInstruments/LayoutSquare.hpp" #include "Component/LineRuler/LineRuler.h" #include "Component/LineRuler/RulerWidget.h" #include "AutoCuttingActionDBManager.h" #include "CircuitBaseScene.h" #include "GraphicElementManage.h" #include <QDebug> #include <SystemSetting.h> AutoCuttingWidget::AutoCuttingWidget(QWidget* parent) : QWidget(parent) , ui(new Ui::AutoCuttingWidgetClass()) { ui->setupUi(this); initLineRuler(); initAmpHistoryLineCharts(); initAlarmMessageTable(); initRemotePanel(); initPFDParameter(); initRemoteConnectParam(); initCutMonitor(); initDrawCutPathViewer(); m_autoCutXmlFilePath = QCoreApplication::applicationDirPath() + "/autoCutXML/"; } AutoCuttingWidget::~AutoCuttingWidget() { if (m_updateCutMonitorTimer) { m_updateCutMonitorTimer->stop(); //delete m_updateCutMonitorTimer; m_updateCutMonitorTimer = nullptr; } delete ui; } void AutoCuttingWidget::on_pushButton_drawPath_clicked() { ui->stackedWidget_autocut->setCurrentWidget(ui->page_cutPath); m_curView->enbaleDrawLine(true); } void AutoCuttingWidget::on_pushButton_clearCutPath_clicked() { QList<QGraphicsItem*> itemList = m_curView->items(); for (QGraphicsItem* item : itemList) { m_curView->scene()->removeItem(item); } } void AutoCuttingWidget::on_pushButton_startAuto_clicked() { m_updateCutMonitorTimer->stop(); resetAutoCuttingDataList(); m_updateCutMonitorTimer->start(hxcommon::SystemSetting::autoCutRefreshInterval); ui->stackedWidget_autocut->setCurrentWidget(ui->page_cutMonitor); } void AutoCuttingWidget::on_pushButton_stopAuto_clicked() { m_updateCutMonitorTimer->stop(); } void AutoCuttingWidget::on_pushButton_systemVoltage_clicked() { qDebug() << "on_pushButton_systemVoltage_clicked"; ui->stackedWidget->setCurrentIndex(0); } void AutoCuttingWidget::on_pushButton_pumpCurrent_clicked() { qDebug() << "on_pushButton_pumpCurrent_clicked"; ui->stackedWidget->setCurrentIndex(1); } void AutoCuttingWidget::on_pushButton_cutLowCurrent_clicked() { qDebug() << "on_pushButton_cutLowCurrent_clicked"; ui->stackedWidget->setCurrentIndex(2); } void AutoCuttingWidget::on_pushButton_cutHighCurrent_clicked() { qDebug() << "on_pushButton_cutHighCurrent_clicked"; ui->stackedWidget->setCurrentIndex(3); } void AutoCuttingWidget::on_pushButton_memoryStart_clicked() { QDateTime time = QDateTime::currentDateTime(); QString dateTime = time.toString("MM-dd-hh-mm"); QString pathname = m_autoCutXmlFilePath + QStringLiteral("自动截割路径_%1.xml").arg(dateTime); m_autoCutFileSavePath = QFileDialog::getSaveFileName(this, QStringLiteral("保存自动截割路径"), pathname, "Xml(*.xml)"); } void AutoCuttingWidget::on_pushButton_memoryStop_clicked() { QString savePath = m_autoCutFileSavePath; if (savePath.isEmpty()) { QDateTime time = QDateTime::currentDateTime(); QString dateTime = time.toString("MM-dd-hh-mm"); savePath = m_autoCutXmlFilePath + QStringLiteral("自动截割路径_%1.xml").arg(dateTime); } if (m_graphicElementManager.saveCurrentPage(savePath)) { emit showResponseMessage(hxcommon::NotifyType::Notify_Type_Success,QStringLiteral("自动截割路径保存成功!")); } else { emit showResponseMessage(hxcommon::NotifyType::Notify_Type_Error, QStringLiteral("保存失败!")); } } void AutoCuttingWidget::on_pushButton_memoryModel_clicked() { QString fileName = QFileDialog::getOpenFileName(this, QStringLiteral("请打开自动截割路径文件"), m_autoCutFileSavePath, "Xml(*.xml)"); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Open failed"; return; } m_graphicElementManager.openPage(file); } void AutoCuttingWidget::initAlarmMessageTable() { m_alarmMessageTable = new AutoCuttingAlarmTable(ui->widget_abnormalInfo); QLayout* alarmMessageTableLayout = ui->widget_abnormalInfo->layout(); if (alarmMessageTableLayout) { // 如果 layout 是 QVBoxLayout 类型,添加控件 QVBoxLayout* vboxLayout = qobject_cast<QVBoxLayout*>(alarmMessageTableLayout); if (vboxLayout) { vboxLayout->addWidget(m_alarmMessageTable); } } } void AutoCuttingWidget::initRemotePanel() { // 创建 4x4 的网格布局 m_remotePanelLayout = new QGridLayout(ui->widget_remotePanel); ui->widget_remotePanel->setLayout(m_remotePanelLayout); // 定义 4x4 的内容 QStringList labels = { QStringLiteral("截割头伸"), QStringLiteral("铲板升"), QStringLiteral("一运正转"), QStringLiteral("左后退"), QStringLiteral("截割头缩"), QStringLiteral("铲板降"), QStringLiteral("一运反转"), QStringLiteral("右后退"), QStringLiteral("左前进"), QStringLiteral("后支撑升"), QStringLiteral("截割左"), QStringLiteral("星轮正转"), QStringLiteral("右前进"), QStringLiteral("后支撑降"), QStringLiteral("截割右"), QStringLiteral("星轮反转") }; // 创建并添加 lightLabel 到布局 int index = 0; for (int row = 0; row < 4; ++row) { for (int col = 0; col < 4; ++col) { lightLabel* label = new lightLabel(ui->widget_remotePanel); label->setLabelText(labels[index++]); // 设置内容 label->setLightPointStep(0); m_remotePanelLayout->addWidget(label, row, col); // 添加到网格布局 } } } void AutoCuttingWidget::initPFDParameter() { LayoutSquare* m_layoutSq = new LayoutSquare(this); m_layoutSq->setContentsMargins(0, 0, 0, 0); m_layoutSq->addWidget(ui->framePFD); } void AutoCuttingWidget::initRemoteConnectParam() { //m_remoteConnectLayout = new QGridLayout(ui->widget_remoteConnectInfo); //ui->widget_remoteConnectInfo->setLayout(m_remoteConnectLayout); //QStringList labels = { // QStringLiteral("遥控在线"), QStringLiteral("油泵启动"), QStringLiteral("截割启动"), QStringLiteral("一运启动"), // QStringLiteral("二运启动"), QStringLiteral("皮带启动"), QStringLiteral("星轮启动"), QStringLiteral("人员误入") //}; //int index = 0; //for (int row = 0; row < 2; ++row) { // for (int col = 0; col < 4; ++col) { // lightLabel* label = new lightLabel(ui->widget_remoteConnectInfo); // label->setLabelText(labels[index++]); // 设置内容 // label->setLightPointStep(0); // m_remoteConnectLayout->addWidget(label, row, col); // 添加到网格布局 // } //} // 创建 4x4 的网格布局 //m_remoteConnectLayout = new QGridLayout(ui->widget_remoteConnectInfo); //ui->widget_remoteConnectInfo->setLayout(m_remoteConnectLayout); //// 定义 4x4 的内容 //QStringList labels = { // QStringLiteral("截割头伸"), QStringLiteral("铲板升"), QStringLiteral("一运正转"), QStringLiteral("左后退"), // QStringLiteral("截割头缩"), QStringLiteral("铲板降"), QStringLiteral("一运反转"), QStringLiteral("右后退"), // QStringLiteral("左前进"), QStringLiteral("后支撑升"), QStringLiteral("截割左"), QStringLiteral("星轮正转"), // QStringLiteral("右前进"), QStringLiteral("后支撑降"), QStringLiteral("截割右"), QStringLiteral("星轮反转") //}; //// 创建并添加 lightLabel 到布局 //int index = 0; //for (int row = 0; row < 4; ++row) { // for (int col = 0; col < 4; ++col) { // lightLabel* label = new lightLabel(ui->widget_remoteConnectInfo); // label->setLabelText(labels[index++]); // 设置内容 // label->setLightPointStep(0); // m_remoteConnectLayout->addWidget(label, row, col); // 添加到网格布局 // } //} } void AutoCuttingWidget::initLineRuler() { // 创建 RulerWidget 实例,方向为垂直 RulerWidget* verticalRuler = new RulerWidget(Qt::Vertical, this); // 检查 widget_verticalRuler 是否已有布局 QLayout* verticallayout = ui->widget_verticalRuler->layout(); if (!verticallayout) { // 如果没有布局,创建一个 QVBoxLayout verticallayout = new QVBoxLayout(ui->widget_verticalRuler); ui->widget_verticalRuler->setLayout(verticallayout); } // 设置布局的间隔和边距为 0 verticallayout->setSpacing(0); verticallayout->setContentsMargins(0, 0, 0, 0); // 将 RulerWidget 添加到布局中 verticallayout->addWidget(verticalRuler); // 创建 RulerWidget 实例,方向为垂直 RulerWidget* horizontalRuler = new RulerWidget(Qt::Horizontal, this); // 检查 widget_verticalRuler 是否已有布局 QLayout* horizontalLayout = ui->widget_horizontalRuler->layout(); if (!horizontalLayout) { // 如果没有布局,创建一个 QVBoxLayout horizontalLayout = new QVBoxLayout(ui->widget_horizontalRuler); ui->widget_verticalRuler->setLayout(horizontalLayout); } // 设置布局的间隔和边距为 0 horizontalLayout->setSpacing(0); horizontalLayout->setContentsMargins(0, 0, 0, 0); // 将 RulerWidget 添加到布局中 horizontalLayout->addWidget(horizontalRuler); } void AutoCuttingWidget::initAmpHistoryLineCharts() { m_lineChartSystemVoltage = new AmpHistoryLineChart(ui->page_systemVoltage); m_lineChartPumpAmp = new AmpHistoryLineChart(ui->page_pumpCurrent); m_lineChartLowCutAmp = new AmpHistoryLineChart(ui->page_cutLowCurrent); m_lineChartHighCutAmp = new AmpHistoryLineChart(ui->page_cutHighCurrent); // 获取 ui 中的布局,并确保布局充满空间 QLayout* layoutSystemVoltage = ui->page_systemVoltage->layout(); if (layoutSystemVoltage) { // 如果 layout 是 QVBoxLayout 类型,添加控件 QVBoxLayout* vboxLayoutSystemVoltage = qobject_cast<QVBoxLayout*>(layoutSystemVoltage); if (vboxLayoutSystemVoltage) { vboxLayoutSystemVoltage->addWidget(m_lineChartSystemVoltage); } } QLayout* layoutPump = ui->page_pumpCurrent->layout(); if (layoutPump) { QVBoxLayout* vboxLayoutPump = qobject_cast<QVBoxLayout*>(layoutPump); if (vboxLayoutPump) { vboxLayoutPump->addWidget(m_lineChartPumpAmp); } } QLayout* layoutLowCut = ui->page_cutLowCurrent->layout(); if (layoutLowCut) { QVBoxLayout* vboxLayoutLowCut = qobject_cast<QVBoxLayout*>(layoutLowCut); if (vboxLayoutLowCut) { vboxLayoutLowCut->addWidget(m_lineChartLowCutAmp); } } QLayout* layoutHighCut = ui->page_cutHighCurrent->layout(); if (layoutHighCut) { QVBoxLayout* vboxLayoutHighCut = qobject_cast<QVBoxLayout*>(layoutHighCut); if (vboxLayoutHighCut) { vboxLayoutHighCut->addWidget(m_lineChartHighCutAmp); } } m_lineChartPumpAmp->setYAxisRange(0, 100); m_lineChartLowCutAmp->setYAxisRange(0, 100); m_lineChartHighCutAmp->setYAxisRange(0, 100); // TODO:假设数据点,替换为实时数据 QList<QPointF> list; QDateTime currentTime = QDateTime::currentDateTime(); for (int i = 0; i < 120; ++i) { // 120个数据点,代表最近两个小时 list.append(QPointF(currentTime.addSecs(-i * 60).toMSecsSinceEpoch(), QRandomGenerator::global()->bounded(10, 14))); } m_lineChartPumpAmp->setLineData(list); m_lineChartLowCutAmp->setLineData(list); m_lineChartHighCutAmp->setLineData(list); } void AutoCuttingWidget::initCutMonitor() { m_autoCuttingProcessor = new AutoCuttingProcessor(this); hxDataBaseManager::AutoCuttingActionDBManager dbManager; QList<hxcommon::AutoCuttingAction*> list = dbManager.getActionList(); m_autoCuttingProcessor->setActionList(list); m_autoCuttingProcessor->doWork(); m_updateCutMonitorTimer = new QTimer(); connect(m_updateCutMonitorTimer, &QTimer::timeout, this, &AutoCuttingWidget::testDrawCutterMonitor); m_cutter = new QGraphicsPixmapItem(QPixmap(":/mainwindow/Resource/AutoCutting/cutter.png").scaled(60, 60, Qt::KeepAspectRatio)); } void AutoCuttingWidget::resetAutoCuttingDataList() { if (m_updateCutMonitorTimer) { m_updateCutMonitorTimer->stop();//先停止可能正在进行的 } // 重置GraphicsScene int width = ui->widget_cutterMonitorPainter->width(); int height = ui->widget_cutterMonitorPainter->height(); int rowPointCount = hxcommon::SystemSetting::autoCutRowTakeTime / hxcommon::SystemSetting::autoCutRefreshInterval;//一行多少点 int columnPointCount = hxcommon::SystemSetting::autoCutColumnTakeTime / hxcommon::SystemSetting::autoCutRefreshInterval;//一列多少点 int columnSplitPointCount = columnPointCount / hxcommon::SystemSetting::autoCutColumnSplit;//向上的每段多少点 double wItem = 540.0 / rowPointCount;//SceneRect尺寸600 - 截割头60 double hItem = 360.0 / columnPointCount;//SceneRect尺寸420 - 截割头60 ui->graphicsViewCutterMonitor->setGeometry(width / 2 - 310, height / 2 - 220, 620, 440);//居中,因为带20边框 QGraphicsScene* scene = ui->graphicsViewCutterMonitor->scene(); if (!scene) { scene = new QGraphicsScene(this); ui->graphicsViewCutterMonitor->setScene(scene); } scene->setSceneRect(0, 0, 600, 420); // 禁用滚动条 ui->graphicsViewCutterMonitor->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->graphicsViewCutterMonitor->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 禁用鼠标拖拽滚动 ui->graphicsViewCutterMonitor->setDragMode(QGraphicsView::NoDrag); // 禁用鼠标滚轮滚动 ui->graphicsViewCutterMonitor->setTransformationAnchor(QGraphicsView::NoAnchor); // 禁用索引提高性能 scene->setItemIndexMethod(QGraphicsScene::NoIndex); //清空画布 scene->clear(); //清空指令列表 m_autoCuttingDataList.clear(); //不管分多少段,最底下从左到右都是一样的 for (int x = 0; x < rowPointCount; x++) { int y = QRandomGenerator::global()->bounded(columnPointCount - 2, columnPointCount); //int y = columnPointCount - 1; AutoCuttingPoint point(x, y, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128 - hxcommon::SystemSetting::autoCutHorizontalSpeed); m_autoCuttingDataList.append(point);//从左到右 } for (int i = 0; i < hxcommon::SystemSetting::autoCutColumnSplit; i++) { if (i % 2 == 0) { //顺着右侧向上 int yStart = columnPointCount - columnSplitPointCount * i - 1; int yEnd = columnPointCount - columnSplitPointCount * (i + 1); for (int y = yStart; y >= yEnd; y--) { int x = QRandomGenerator::global()->bounded(rowPointCount - 2, rowPointCount); //int x = rowPointCount - 1; AutoCuttingPoint point(x, y, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128 - hxcommon::SystemSetting::autoCutHorizontalSpeed, 128); m_autoCuttingDataList.append(point);//右边向上 } for (int x = rowPointCount - 1; x >= 0; x--) { int y = QRandomGenerator::global()->bounded(yEnd - 2, yEnd); //int y = yEnd - 1; AutoCuttingPoint point(x, y, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128 + hxcommon::SystemSetting::autoCutHorizontalSpeed); m_autoCuttingDataList.append(point);//从右到左 } } else { //顺着左侧向上 int yStart = columnPointCount - columnSplitPointCount * i - 1; int yEnd = columnPointCount - columnSplitPointCount * (i + 1); for (int y = yStart; y >= yEnd; y--) { int x = QRandomGenerator::global()->bounded(-1, 1); //int x = 0; AutoCuttingPoint point(x, y, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128 - hxcommon::SystemSetting::autoCutHorizontalSpeed, 128); m_autoCuttingDataList.append(point);//左边向上 } for (int x = 0; x < rowPointCount; x++) { int y = QRandomGenerator::global()->bounded(yEnd - 2, yEnd); //int y = yEnd - 1; AutoCuttingPoint point(x, y, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128 - hxcommon::SystemSetting::autoCutHorizontalSpeed); m_autoCuttingDataList.append(point);//从左到右 } } } //结束时添加停止命令 if (hxcommon::SystemSetting::autoCutColumnSplit % 2 == 0) { AutoCuttingPoint point1(rowPointCount - 1, 0, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128); m_autoCuttingDataList.append(point1); AutoCuttingPoint point2(rowPointCount - 1, 0, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128); m_autoCuttingDataList.append(point2); } else { AutoCuttingPoint point1(0, 0, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128); m_autoCuttingDataList.append(point1); AutoCuttingPoint point2(0, 0, hxcommon::HeadingCutterCmdEnum::JoystickArm, 128, 128); m_autoCuttingDataList.append(point2); } } void AutoCuttingWidget::initDrawCutPathViewer() { // 创建管理器 hxdraw2dlib::GraphicElementManage manager; // 创建场景 hxdraw2dlib::CircuitBaseScene* pScene = new hxdraw2dlib::CircuitBaseScene(this); //pScene->setSceneRect(-297 * 3 / 2, -210 * 3 / 2, 297 * 3, 210 * 3); pScene->setScalePerGrid(10.0); QTimer::singleShot(0, this, [=]() { QSize widgetSize = ui->widget_drawCutPathView->size(); pScene->setSceneRect(0, 0, widgetSize.width(), widgetSize.height()); }); // 创建视图(不要设置 parent) hxdraw2dlib::CircuitBaseView* view = new hxdraw2dlib::CircuitBaseView(pScene); m_curView = view; view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 获取或创建布局 QLayout* layout = ui->widget_drawCutPathView->layout(); if (layout) { // 如果 layout 是 QVBoxLayout 类型,添加控件 QVBoxLayout* vboxLayout = qobject_cast<QVBoxLayout*>(layout); vboxLayout->setContentsMargins(0, 0, 0, 0); // 去除边距 vboxLayout->setSpacing(0); // 去除间隔 if (vboxLayout) { //QTimer::singleShot(0, this, [=]() { //vboxLayout->addWidget(m_curView); //}); } } m_graphicElementManager.setCurView(m_curView); } void AutoCuttingWidget::testDrawCutterMonitor() { //获取本次要绘制的点 if (m_autoCuttingDataList.size() == 0) { return; } AutoCuttingPoint point = m_autoCuttingDataList.takeFirst(); emit sendAutoCuttingCmd(point.cmd, point.b0, point.b1); m_cutterRotation -= 13; if (m_cutterRotation <= -360) { m_cutterRotation = 0; } int width = ui->widget_cutterMonitorPainter->width(); int height = ui->widget_cutterMonitorPainter->height(); int rowPointCount = hxcommon::SystemSetting::autoCutRowTakeTime / hxcommon::SystemSetting::autoCutRefreshInterval;//一行多少点 int columnPointCount = hxcommon::SystemSetting::autoCutColumnTakeTime / hxcommon::SystemSetting::autoCutRefreshInterval;//一列多少点 double wItem = 540.0 / rowPointCount;//SceneRect尺寸600 - 截割头60 double hItem = 360.0 / columnPointCount;//SceneRect尺寸420 - 截割头60 //qDebug() << "width" << width << "height" << height; ui->graphicsViewCutterMonitor->setGeometry(width / 2 - 310, height / 2 - 220, 620, 440);//居中,因为带20边框 QGraphicsScene* scene = ui->graphicsViewCutterMonitor->scene(); if (!scene) { scene = new QGraphicsScene(this); ui->graphicsViewCutterMonitor->setScene(scene); } scene->setSceneRect(0, 0, 600, 420); // 禁用滚动条 ui->graphicsViewCutterMonitor->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->graphicsViewCutterMonitor->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 禁用鼠标拖拽滚动 ui->graphicsViewCutterMonitor->setDragMode(QGraphicsView::NoDrag); // 禁用鼠标滚轮滚动 ui->graphicsViewCutterMonitor->setTransformationAnchor(QGraphicsView::NoAnchor); // 禁用索引提高性能 scene->setItemIndexMethod(QGraphicsScene::NoIndex); // 先移除画上去的截割头 scene->removeItem(m_cutter); //画很粗的圆组成切割图形 QGraphicsEllipseItem* circle = new QGraphicsEllipseItem(); circle->setRect(0, 0, 60, 60); // x, y, width, height (width=height时为圆形) circle->setPos(point.x * wItem, point.y * hItem); // 设置位置 circle->setBrush(QColor(26, 155, 114)); // 设置填充颜色 circle->setPen(QPen(QColor(26, 155, 114), 1)); // 设置边框 scene->addItem(circle);//添加到场景 //画小点连成轨迹 QGraphicsEllipseItem* linePoint = new QGraphicsEllipseItem(); linePoint->setRect(0, 0, 1, 1); // x, y, width, height (width=height时为圆形) linePoint->setPos(point.x * wItem + 30, point.y * hItem + 30); // 设置位置 linePoint->setBrush(Qt::green); // 设置填充颜色 linePoint->setPen(QPen(Qt::green, 1)); // 设置边框 scene->addItem(linePoint);//添加到场景 //画截割头和截割头旋转 scene->addItem(m_cutter); m_cutter->setPos(point.x * wItem, point.y * hItem); m_cutter->setTransformOriginPoint(QPointF(30, 30)); m_cutter->setRotation(m_cutterRotation); //联动左边姿态 float pitch = (columnPointCount / 2 - point.y) / columnPointCount * 80.0; float heading = (point.x - rowPointCount / 2) / rowPointCount * 80.0; ui->graphicsPFD->setPitch(pitch); ui->graphicsPFD->setHeading(heading); ui->graphicsPFD->update(); // ui->label_cutX->setText(QString::number(point.x)); ui->label_cutY->setText(QString::number(point.y)); }
最新发布
07-27
<think>根据提供的代码,这是一个使用Qt框架编写的C++类`AutoCuttingWidget`的实现文件。该类是一个自定义的QWidget,用于实现自动截割系统的界面和功能。下面我将详细解释代码的结构和功能,并指出可能存在的问题或改进点。 ### 代码结构分析 1. **构造函数** `AutoCuttingWidget::AutoCuttingWidget(QWidget* parent)` - 初始化UI,调用`ui->setupUi(this)`设置界面。 - 调用多个初始化函数:`initLineRuler`, `initAmpHistoryLineCharts`, `initAlarmMessageTable`, `initRemotePanel`, `initPFDParameter`, `initRemoteConnectParam`, `initCutMonitor`, `initDrawCutPathViewer`。 - 设置自动截割XML文件的保存路径。 2. **析构函数** `AutoCuttingWidget::~AutoCuttingWidget()` - 停止定时器并释放资源,删除UI对象。 3. **槽函数**(以`on_`开头的函数) - 响应按钮点击事件,例如:`on_pushButton_drawPath_clicked`(切换到绘制路径页面)、`on_pushButton_clearCutPath_clicked`(清除路径)、`on_pushButton_startAuto_clicked`(开始自动截割)、`on_pushButton_stopAuto_clicked`(停止自动截割)等。 4. **初始化函数** - `initAlarmMessageTable`: 初始化报警信息表格,并添加到界面。 - `initRemotePanel`: 初始化遥控面板,使用网格布局添加16个`lightLabel`控件。 - `initPFDParameter`: 初始化飞行仪表参数(名称可能不准确,实际是设置一个方形布局)。 - `initRemoteConnectParam`: 初始化遥控连接参数(注释掉的代码,未实际使用)。 - `initLineRuler`: 初始化水平和垂直标尺。 - `initAmpHistoryLineCharts`: 初始化四个电流历史曲线图(系统电压、泵电流、截割低电流、截割高电流)。 - `initCutMonitor`: 初始化截割监控,创建自动截割处理器,设置定时器,创建截割头图形项。 - `initDrawCutPathViewer`: 初始化绘制截割路径的视图,包括场景和视图的设置。 5. **其他成员函数** - `resetAutoCuttingDataList`: 重置自动截割数据列表,生成模拟的截割路径点。 - `testDrawCutterMonitor`: 定时器触发的函数,用于在图形视图上绘制截割头的位置和轨迹。 ### 可能存在的问题及改进建议 1. **内存管理** - 在`initRemotePanel`函数中,创建了16个`lightLabel`对象,但没有在析构函数中删除。由于这些对象的父对象是`ui->widget_remotePanel`,当父对象被删除时,这些子对象会被自动删除,所以这里没有问题。 - 在`initLineRuler`中,创建了两个`RulerWidget`对象,同样设置了父对象,因此会自动删除。 - 在`initAmpHistoryLineCharts`中,创建了四个`AmpHistoryLineChart`对象,并添加到布局中,由于布局管理,这些对象会在父对象销毁时被删除。 - 但是,在`testDrawCutterMonitor`函数中,每次调用都会创建新的`QGraphicsEllipseItem`(圆点和轨迹点),并添加到场景中。这些图形项不会被删除,随着时间推移,场景中的图形项会越来越多,可能导致内存占用增加和性能下降。应该考虑在不需要时清理这些图形项,或者重用已有的图形项。 2. **定时器管理** - 在`on_pushButton_startAuto_clicked`中,先停止定时器,然后重置数据,再启动定时器。在`on_pushButton_stopAuto_clicked`中停止定时器。但是,在`resetAutoCuttingDataList`中,如果定时器正在运行,会先停止定时器,但这里在调用`resetAutoCuttingDataList`之前已经停止了定时器(在`on_pushButton_startAuto_clicked`中),所以这里停止两次是安全的,但冗余。 3. **路径生成算法** - `resetAutoCuttingDataList`中生成模拟路径的算法。该算法生成一个之字形的路径(从左到右,然后向上,再从右到左,再向上,如此反复)。但是,在生成路径点时,使用了随机数(`QRandomGenerator::global()->bounded`),这会导致每次生成的路径都不相同。如果这是期望的随机路径,则没有问题;如果是固定的预设路径,则应该使用固定的数据。 4. **图形视图性能** - 在`testDrawCutterMonitor`中,每次定时器触发都会重新设置场景的矩形和视图的几何属性,这是不必要的,因为视图的大小不会频繁改变。建议在窗口大小改变时才调整视图的几何属性(重写`resizeEvent`)。 - 另外,每次绘制都重新设置场景的矩形也是不必要的,因为场景矩形在初始化时已经设置过。 5. **代码可读性和维护性** - 部分初始化函数(如`initRemoteConnectParam`)被注释掉,但代码仍然保留。如果不再使用,应该删除。 - 在`initDrawCutPathViewer`函数中,有被注释掉的代码,以及一个使用`QTimer::singleShot`延迟执行的代码块,这可能是为了在界面显示后再设置视图大小。但这种方式可能不是最佳实践,建议在`resizeEvent`中调整视图大小。 6. **资源路径** - 在`initCutMonitor`中,加载截割头图片时使用了资源路径`":/mainwindow/Resource/AutoCutting/cutter.png"`。需要确保该资源文件存在,否则会显示空白。 7. **未初始化的成员变量** - 在构造函数中,`m_curView`在`initDrawCutPathViewer`中被初始化。但是,在`on_pushButton_drawPath_clicked`中使用了`m_curView`,因此需要确保`initDrawCutPathViewer`在构造函数中被调用(目前确实如此)。 ### 改进建议代码示例 针对图形项内存泄漏问题,修改`testDrawCutterMonitor`函数,重用图形项或者定期清理场景: ```cpp // 在类AutoCuttingWidget中添加成员变量,用于保存上一次绘制的圆和点(如果需要保留轨迹,则不能删除) // 但轨迹点会越来越多,所以需要限制轨迹点的数量 // 例如,只保留最近的1000个点 void AutoCuttingWidget::testDrawCutterMonitor() { // ... [前面的代码不变] // 画很粗的圆组成切割图形(覆盖上一次的圆) if (!m_currentCircle) { m_currentCircle = new QGraphicsEllipseItem(); scene->addItem(m_currentCircle); } m_currentCircle->setRect(0, 0, 60, 60); m_currentCircle->setPos(point.x * wItem, point.y * hItem); m_currentCircle->setBrush(QColor(26, 155, 114)); m_currentCircle->setPen(QPen(QColor(26, 155, 114), 1)); // 画轨迹点(小绿点),并限制轨迹点的数量 QGraphicsEllipseItem* linePoint = new QGraphicsEllipseItem(); linePoint->setRect(0, 0, 1, 1); linePoint->setPos(point.x * wItem + 30, point.y * hItem + 30); linePoint->setBrush(Qt::green); linePoint->setPen(QPen(Qt::green, 1)); scene->addItem(linePoint); m_trackPoints.append(linePoint); // 如果轨迹点超过1000个,删除最旧的点 if (m_trackPoints.size() > 1000) { QGraphicsEllipseItem* oldPoint = m_trackPoints.takeFirst(); scene->removeItem(oldPoint); delete oldPoint; } // ... [后面的代码不变] } ``` 在类定义中添加两个成员变量: ```cpp private: QGraphicsEllipseItem* m_currentCircle; // 当前截割头位置的大圆 QList<QGraphicsEllipseItem*> m_trackPoints; // 存储轨迹点 ``` 在构造函数中初始化`m_currentCircle`为`nullptr`。 另外,建议将视图和场景的初始设置放在一个单独的函数中,并在窗口大小改变时调整视图大小。 ### 相关问题 §§相关问题§§: 1. 在Qt中,如何避免QGraphicsScene中图形项过多导致的内存泄漏? 2. 如何优化QGraphicsView的性能,特别是在频繁更新图形项的场景中? 3. 在Qt中,如何正确管理动态创建的QObject子对象(如QWidget、QGraphicsItem)的内存? 4. 如何实现一个高效的绘图轨迹,既要保存历史轨迹,又要避免内存无限增长?</think>### 问题分析 提供的代码是`AutoCuttingWidget`类的实现,这是一个Qt/C++编写的自动截割系统界面控制类。主要问题集中在以下几个方面: 1. **内存泄漏风险**: - `testDrawCutterMonitor()`中每次调用都创建新的`QGraphicsEllipseItem`而未删除旧项 - 动态创建的UI元素(如`lightLabel`)未在析构函数中释放 2. **性能问题**: - 频繁的图形项创建/销毁操作(每次定时器触发都创建新图形项) - 未使用批量渲染技术(`QGraphicsItemGroup`) 3. **代码结构问题**: - 初始化函数过于臃肿(如`initCutMonitor()`超过50行) - 缺乏错误处理机制(如文件操作未检查路径有效性) - 魔数(magic number)过多(如坐标计算中的540.0, 360.0等) 4. **资源管理**: - 图片资源路径硬编码(`":/mainwindow/Resource/AutoCutting/cutter.png"`) - XML文件路径未验证可写性 ### 解决方案代码 ```cpp // AutoCuttingWidget.cpp - 修复版本 // 1. 添加内存管理成员变量 class AutoCuttingWidget : public QWidget { // ... private: QList<QGraphicsEllipseItem*> m_trackItems; // 轨迹点集合 QGraphicsEllipseItem* m_currentCircle = nullptr; // 当前截割头位置 // ... }; // 2. 修改析构函数释放资源 AutoCuttingWidget::~AutoCuttingWidget() { if (m_updateCutMonitorTimer) { m_updateCutMonitorTimer->stop(); delete m_updateCutMonitorTimer; } // 释放图形项 qDeleteAll(m_trackItems); delete m_currentCircle; delete m_cutter; delete ui; } // 3. 优化图形渲染函数 void AutoCuttingWidget::testDrawCutterMonitor() { if (m_autoCuttingDataList.empty()) return; const AutoCuttingPoint point = m_autoCuttingDataList.takeFirst(); emit sendAutoCuttingCmd(point.cmd, point.b0, point.b1); // 旋转处理 m_cutterRotation = (m_cutterRotation - 13) % 360; // 计算尺寸参数 const int rowPointCount = hxcommon::SystemSetting::autoCutRowTakeTime / hxcommon::SystemSetting::autoCutRefreshInterval; const int columnPointCount = hxcommon::SystemSetting::autoCutColumnTakeTime / hxcommon::SystemSetting::autoCutRefreshInterval; const double wItem = 540.0 / rowPointCount; const double hItem = 360.0 / columnPointCount; QGraphicsScene* scene = ui->graphicsViewCutterMonitor->scene(); if (!scene) { scene = new QGraphicsScene(this); scene->setItemIndexMethod(QGraphicsScene::NoIndex); // 禁用索引 ui->graphicsViewCutterMonitor->setScene(scene); } // 重用图形项(避免频繁创建) if (!m_currentCircle) { m_currentCircle = new QGraphicsEllipseItem(); m_currentCircle->setBrush(QColor(26, 155, 114)); m_currentCircle->setPen(Qt::NoPen); scene->addItem(m_currentCircle); } m_currentCircle->setRect(0, 0, 60, 60); m_currentCircle->setPos(point.x * wItem, point.y * hItem); // 轨迹点(重用或创建) if (m_trackItems.size() >= 1000) { // 限制轨迹数量 delete m_trackItems.takeFirst(); } QGraphicsEllipseItem* trackPoint = new QGraphicsEllipseItem(); trackPoint->setRect(0, 0, 1, 1); trackPoint->setPos(point.x * wItem + 30, point.y * hItem + 30); trackPoint->setBrush(Qt::green); trackPoint->setPen(Qt::NoPen); scene->addItem(trackPoint); m_trackItems.append(trackPoint); // 截割头位置更新 scene->removeItem(m_cutter); m_cutter->setPos(point.x * wItem, point.y * hItem); m_cutter->setTransformOriginPoint(30, 30); m_cutter->setRotation(m_cutterRotation); scene->addItem(m_cutter); // 更新姿态显示 const float pitch = (columnPointCount/2.0 - point.y) / columnPointCount * 80.0f; const float heading = (point.x - rowPointCount/2.0) / rowPointCount * 80.0f; ui->graphicsPFD->setPitch(pitch); ui->graphicsPFD->setHeading(heading); ui->label_cutX->setText(QString::number(point.x)); ui->label_cutY->setText(QString::number(point.y)); } // 4. 文件操作增加错误处理 void AutoCuttingWidget::on_pushButton_memoryStop_clicked() { QString savePath = m_autoCutFileSavePath; if (savePath.isEmpty()) { savePath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/AutoCutting/"; QDir().mkpath(savePath); savePath += QStringLiteral("自动截割路径_%1.xml") .arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss")); } QFileInfo fileInfo(savePath); if (!fileInfo.dir().exists()) { emit showResponseMessage(hxcommon::NotifyType::Notify_Type_Error, QStringLiteral("目录不存在")); return; } if (m_graphicElementManager.saveCurrentPage(savePath)) { emit showResponseMessage(hxcommon::NotifyType::Notify_Type_Success, QStringLiteral("保存成功!")); } else { emit showResponseMessage(hxcommon::NotifyType::Notify_Type_Error, QStringLiteral("保存失败!")); } } ``` ### 关键问题修复说明 1. **内存泄漏修复**: - 添加`m_trackItems`容器统一管理轨迹点 - 在析构函数中使用`qDeleteAll()`释放图形项 - 限制最大轨迹点数(1000个)防止无限增长 2. **性能优化**: - 重用图形项(`m_currentCircle`) - 设置`QGraphicsScene::NoIndex`提升渲染性能 - 使用`Qt::NoPen`减少不必要的边框渲染 3. **资源管理改进**: - 使用`QStandardPaths`获取可写文档路径 - 增加目录存在性检查 - 添加错误反馈机制 4. **代码健壮性**: - 添加空容器检查(`m_autoCuttingDataList.empty()`) - 使用浮点数除法避免整数截断错误 - 常量命名(`wItem`/`hItem`)替代魔数 5. **旋转计算优化**: ```cpp // 原代码(可能产生负值) m_cutterRotation -= 13; if (m_cutterRotation <= -360) m_cutterRotation = 0; // 优化后(保持0-360范围) m_cutterRotation = (m_cutterRotation - 13) % 360; ``` ### 改进建议 1. **使用智能指针**: ```cpp // 头文件中 #include <memory> std::unique_ptr<QGraphicsEllipseItem> m_currentCircle; // 初始化 m_currentCircle = std::make_unique<QGraphicsEllipseItem>(); ``` 2. **批量渲染优化**: ```cpp // 使用QGraphicsItemGroup QGraphicsItemGroup* trackGroup = new QGraphicsItemGroup; scene->addItem(trackGroup); // 添加轨迹点 trackGroup->addToGroup(new QGraphicsEllipseItem(...)); ``` 3. **配置文件路径**: ```cpp // 使用QSettings管理路径 QSettings settings; m_autoCutXmlFilePath = settings.value("AutoCut/Path", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值