#include "DetectImageWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ============================================================================ // ImageZoomView 实现 // ============================================================================ ImageZoomView::ImageZoomView(QWidget* parent) : QWidget(parent) { setStyleSheet("background-color: rgb(25, 26, 28);"); QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(10, 10, 10, 10); mainLayout->setSpacing(10); // 顶部标题栏 QHBoxLayout* topLayout = new QHBoxLayout(); m_titleLabel = new QLabel(this); m_titleLabel->setStyleSheet("QLabel { color: rgb(221, 225, 233); font-size: 16px; font-weight: bold; background: transparent; }"); topLayout->addWidget(m_titleLabel); topLayout->addStretch(); // 返回按钮 QPushButton* btnBack = new QPushButton(QString::fromUtf8("返回"), this); btnBack->setFixedSize(80, 30); btnBack->setStyleSheet( "QPushButton { background-color: rgb(60, 62, 68); color: rgb(221, 225, 233); " "border: 1px solid rgb(80, 82, 88); border-radius: 5px; font-size: 14px; }" "QPushButton:hover { background-color: rgb(80, 82, 88); }" "QPushButton:pressed { background-color: rgb(50, 52, 58); }" ); connect(btnBack, &QPushButton::clicked, this, &ImageZoomView::exitRequested); topLayout->addWidget(btnBack); mainLayout->addLayout(topLayout); // 滚动区域 m_scrollArea = new QScrollArea(this); m_scrollArea->setWidgetResizable(false); m_scrollArea->setAlignment(Qt::AlignCenter); m_scrollArea->setStyleSheet( "QScrollArea { background-color: rgb(38, 40, 47); border: none; }" "QScrollBar:vertical { background-color: rgb(38, 40, 47); width: 12px; }" "QScrollBar::handle:vertical { background-color: rgb(80, 82, 88); border-radius: 6px; min-height: 20px; }" "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }" "QScrollBar:horizontal { background-color: rgb(38, 40, 47); height: 12px; }" "QScrollBar::handle:horizontal { background-color: rgb(80, 82, 88); border-radius: 6px; min-width: 20px; }" "QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }" ); m_imageLabel = new QLabel(m_scrollArea); m_imageLabel->setAlignment(Qt::AlignCenter); m_imageLabel->setStyleSheet("background-color: rgb(38, 40, 47);"); m_imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); m_imageLabel->setScaledContents(false); m_scrollArea->setWidget(m_imageLabel); mainLayout->addWidget(m_scrollArea, 1); // 底部信息栏 m_infoLabel = new QLabel(this); m_infoLabel->setStyleSheet("QLabel { color: rgb(180, 180, 180); font-size: 12px; background: transparent; }"); m_infoLabel->setText(QString::fromUtf8("滚轮缩放 | 拖拽平移 | 双击还原 | 点击返回按钮退出")); mainLayout->addWidget(m_infoLabel); setFocusPolicy(Qt::StrongFocus); } void ImageZoomView::setImage(const QImage& image, const QString& title) { m_originalImage = image; m_titleLabel->setText(title); m_needResetZoom = true; updateImageDisplay(); } void ImageZoomView::showEvent(QShowEvent* event) { QWidget::showEvent(event); if (m_needResetZoom && !m_originalImage.isNull()) { m_needResetZoom = false; QMetaObject::invokeMethod(this, &ImageZoomView::resetZoom, Qt::QueuedConnection); } } void ImageZoomView::updateImageDisplay() { if (m_originalImage.isNull()) { m_imageLabel->setText(QString::fromUtf8("无图像")); return; } int newWidth = static_cast(m_originalImage.width() * m_scaleFactor); int newHeight = static_cast(m_originalImage.height() * m_scaleFactor); QImage scaledImage = m_originalImage.scaled(newWidth, newHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_imageLabel->setPixmap(QPixmap::fromImage(scaledImage)); m_imageLabel->resize(scaledImage.size()); QString info = QString::fromUtf8("缩放: %1% | 尺寸: %2x%3 | 滚轮缩放 | 拖拽平移 | 双击还原") .arg(static_cast(m_scaleFactor * 100)) .arg(m_originalImage.width()) .arg(m_originalImage.height()); m_infoLabel->setText(info); } void ImageZoomView::resetZoom() { if (m_originalImage.isNull()) return; QSize viewSize = m_scrollArea->viewport()->size(); double scaleX = static_cast(viewSize.width() - 20) / m_originalImage.width(); double scaleY = static_cast(viewSize.height() - 20) / m_originalImage.height(); m_scaleFactor = qMin(scaleX, scaleY); m_scaleFactor = qMax(m_scaleFactor, MIN_SCALE); updateImageDisplay(); m_scrollArea->horizontalScrollBar()->setValue( (m_imageLabel->width() - m_scrollArea->viewport()->width()) / 2); m_scrollArea->verticalScrollBar()->setValue( (m_imageLabel->height() - m_scrollArea->viewport()->height()) / 2); } void ImageZoomView::wheelEvent(QWheelEvent* event) { if (m_originalImage.isNull()) return; double delta = event->angleDelta().y() > 0 ? SCALE_STEP : -SCALE_STEP; double newScale = m_scaleFactor + delta; newScale = qMax(newScale, MIN_SCALE); newScale = qMin(newScale, MAX_SCALE); if (qAbs(newScale - m_scaleFactor) > 0.001) { QScrollBar* hBar = m_scrollArea->horizontalScrollBar(); QScrollBar* vBar = m_scrollArea->verticalScrollBar(); double hRatio = hBar->maximum() > 0 ? static_cast(hBar->value()) / hBar->maximum() : 0.5; double vRatio = vBar->maximum() > 0 ? static_cast(vBar->value()) / vBar->maximum() : 0.5; m_scaleFactor = newScale; updateImageDisplay(); hBar->setValue(static_cast(hRatio * hBar->maximum())); vBar->setValue(static_cast(vRatio * vBar->maximum())); } event->accept(); } void ImageZoomView::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { m_dragging = true; m_lastPos = event->pos(); setCursor(Qt::ClosedHandCursor); } QWidget::mousePressEvent(event); } void ImageZoomView::mouseMoveEvent(QMouseEvent* event) { if (m_dragging) { QPoint delta = event->pos() - m_lastPos; m_lastPos = event->pos(); QScrollBar* hBar = m_scrollArea->horizontalScrollBar(); QScrollBar* vBar = m_scrollArea->verticalScrollBar(); hBar->setValue(hBar->value() - delta.x()); vBar->setValue(vBar->value() - delta.y()); } QWidget::mouseMoveEvent(event); } void ImageZoomView::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { m_dragging = false; setCursor(Qt::ArrowCursor); } QWidget::mouseReleaseEvent(event); } void ImageZoomView::mouseDoubleClickEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { resetZoom(); } QWidget::mouseDoubleClickEvent(event); } void ImageZoomView::keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Escape: emit exitRequested(); break; case Qt::Key_Plus: case Qt::Key_Equal: m_scaleFactor = qMin(m_scaleFactor + SCALE_STEP, MAX_SCALE); updateImageDisplay(); break; case Qt::Key_Minus: m_scaleFactor = qMax(m_scaleFactor - SCALE_STEP, MIN_SCALE); updateImageDisplay(); break; case Qt::Key_0: m_scaleFactor = 1.0; updateImageDisplay(); break; case Qt::Key_F: resetZoom(); break; default: QWidget::keyPressEvent(event); } } void ImageZoomView::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (!m_originalImage.isNull()) { resetZoom(); } } // ============================================================================ // DetectImageWidget 实现 // ============================================================================ DetectImageWidget::DetectImageWidget(QWidget* parent) : QWidget(parent) { setupUI(); } DetectImageWidget::~DetectImageWidget() { } void DetectImageWidget::setupUI() { QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); // 使用 QStackedWidget 切换普通视图和放大视图 m_stackedWidget = new QStackedWidget(this); // 普通视图 - QGraphicsView m_graphicsView = new QGraphicsView(this); m_scene = new QGraphicsScene(this); m_graphicsView->setScene(m_scene); m_graphicsView->setRenderHint(QPainter::Antialiasing); m_graphicsView->setRenderHint(QPainter::SmoothPixmapTransform); m_graphicsView->setDragMode(QGraphicsView::NoDrag); m_graphicsView->setStyleSheet("QGraphicsView { background-color: rgb(47, 48, 52); border: none; }"); m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_graphicsView->setCursor(Qt::PointingHandCursor); // 设置点击事件过滤 m_graphicsView->viewport()->installEventFilter(this); m_stackedWidget->addWidget(m_graphicsView); // 放大视图 m_zoomView = new ImageZoomView(this); connect(m_zoomView, &ImageZoomView::exitRequested, this, &DetectImageWidget::showNormalView); m_stackedWidget->addWidget(m_zoomView); mainLayout->addWidget(m_stackedWidget); // 默认显示普通视图 m_stackedWidget->setCurrentIndex(0); // 设置右键菜单 m_graphicsView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_graphicsView, &QGraphicsView::customContextMenuRequested, this, &DetectImageWidget::showContextMenu); // 创建右键菜单 m_contextMenu = new QMenu(this); m_saveDataAction = new QAction(QString::fromUtf8("保存检测数据"), this); m_saveDataAction->setEnabled(false); // 设置菜单样式 m_contextMenu->setStyleSheet( "QMenu { background-color: rgb(60, 62, 68); color: rgb(221, 225, 233); border: 1px solid rgb(80, 82, 88); }" "QMenu::item { padding: 8px 25px; }" "QMenu::item:selected { background-color: rgb(80, 82, 88); }" "QMenu::item:disabled { color: rgb(120, 120, 120); }" ); m_contextMenu->addAction(m_saveDataAction); connect(m_saveDataAction, &QAction::triggered, this, &DetectImageWidget::onSaveDataTriggered); } bool DetectImageWidget::eventFilter(QObject* obj, QEvent* event) { if (obj == m_graphicsView->viewport()) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent* mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::LeftButton) { // 点击放大 if (!m_image.isNull()) { showZoomView(); emit clicked(); } return true; } } } return QWidget::eventFilter(obj, event); } void DetectImageWidget::setImage(const QImage& image) { m_image = image; updateImageDisplay(); } QImage DetectImageWidget::getImage() const { return m_image; } void DetectImageWidget::clearImage() { m_image = QImage(); m_scene->clear(); } void DetectImageWidget::setTitle(const QString& title) { m_title = title; } void DetectImageWidget::setSaveDataEnabled(bool enabled) { m_saveDataEnabled = enabled; m_saveDataAction->setEnabled(enabled); } void DetectImageWidget::setSaveDataCallback(const SaveDataCallback& callback) { m_saveDataCallback = callback; } void DetectImageWidget::setSaveDataMenuText(const QString& text) { m_saveDataAction->setText(text); } void DetectImageWidget::showZoomView() { if (m_image.isNull()) { return; } m_zoomView->setImage(m_image, m_title); m_stackedWidget->setCurrentIndex(1); m_zoomView->setFocus(); } void DetectImageWidget::showNormalView() { m_stackedWidget->setCurrentIndex(0); } void DetectImageWidget::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); updateImageDisplay(); } void DetectImageWidget::updateImageDisplay() { if (m_image.isNull()) { return; } m_scene->clear(); QPixmap pixmap = QPixmap::fromImage(m_image); m_scene->addPixmap(pixmap); m_scene->setSceneRect(pixmap.rect()); // 自适应显示 m_graphicsView->fitInView(m_scene->sceneRect(), Qt::KeepAspectRatio); } void DetectImageWidget::showContextMenu(const QPoint& pos) { // 检查是否有数据可保存 if (!m_saveDataEnabled) { return; } m_contextMenu->exec(m_graphicsView->mapToGlobal(pos)); } void DetectImageWidget::onSaveDataTriggered() { // 发射信号,让外部处理保存逻辑 emit saveDataRequested(this); // 如果设置了回调,也调用回调 if (m_saveDataCallback) { // 获取默认保存路径 QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"); QString defaultFileName = QString("%1/detect_data_%2.pcd").arg(defaultPath).arg(timestamp); QString filePath = QFileDialog::getSaveFileName( this, QString::fromUtf8("保存检测数据"), defaultFileName, QString::fromUtf8("点云文件 (*.pcd);;所有文件 (*.*)") ); if (!filePath.isEmpty()) { m_saveDataCallback(filePath); } } }