#include "MainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ImageLabel::ImageLabel(QWidget *parent) : QLabel(parent) { setAlignment(Qt::AlignCenter); setBackgroundRole(QPalette::Base); setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); setScaledContents(false); setMinimumSize(320, 240); setText(tr("打开一张图片开始查看")); } void ImageLabel::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { Q_EMIT doubleClicked(); } QLabel::mouseDoubleClickEvent(event); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { createActions(); createMenus(); createToolbar(); createCentralWidget(); updateActions(); setWindowTitle(tr("chengnan 图片查看器")); resize(1100, 720); statusBar()->showMessage(tr("就绪")); } void MainWindow::createActions() { openAction_ = new QAction(tr("打开(&O)"), this); openAction_->setShortcut(QKeySequence::Open); connect(openAction_, &QAction::triggered, this, &MainWindow::openImage); saveAction_ = new QAction(tr("保存(&S)"), this); saveAction_->setShortcut(QKeySequence::Save); connect(saveAction_, &QAction::triggered, this, &MainWindow::saveImage); saveAsAction_ = new QAction(tr("另存为(&A)"), this); saveAsAction_->setShortcut(QKeySequence::SaveAs); connect(saveAsAction_, &QAction::triggered, this, &MainWindow::saveImageAs); exitAction_ = new QAction(tr("退出(&X)"), this); exitAction_->setShortcut(QKeySequence::Quit); connect(exitAction_, &QAction::triggered, this, &QWidget::close); zoomInAction_ = new QAction(tr("放大"), this); zoomInAction_->setShortcut(QKeySequence::ZoomIn); connect(zoomInAction_, &QAction::triggered, this, &MainWindow::zoomIn); zoomOutAction_ = new QAction(tr("缩小"), this); zoomOutAction_->setShortcut(QKeySequence::ZoomOut); connect(zoomOutAction_, &QAction::triggered, this, &MainWindow::zoomOut); fitAction_ = new QAction(tr("适配窗口"), this); fitAction_->setShortcut(Qt::CTRL | Qt::Key_F); connect(fitAction_, &QAction::triggered, this, &MainWindow::fitToWindow); resetAction_ = new QAction(tr("原始大小"), this); resetAction_->setShortcut(Qt::CTRL | Qt::Key_0); connect(resetAction_, &QAction::triggered, this, &MainWindow::resetView); rotateLeftAction_ = new QAction(tr("左旋 90°"), this); connect(rotateLeftAction_, &QAction::triggered, this, &MainWindow::rotateLeft); rotateRightAction_ = new QAction(tr("右旋 90°"), this); connect(rotateRightAction_, &QAction::triggered, this, &MainWindow::rotateRight); flipHorizontalAction_ = new QAction(tr("水平翻转"), this); connect(flipHorizontalAction_, &QAction::triggered, this, &MainWindow::flipHorizontal); flipVerticalAction_ = new QAction(tr("垂直翻转"), this); connect(flipVerticalAction_, &QAction::triggered, this, &MainWindow::flipVertical); grayscaleAction_ = new QAction(tr("灰度化"), this); connect(grayscaleAction_, &QAction::triggered, this, &MainWindow::grayscale); invertAction_ = new QAction(tr("反色"), this); connect(invertAction_, &QAction::triggered, this, &MainWindow::invertColors); } void MainWindow::createMenus() { QMenu *fileMenu = menuBar()->addMenu(tr("文件(&F)")); fileMenu->addAction(openAction_); fileMenu->addAction(saveAction_); fileMenu->addAction(saveAsAction_); fileMenu->addSeparator(); fileMenu->addAction(exitAction_); QMenu *viewMenu = menuBar()->addMenu(tr("查看(&V)")); viewMenu->addAction(zoomInAction_); viewMenu->addAction(zoomOutAction_); viewMenu->addAction(fitAction_); viewMenu->addAction(resetAction_); QMenu *processMenu = menuBar()->addMenu(tr("处理(&P)")); processMenu->addAction(rotateLeftAction_); processMenu->addAction(rotateRightAction_); processMenu->addAction(flipHorizontalAction_); processMenu->addAction(flipVerticalAction_); processMenu->addSeparator(); processMenu->addAction(grayscaleAction_); processMenu->addAction(invertAction_); } void MainWindow::createToolbar() { QToolBar *toolbar = addToolBar(tr("主工具栏")); toolbar->setMovable(false); toolbar->addAction(openAction_); toolbar->addAction(saveAction_); toolbar->addSeparator(); toolbar->addAction(zoomOutAction_); zoomSlider_ = new QSlider(Qt::Horizontal, this); zoomSlider_->setRange(10, 400); zoomSlider_->setValue(100); zoomSlider_->setFixedWidth(180); zoomSlider_->setToolTip(tr("缩放比例")); connect(zoomSlider_, &QSlider::valueChanged, this, &MainWindow::updateZoomFromSlider); toolbar->addWidget(zoomSlider_); toolbar->addAction(zoomInAction_); toolbar->addAction(fitAction_); toolbar->addSeparator(); toolbar->addAction(rotateLeftAction_); toolbar->addAction(rotateRightAction_); toolbar->addAction(grayscaleAction_); toolbar->addAction(invertAction_); } void MainWindow::createCentralWidget() { imageLabel_ = new ImageLabel(this); connect(imageLabel_, &ImageLabel::doubleClicked, this, &MainWindow::fitToWindow); scrollArea_ = new QScrollArea(this); scrollArea_->setBackgroundRole(QPalette::Dark); scrollArea_->setWidget(imageLabel_); scrollArea_->setWidgetResizable(true); setCentralWidget(scrollArea_); } void MainWindow::openImage() { const QString fileName = QFileDialog::getOpenFileName( this, tr("打开图片"), QString(), tr("图片文件 (*.png *.jpg *.jpeg *.bmp *.gif *.webp);;所有文件 (*.*)")); if (fileName.isEmpty()) { return; } QImageReader reader(fileName); reader.setAutoTransform(true); const QImage image = reader.read(); if (image.isNull()) { QMessageBox::warning(this, tr("打开失败"), tr("无法读取图片:%1").arg(reader.errorString())); return; } setImage(image, fileName); } void MainWindow::saveImage() { if (!ensureImageLoaded(tr("保存"))) { return; } if (currentPath_.isEmpty()) { saveImageAs(); return; } writeImageToPath(currentPath_); } void MainWindow::saveImageAs() { if (!ensureImageLoaded(tr("另存为"))) { return; } const QString fileName = QFileDialog::getSaveFileName( this, tr("另存为"), currentPath_.isEmpty() ? QStringLiteral("chengnan.png") : currentPath_, tr("PNG 图片 (*.png);;JPEG 图片 (*.jpg *.jpeg);;BMP 图片 (*.bmp);;所有文件 (*.*)")); if (!fileName.isEmpty()) { writeImageToPath(fileName); } } void MainWindow::zoomIn() { setZoomFactor(zoomFactor_ * 1.25); } void MainWindow::zoomOut() { setZoomFactor(zoomFactor_ / 1.25); } void MainWindow::fitToWindow() { if (!ensureImageLoaded(tr("适配窗口"))) { return; } const QSize viewportSize = scrollArea_->viewport()->size(); const QSize imageSize = currentImage_.size(); if (imageSize.isEmpty()) { return; } const double xRatio = static_cast(viewportSize.width()) / imageSize.width(); const double yRatio = static_cast(viewportSize.height()) / imageSize.height(); setZoomFactor(std::min(xRatio, yRatio)); } void MainWindow::resetView() { setZoomFactor(1.0); } void MainWindow::rotateLeft() { if (!ensureImageLoaded(tr("旋转"))) { return; } QTransform transform; transform.rotate(-90); applyTransformedImage(currentImage_.transformed(transform, Qt::SmoothTransformation)); } void MainWindow::rotateRight() { if (!ensureImageLoaded(tr("旋转"))) { return; } QTransform transform; transform.rotate(90); applyTransformedImage(currentImage_.transformed(transform, Qt::SmoothTransformation)); } void MainWindow::flipHorizontal() { if (!ensureImageLoaded(tr("翻转"))) { return; } applyTransformedImage(currentImage_.mirrored(true, false)); } void MainWindow::flipVertical() { if (!ensureImageLoaded(tr("翻转"))) { return; } applyTransformedImage(currentImage_.mirrored(false, true)); } void MainWindow::grayscale() { if (!ensureImageLoaded(tr("灰度化"))) { return; } applyTransformedImage(currentImage_.convertToFormat(QImage::Format_Grayscale8)); } void MainWindow::invertColors() { if (!ensureImageLoaded(tr("反色"))) { return; } QImage image = currentImage_.convertToFormat(QImage::Format_ARGB32); image.invertPixels(QImage::InvertRgb); applyTransformedImage(image); } void MainWindow::updateZoomFromSlider(int value) { const double factor = static_cast(value) / 100.0; if (std::abs(factor - zoomFactor_) > 0.001) { zoomFactor_ = factor; updateImageView(); } } void MainWindow::setImage(const QImage &image, const QString &path) { currentImage_ = image.convertToFormat(QImage::Format_ARGB32); currentPath_ = path; setZoomFactor(1.0); updateActions(); const QFileInfo info(path); setWindowTitle(path.isEmpty() ? tr("chengnan 图片查看器") : tr("%1 - chengnan 图片查看器").arg(info.fileName())); statusBar()->showMessage(tr("已加载:%1 × %2").arg(currentImage_.width()).arg(currentImage_.height()), 5000); } void MainWindow::updateImageView() { if (currentImage_.isNull()) { imageLabel_->setPixmap(QPixmap()); imageLabel_->setText(tr("打开一张图片开始查看")); return; } const QSize scaledSize = currentImage_.size() * zoomFactor_; const QPixmap pixmap = QPixmap::fromImage(currentImage_).scaled( scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); imageLabel_->setText(QString()); imageLabel_->setPixmap(pixmap); imageLabel_->resize(pixmap.size()); if (zoomSlider_->value() != static_cast(zoomFactor_ * 100.0)) { zoomSlider_->blockSignals(true); zoomSlider_->setValue(static_cast(zoomFactor_ * 100.0)); zoomSlider_->blockSignals(false); } statusBar()->showMessage(tr("缩放:%1%").arg(static_cast(zoomFactor_ * 100.0))); } void MainWindow::setZoomFactor(double factor) { zoomFactor_ = std::clamp(factor, 0.10, 4.00); updateImageView(); } void MainWindow::applyTransformedImage(const QImage &image) { currentImage_ = image.convertToFormat(QImage::Format_ARGB32); updateImageView(); updateActions(); statusBar()->showMessage(tr("处理完成,可保存结果"), 3000); } void MainWindow::updateActions() { const bool hasImage = !currentImage_.isNull(); saveAction_->setEnabled(hasImage); saveAsAction_->setEnabled(hasImage); zoomInAction_->setEnabled(hasImage); zoomOutAction_->setEnabled(hasImage); fitAction_->setEnabled(hasImage); resetAction_->setEnabled(hasImage); rotateLeftAction_->setEnabled(hasImage); rotateRightAction_->setEnabled(hasImage); flipHorizontalAction_->setEnabled(hasImage); flipVerticalAction_->setEnabled(hasImage); grayscaleAction_->setEnabled(hasImage); invertAction_->setEnabled(hasImage); zoomSlider_->setEnabled(hasImage); } bool MainWindow::ensureImageLoaded(const QString &actionName) const { if (!currentImage_.isNull()) { return true; } QMessageBox::information(nullptr, actionName, tr("请先打开一张图片。")); return false; } bool MainWindow::writeImageToPath(const QString &path) { QImageWriter writer(path); if (!writer.write(currentImage_)) { QMessageBox::warning(this, tr("保存失败"), tr("无法保存图片:%1").arg(writer.errorString())); return false; } currentPath_ = path; setWindowTitle(tr("%1 - chengnan 图片查看器").arg(QFileInfo(path).fileName())); statusBar()->showMessage(tr("已保存:%1").arg(path), 5000); return true; }