当前位置: 首页 > news >正文

【QT常用技术讲解】可拖拽文件的Widget--QListWidget

前言

        有些与文件、图片相关桌面工具,为了提高交互性,允许把桌面的文件、图片直接拖拽到应用工具框体控件中,并且框体控件中也可以拖拽里面的文件、图片。本篇分享的是通过QListWidget实现处理来自外部以及自身内部的文件拖放功能。

效果图

源代码已上传到绑定资源中。

功能讲解

自定义QListWidget控件

代码中有详细的注释,以下先提供自定义控件的代码,后续再按照功能点进行说明。

//imagelistwidget.h
#ifndef IMAGELISTWIDGET_H
#define IMAGELISTWIDGET_H#include <QListWidget>
#include <QString>
#include <QSize>
#include <QDebug>
class ImageListWidget : public QListWidget
{Q_OBJECTpublic:explicit ImageListWidget(QWidget *parent = nullptr);void addImage(const QString &filePath);  // 添加单个图片void addImages(const QStringList &filePaths);  // 添加多个图片void clearAllImages();  // 清空所有图片int imageCount() const;  // 获取图片数量QStringList getImagePaths() const;  // 获取所有图片路径signals:void imageSelected(const QString &filePath);  // 图片被选中信号void imageCountChanged(int count);  // 图片数量变化信号void imageOrderChanged();  // 图片顺序变化信号void requestContextMenu(const QPoint &pos);  // 请求上下文菜单信号void imageDoubleClicked(const QString &filePath);  // 图片双击信号public slots:void verifyDragOrder();void deleteSelectedImage();// 删除选中图片protected:void dragEnterEvent(QDragEnterEvent *event) override;  // 拖拽进入事件void dragMoveEvent(QDragMoveEvent *event) override;  // 拖拽移动事件void dropEvent(QDropEvent *event) override;  // 拖放事件void startDrag(Qt::DropActions supportedActions) override;  // 开始拖拽void mousePressEvent(QMouseEvent *event) override;  // 鼠标按下事件void mouseDoubleClickEvent(QMouseEvent *event) override;  // 鼠标双击事件private:void initializeUI();// 初始化UIQSize m_gridSize;  // 网格大小QSize m_iconSize;  // 图标大小
};#endif // IMAGELISTWIDGET_H
//imagelistwidget.cpp
#include "imagelistwidget.h"
#include <QListWidgetItem>
#include <QPixmap>
#include <QFileInfo>
#include <QMessageBox>
#include <QDrag>
#include <QMimeData>
#include <QApplication>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QPainter>
#include <QMouseEvent>ImageListWidget::ImageListWidget(QWidget *parent): QListWidget(parent), m_gridSize(150, 150)//初始化网格大小为150x150像素, m_iconSize(120, 120)//初始化图标大小为120x120像素
{initializeUI();
}
// UI初始化函数
void ImageListWidget::initializeUI()
{setViewMode(QListWidget::IconMode);// 设置视图模式为图标模式(显示图标而非列表)setGridSize(m_gridSize);// 设置网格大小setIconSize(m_iconSize);// 设置图标大小setResizeMode(QListWidget::Adjust); // 设置调整模式为自动调整setMovement(QListWidget::Snap);// 设置移动模式为对齐网格setSelectionMode(QListWidget::SingleSelection);// 设置选择模式为单选setDragDropMode(QListWidget::InternalMove);// 设置拖放模式为内部移动setDefaultDropAction(Qt::MoveAction);// 设置默认拖放动作为移动setSpacing(0);// 设置项目间距为0setDropIndicatorShown(true);// 显示拖放指示器setUniformItemSizes(true);// 设置统一的项目大小setWordWrap(true);// 启用文本自动换行
}// 鼠标双击事件处理函数--双击弹出预览图片
void ImageListWidget::mouseDoubleClickEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {//检测双击事件QListWidgetItem *item = itemAt(event->pos());// 获取点击位置的项目if (item) {QString filePath = item->data(Qt::UserRole).toString();//获取文件路径qDebug() << "双击预览图片:" << filePath;emit imageDoubleClicked(filePath);// 发送信号--由主框架统一处理event->accept();// 吸收事件return;}}//其他的事件调用父类的默认处理QListWidget::mouseDoubleClickEvent(event);
}//鼠标右键单击--菜单功能
void ImageListWidget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::RightButton) {//检测右键单击事件emit requestContextMenu(event->pos());// 发送信号--由主框架统一处理event->accept();// 吸收事件return;}//其他的事件调用父类的默认处理QListWidget::mousePressEvent(event);
}// 删除选中图片的函数
void ImageListWidget::deleteSelectedImage()
{QListWidgetItem *currentItem = this->currentItem();// 获取当前选中的项目if (!currentItem) {return;}int row = this->row(currentItem);// 获取项目所在行delete takeItem(row); // 删除项目emit imageCountChanged(count());// 发射图片数量变化信号--如果主框架有数量统计的需求,可以同步更新emit imageOrderChanged();// 发射图片顺序变化信号//qDebug() << "删除图片,当前剩余数量:" << count();
}// 添加单个图片
void ImageListWidget::addImage(const QString &filePath)
{QPixmap pixmap(filePath);// 从文件路径加载图片if (pixmap.isNull()) return;// 缩放图片到图标大小,保持宽高比QPixmap scaledPixmap = pixmap.scaled(m_iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);// 创建列表项目,显示图标和文件名QListWidgetItem *item = new QListWidgetItem(QIcon(scaledPixmap), QFileInfo(filePath).fileName());item->setData(Qt::UserRole, filePath);// 将完整文件路径存储为用户数据item->setSizeHint(m_gridSize);// 设置项目大小提示// 设置项目标志Qt::ItemIsEnabled:启用;Qt::ItemIsSelectable:可选;Qt::ItemIsDragEnabled:可拖拽item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);addItem(item);// 添加项目到列表emit imageCountChanged(count());// 发射图片数量变化信号
}// 添加多个图片的函数
void ImageListWidget::addImages(const QStringList &filePaths)
{for (const QString &filePath : filePaths) {addImage(filePath);}
}// 清空所有图片的函数
void ImageListWidget::clearAllImages()
{clear();emit imageCountChanged(0);
}// 获取图片数量的函数
int ImageListWidget::imageCount() const
{return count();
}// 获取所有图片路径的函数
QStringList ImageListWidget::getImagePaths() const
{QStringList paths;for (int i = 0; i < count(); ++i) {QListWidgetItem *item = this->item(i);paths.append(item->data(Qt::UserRole).toString());// 从项目数据中获取路径并添加到列表}return paths;
}// 拖拽进入事件处理函数
void ImageListWidget::dragEnterEvent(QDragEnterEvent *event)
{// 检查拖拽数据是否包含URL或来自自身if (event->mimeData()->hasUrls() || event->source() == this) {event->acceptProposedAction();// 接受动作} else {event->ignore();// 忽略事件}
}
// 拖拽移动事件处理函数
void ImageListWidget::dragMoveEvent(QDragMoveEvent *event)
{// 检查拖拽数据是否包含URL或来自自身if (event->mimeData()->hasUrls() || event->source() == this) {event->acceptProposedAction();// 接受动作} else {event->ignore();// 忽略事件}
}// 拖放事件处理函数
void ImageListWidget::dropEvent(QDropEvent *event)
{const QMimeData *mimeData = event->mimeData();// 获取拖拽的MIME数据// 处理来自自身的拖拽(内部重排序)if (event->source() == this) {QListWidgetItem *draggedItem = currentItem();// 获取被拖拽的项目if (!draggedItem) return;QPoint pos = event->pos();// 获取拖放位置QListWidgetItem *targetItem = itemAt(pos);// 获取目标位置的项目//项目拖拽了,但不代表item中的顺序会同步变化,以下手动方式排列// 如果目标项目存在且不是被拖拽项目本身if (targetItem && targetItem != draggedItem) {// 获取拖动前和拖动后的索引值,比如从2->6int draggedIndex = row(draggedItem);int targetIndex = row(targetItem);// 提取出被拖拽项目,把2提取出(比如一个list链表,2被提取之后,2之后的就会自动往前排)QListWidgetItem *item = takeItem(draggedIndex);//把提取出的被拖拽项目插入到目标位置,2项的数据就插入到6位置了insertItem(targetIndex, item);setCurrentItem(item);//设置当前选中项目,设置6位置为选中项,保持原来选中的项内容不变// 发射图片顺序变化信号emit imageOrderChanged();}event->acceptProposedAction();} else if (mimeData->hasUrls()) {// 处理来自外部的文件拖放QStringList filePaths;QList<QUrl> urlList = mimeData->urls();// 获取所有URLfor (const QUrl &url : urlList) {QString filePath = url.toLocalFile();// 转换为本地文件路径QFileInfo fileInfo(filePath);QString extension = fileInfo.suffix().toLower();// 获取文件扩展名并转换为小写// 检查是否为支持的图片格式if (extension == "png" || extension == "jpg" ||extension == "jpeg" || extension == "bmp" || extension == "gif") {filePaths.append(filePath);// 添加到文件路径列表}}if (!filePaths.isEmpty()) {addImages(filePaths);//添加图片}event->acceptProposedAction();} else {event->ignore();// 忽略不支持的拖放}
}// 开始拖拽操作函数
void ImageListWidget::startDrag(Qt::DropActions supportedActions)
{QListWidgetItem *item = currentItem();// 获取当前选中的项目if (!item) return;QDrag *drag = new QDrag(this);// 创建拖拽对象QMimeData *mimeData = new QMimeData(); // 创建MIME数据对象// 从项目数据中获取文件路径QString filePath = item->data(Qt::UserRole).toString();QList<QUrl> urls;urls.append(QUrl::fromLocalFile(filePath));// 添加文件URL到URL列表mimeData->setUrls(urls);// 设置MIME数据的URL// 获取项目图标并转换为指定大小的像素图QPixmap pixmap = item->icon().pixmap(m_iconSize);// 创建稍大的透明拖拽图标QPixmap dragPixmap(m_iconSize + QSize(20, 20));dragPixmap.fill(Qt::transparent);// 在拖拽图标上绘制半透明的原图标QPainter painter(&dragPixmap);painter.setRenderHint(QPainter::Antialiasing);// 设置抗锯齿painter.setOpacity(0.7);// 设置透明度为70%painter.drawPixmap(10, 10, pixmap); // 绘制图标(偏移10像素)painter.end();drag->setMimeData(mimeData);// 设置拖拽对象的MIME数据drag->setPixmap(dragPixmap);// 设置拖拽时显示的图标// 设置拖拽热点(中心点)drag->setHotSpot(QPoint(dragPixmap.width()/2, dragPixmap.height()/2));// 执行拖拽操作drag->exec(supportedActions, Qt::MoveAction);
}
// 验证拖拽顺序的函数(用于调试)
void ImageListWidget::verifyDragOrder()
{qDebug() << "验证拖拽顺序 - 项目数量:" << count();for (int i = 0; i < count(); ++i) {QListWidgetItem *item = this->item(i);qDebug() << "位置" << i << ":" << item->text()<< "路径:" << item->data(Qt::UserRole).toString();}
}

1、UI初始化

涉及以下4到要点:

  • 内容显示模式:视图模式(网格图标模式)
  • 显示内容的样式:网格大小、图标大小
  • 选中方式:单选
  • 拖放方式:可拖放移动

2、添加图片

对应的函数名称为:addImage()和addImages()。涉及到以下3要素:

  • 显示的内容:缩小的图片+文件名称
  • 存储数据:存储文件路径用于其他操作
  • 设置拖拽属性:可拖拽

3、清空所有图片

直接调用QListWidget的clear()即可。

4、获取所有图片路径

获取添加图片时存储的数据(文件路径),方便做图片处理操作。

5、拖拽事件重载

包含3个函数

  • 开始拖拽:startDrag
  • 拖拽进入事件:dragEnterEvent
  • 拖拽移动事件:dragMoveEvent
  • 拖拽放下事件:dropEvent

开始拖拽startDrag()是拖拽初始化的操作,涉及以下5个要点:

  • 创建拖拽对象
  • 创建MIME数据对象,存储数据(文件路径)
  • 设置显示的内容格式
  • 设置拖拽移动效果
  • 执行拖拽操作

dragEnterEvent与dragMoveEvent的代码相同,但两者是有区别的:

特性

dragEnterEvent

dragMoveEvent

​触发时机​

拖拽操作首次进入控件边界时触发​​一次​

在控件区域内移动时​​连续触发​

​主要用途​

决定是否允许进入控件区域

跟踪拖拽位置,为视觉反馈和drop做准备

​执行频率​

单次(进入时)

高频(移动时)

重点的交互是在dropEvent()拖拽放下时,需要调整已有图片项的排序。特别是内部拖放之后默认是不更新排序的,需要编写调整代码:

        QListWidgetItem *draggedItem = currentItem();// 获取被拖拽的项目if (!draggedItem) return;QPoint pos = event->pos();// 获取拖放位置QListWidgetItem *targetItem = itemAt(pos);// 获取目标位置的项目//项目拖拽了,但不代表item中的顺序会同步变化,以下手动方式排列// 如果目标项目存在且不是被拖拽项目本身if (targetItem && targetItem != draggedItem) {// 获取拖动前和拖动后的索引值,比如从2->6int draggedIndex = row(draggedItem);int targetIndex = row(targetItem);// 提取出被拖拽项目,把2提取出(比如一个list链表,2被提取之后,2之后的就会自动往前排)QListWidgetItem *item = takeItem(draggedIndex);//把提取出的被拖拽项目插入到目标位置,2项的数据就插入到6位置了insertItem(targetIndex, item);setCurrentItem(item);//设置当前选中项目,设置6位置为选中项,保持原来选中的项内容不变// 发射图片顺序变化信号emit imageOrderChanged();}
  • takeItem(draggedIndex)从指定位置取出节点项
  • insertItem(targetIndex, item)在指定位置插入取出的节点项

以上这两项操作可以用链表的特性来理解,比如:节点2插入到节点6,即是把节点2从链表中取出,此时节点3移动到节点2位置,以此类推节点6移动到节点5的位置,然后节点2再插入到节点6的位置,即节点5的后面,此时的效果就算节点2移动到了节点6的效果。

6、鼠标单击事件重载

        涉及右键菜单功能,监测右键单击事件,右键会被主框架劫持,这里是发生信号到主框架,由主框架处理右键事件。

if (event->button() == Qt::RightButton) {//测右键单击事件emit requestContextMenu(event->pos());// 发送信号--由主框架统一处理event->accept();// 吸收事件return;}

7、鼠标双击事件重载

        涉及左键双击显示图片预览功能,这里是发生信号到主框架,由主框架处理双击事件

if (event->button() == Qt::LeftButton) {//检测双击事件QListWidgetItem *item = itemAt(event->pos());// 获取点击位置的项目if (item) {QString filePath = item->data(Qt::UserRole).toString();//获取文件路径qDebug() << "双击预览图片:" << filePath;emit imageDoubleClicked(filePath);// 发送信号--由主框架统一处理event->accept();// 吸收事件return;}}

主框架功能

mainwindow引用自定义控件

在UI界面添加一个QListWidget控件,右键点击”升级为“,设置为ImageListWidget类,具体的操作可以参考我之前分享的博文:

【QT常用技术讲解】自定义支持多选项的下拉框https://blog.csdn.net/liangyuna8787/article/details/152454461

1、添加图片

1.1、点击按钮方式添加

在on_pushButton_clicked按钮事件中调用addImages()

1.2、拖拽方式从外部添加

重载拖拽函数:dragEnterEvent、dragMoveEvent、dropEvent,只监测外部拖拽进来的事件。

if (mimeData->hasUrls())//只判断是不是从外部拖拽进来的事件

并且在拖放时,调用addImages增加图片到图片项中。

// 遍历所有URL,转换为本地文件路径for (const QUrl &url : urlList) {QString filePath = url.toLocalFile();if (isImageFile(filePath)) {filePaths.append(filePath);}}if (!filePaths.isEmpty()) {ui->imageListWidget->addImages(filePaths);// 添加图片}

2、删除图片

2.1、点击按钮清空图片

调用clearAllImages进行清空。

2.2、点击键盘Delete键删除选中图片

设置快捷键:QKeySequence::Delete

m_deleteImageAction = new QAction("删除图片", this);// 创建删除动作connect(m_deleteImageAction, &QAction::triggered, this, &MainWindow::deleteSelectedImage);m_deleteImageAction->setShortcut(QKeySequence::Delete);// 设置快捷键为Delete键this->addAction(m_deleteImageAction);//将动作添加到窗口,使快捷键全局生效
2.3、右键菜单删除选中的图片

上面已经可以实现删除功能了,这里只是为了演示右键菜单功能的设置

    m_imageListContextMenu = new QMenu(this);// 创建右键菜单m_deleteImageAction = new QAction("删除图片", this);// 创建删除动作m_deleteImageAction->setStatusTip("删除选中的图片");// 设置状态栏提示m_imageListContextMenu->addAction(m_deleteImageAction);// 将动作添加到菜单connect(m_deleteImageAction, &QAction::triggered, this, &MainWindow::deleteSelectedImage);

以上2给的删除动作都是调用槽函数deleteSelectedImage实现的。

3、上移/下移图片

核心功能与ImageListWidget中的拖放重载函数dropEvent一样,都是先takeItem(currentRow)提取出需要移动位置的图片项,然后insertItem(currentRow - 1, currentItem)/insertItem(currentRow+1, currentItem)来实现移动。

4、生成PDF

在按钮事件on_generatePdfButton_clicked中实现,比较简单,通过QT的QPrinter模块生成PDF文件。

5、左键双击图片预览图片

接收ImageListWidget发过来的信号后,通过槽函数previewImage响应,引用了一个自定义预览对话框ImagePreviewDialog,自定义对话框代码如下:

//imagepreviewdialog.h
#ifndef IMAGEPREVIEWDIALOG_H
#define IMAGEPREVIEWDIALOG_H#include <QDialog>
#include <QLabel>
#include <QScrollArea>
#include <QAction>
#include <QToolBar>
#include <QString>
#include <QPixmap>
#include <QTimer>
class ImagePreviewDialog : public QDialog
{Q_OBJECTpublic:explicit ImagePreviewDialog(const QString &imagePath, QWidget *parent = nullptr);~ImagePreviewDialog() = default;protected:void keyPressEvent(QKeyEvent *event) override;// 重写键盘事件处理函数void wheelEvent(QWheelEvent *event) override;// 重写鼠标滚轮事件处理函数void showEvent(QShowEvent *event) override;// 重写显示事件处理函数(窗口显示时触发)private:void setupUI();void setupToolbar();void loadImage(const QString &imagePath);void updateImageDisplay();void scaleImage(double factor);void adjustScrollBar(QScrollBar *scrollBar, double factor);void calculateOptimalSize();// 界面控件QLabel *m_imageLabel;        // 显示图片的标签QScrollArea *m_scrollArea;   // 滚动区域(用于显示大图片)QToolBar *m_toolBar;         // 工具栏// 图片数据QPixmap m_originalPixmap;    // 原始图片数据double m_scaleFactor;        // 当前缩放比例int m_rotationAngle;         // 旋转角度(0, 90, 180, 270度)QString m_currentImagePath;  // 当前图片路径int m_currentImageIndex;     // 当前图片索引(预留,用于多图片浏览)// 工具栏动作QAction *m_zoomInAction;        // 放大动作QAction *m_zoomOutAction;       // 缩小动作QAction *m_fitToWindowAction;   // 适应窗口动作QAction *m_originalSizeAction;  // 原始大小动作QAction *m_rotateLeftAction;    // 左旋转动作QAction *m_rotateRightAction;   // 右旋转动作private slots:void zoomIn();        // 放大图片void zoomOut();       // 缩小图片void fitToWindow();   // 适应窗口显示void originalSize();  // 原始大小显示void rotateLeft();    // 向左旋转90度void rotateRight();   // 向右旋转90度
};#endif // IMAGEPREVIEWDIALOG_H
//imagepreviewdialog.cpp
#include "imagepreviewdialog.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QToolBar>
#include <QFileInfo>
#include <QApplication>
#include <QScreen>
#include <QMessageBox>
#include <QKeyEvent>
#include <QScrollBar>
#include <QClipboard>
#include <QFileDialog>
#include <QGuiApplication>
#include <QShowEvent>
#include <QDesktopWidget>ImagePreviewDialog::ImagePreviewDialog(const QString &imagePath, QWidget *parent): QDialog(parent), m_scaleFactor(1.0)// 初始化缩放因子为1.0(原始大小), m_rotationAngle(0) // 初始化旋转角度为0度, m_currentImageIndex(-1)// 初始化当前图片索引为-1(单图模式)
{setWindowTitle("图片预览 - " + QFileInfo(imagePath).fileName());setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint);setMinimumSize(600, 500);  // 增大最小尺寸setupUI();setupToolbar();// 设置工具栏loadImage(imagePath);// 加载图片
}// 设置UI布局和组件
void ImagePreviewDialog::setupUI()
{QVBoxLayout *mainLayout = new QVBoxLayout(this);mainLayout->setContentsMargins(2, 2, 2, 2);  // 减少边距// 创建工具栏m_toolBar = new QToolBar(this);m_toolBar->setMovable(false); // 禁止拖动工具栏// 创建动作m_zoomInAction = new QAction("放大", this);m_zoomOutAction = new QAction("缩小", this);m_fitToWindowAction = new QAction("适应窗口", this);m_originalSizeAction = new QAction("原始大小", this);m_rotateLeftAction = new QAction("左旋转", this);m_rotateRightAction = new QAction("右旋转", this);// 设置快捷键m_zoomInAction->setShortcut(QKeySequence::ZoomIn);m_zoomOutAction->setShortcut(QKeySequence::ZoomOut);m_fitToWindowAction->setShortcut(QKeySequence(Qt::Key_F));m_originalSizeAction->setShortcut(QKeySequence(Qt::Key_R));// 添加动作到工具栏m_toolBar->addAction(m_zoomInAction);m_toolBar->addAction(m_zoomOutAction);m_toolBar->addSeparator();m_toolBar->addAction(m_fitToWindowAction);m_toolBar->addAction(m_originalSizeAction);m_toolBar->addSeparator();m_toolBar->addAction(m_rotateLeftAction);m_toolBar->addAction(m_rotateRightAction);// 创建滚动区域和图片标签m_scrollArea = new QScrollArea(this);m_imageLabel = new QLabel(this);m_imageLabel->setBackgroundRole(QPalette::Base);m_imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);m_imageLabel->setScaledContents(false);m_imageLabel->setAlignment(Qt::AlignCenter);m_scrollArea->setBackgroundRole(QPalette::Dark);m_scrollArea->setWidget(m_imageLabel);m_scrollArea->setVisible(true);m_scrollArea->setWidgetResizable(true);  // 允许调整大小// 添加到布局mainLayout->addWidget(m_toolBar);mainLayout->addWidget(m_scrollArea, 1);  // 设置拉伸因子为1,让图片区域占据更多空间// 连接信号槽connect(m_zoomInAction, &QAction::triggered, this, &ImagePreviewDialog::zoomIn);connect(m_zoomOutAction, &QAction::triggered, this, &ImagePreviewDialog::zoomOut);connect(m_fitToWindowAction, &QAction::triggered, this, &ImagePreviewDialog::fitToWindow);connect(m_originalSizeAction, &QAction::triggered, this, &ImagePreviewDialog::originalSize);connect(m_rotateLeftAction, &QAction::triggered, this, &ImagePreviewDialog::rotateLeft);connect(m_rotateRightAction, &QAction::triggered, this, &ImagePreviewDialog::rotateRight);
}void ImagePreviewDialog::setupToolbar()
{// 设置工具栏样式表m_toolBar->setStyleSheet("QToolBar {"  // 工具栏样式"    background-color: #f5f5f5;"  // 背景色"    border-bottom: 1px solid #dddddd;"  // 底部边框"    spacing: 5px;"  // 间距"    padding: 3px;"  // 内边距"}""QToolButton {"  // 工具按钮样式"    background-color: transparent;"  // 透明背景"    border: 1px solid transparent;"  // 透明边框"    border-radius: 3px;"  // 圆角"    padding: 5px 8px;"  // 内边距"    font-size: 12px;"  // 字体大小"}""QToolButton:hover {"  // 鼠标悬停样式"    background-color: #e0e0e0;"  // 背景色"    border: 1px solid #cccccc;"  // 边框"}""QToolButton:pressed {"  // 鼠标按下样式"    background-color: #d0d0d0;"  // 背景色"}");
}// 加载图片
void ImagePreviewDialog::loadImage(const QString &imagePath)
{m_originalPixmap = QPixmap(imagePath);// 从文件路径加载图片if (m_originalPixmap.isNull()) {QMessageBox::warning(this, "错误", "无法加载图片: " + imagePath);return;}m_currentImagePath = imagePath;m_rotationAngle = 0;// 显示图片updateImageDisplay();// 计算最佳显示尺寸calculateOptimalSize();
}// 计算最佳显示尺寸
void ImagePreviewDialog::calculateOptimalSize()
{if (m_originalPixmap.isNull()) return;// 获取屏幕尺寸QScreen *screen = QApplication::primaryScreen();QRect screenGeometry = screen->availableGeometry();// 计算图片原始尺寸QSize imageSize = m_originalPixmap.size();// 计算对话框的理想尺寸(屏幕的70-80%)int maxWidth = screenGeometry.width() * 0.8;int maxHeight = screenGeometry.height() * 0.8;// 如果图片比最大尺寸小,则按图片尺寸显示if (imageSize.width() <= maxWidth && imageSize.height() <= maxHeight) {// 图片较小,按原始大小显示,但加上一些边距resize(imageSize.width() + 50, imageSize.height() + 100);m_scaleFactor = 1.0;} else {// 图片较大,适应屏幕double widthRatio = (double)maxWidth / imageSize.width();double heightRatio = (double)maxHeight / imageSize.height();m_scaleFactor = qMin(widthRatio, heightRatio) * 0.9;  // 稍微缩小一点留出边距// 设置对话框大小QSize dialogSize = imageSize * m_scaleFactor;dialogSize += QSize(50, 100);  // 加上工具栏和边距的空间resize(dialogSize);}updateImageDisplay();
}// 更新图片显示
void ImagePreviewDialog::updateImageDisplay()
{if (m_originalPixmap.isNull()) return;// 应用旋转QTransform transform;transform.rotate(m_rotationAngle);QPixmap rotatedPixmap = m_originalPixmap.transformed(transform, Qt::SmoothTransformation);// 应用缩放if (qFuzzyCompare(m_scaleFactor, 1.0)) {m_imageLabel->setPixmap(rotatedPixmap);} else {QSize newSize = rotatedPixmap.size() * m_scaleFactor;QPixmap scaledPixmap = rotatedPixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);m_imageLabel->setPixmap(scaledPixmap);}m_imageLabel->adjustSize();// 调整标签大小以适应图片
}void ImagePreviewDialog::zoomIn()
{scaleImage(1.25);// 放大25%
}void ImagePreviewDialog::zoomOut()
{scaleImage(0.8);// 缩小到80%
}void ImagePreviewDialog::scaleImage(double factor)
{m_scaleFactor *= factor;// 限制缩放范围if (m_scaleFactor < 0.1) m_scaleFactor = 0.1;if (m_scaleFactor > 10.0) m_scaleFactor = 10.0;updateImageDisplay();// 调整滚动条以保持视图中心adjustScrollBar(m_scrollArea->horizontalScrollBar(), factor);adjustScrollBar(m_scrollArea->verticalScrollBar(), factor);
}void ImagePreviewDialog::adjustScrollBar(QScrollBar *scrollBar, double factor)
{scrollBar->setValue(int(factor * scrollBar->value() + ((factor - 1) * scrollBar->pageStep() / 2)));
}// 适应窗口大小
void ImagePreviewDialog::fitToWindow()
{if (m_originalPixmap.isNull()) return;QSize scrollAreaSize = m_scrollArea->viewport()->size();QSize imageSize = m_originalPixmap.size();// 如果旋转了90或270度,则交换宽高if (m_rotationAngle % 180 == 90) {imageSize.transpose();}// 计算适应窗口的比例double widthRatio = (double)scrollAreaSize.width() / imageSize.width();double heightRatio = (double)scrollAreaSize.height() / imageSize.height();m_scaleFactor = qMin(widthRatio, heightRatio) * 0.95;  // 留一点边距updateImageDisplay();
}
// 显示原始大小
void ImagePreviewDialog::originalSize()
{m_scaleFactor = 1.0;updateImageDisplay();
}
// 向左旋转
void ImagePreviewDialog::rotateLeft()
{m_rotationAngle -= 90;// 减少90度if (m_rotationAngle < 0) m_rotationAngle += 360;updateImageDisplay();// 旋转后重新适应窗口QTimer::singleShot(50, this, &ImagePreviewDialog::fitToWindow);
}void ImagePreviewDialog::rotateRight()
{m_rotationAngle += 90;if (m_rotationAngle >= 360) m_rotationAngle -= 360;updateImageDisplay();// 旋转后重新适应窗口QTimer::singleShot(50, this, &ImagePreviewDialog::fitToWindow);
}void ImagePreviewDialog::showEvent(QShowEvent *event)
{QDialog::showEvent(event);// 窗口显示后,确保图片正确适应QTimer::singleShot(100, this, [this]() {if (m_scaleFactor < 0.5) {  // 如果缩放比例太小,重新适应窗口fitToWindow();}});
}// 键盘事件处理
void ImagePreviewDialog::keyPressEvent(QKeyEvent *event)
{switch (event->key()) {case Qt::Key_Escape:  // ESC键:关闭窗口close();break;case Qt::Key_Plus:    // +键:放大case Qt::Key_Equal:   // =键:放大(与+键相同)zoomIn();break;case Qt::Key_Minus:   // -键:缩小zoomOut();break;case Qt::Key_0:       // 0键:原始大小originalSize();break;case Qt::Key_1:       // 1键:适应窗口fitToWindow();break;case Qt::Key_Left:    // 左箭头键:预留(可添加上一张图片功能)break;case Qt::Key_Right:   // 右箭头键:预留(可添加下一张图片功能)break;default:// 其他按键交给父类处理QDialog::keyPressEvent(event);}
}// 鼠标滚轮事件处理
void ImagePreviewDialog::wheelEvent(QWheelEvent *event)
{// 如果按住Ctrl键滚动滚轮,进行缩放操作if (event->modifiers() & Qt::ControlModifier) {if (event->angleDelta().y() > 0) {// 向上滚动:放大zoomIn();} else {// 向下滚动:缩小zoomOut();}event->accept();  // 标记事件已处理} else {// 其他情况交给父类处理QDialog::wheelEvent(event);}
}
http://www.dtcms.com/a/548575.html

相关文章:

  • 手机网站标准家政公司怎么注册
  • 上海网站建设500元wordpress在线支付表单
  • 厦门市小学生信息学竞赛(C++)初赛总复习(第二章 算法知识与数据结构 -第二节 数据结构 第三节 栈)
  • 【OD刷题笔记】- 5G网络建设
  • 基于毫米波雷达的汽车变道辅助系统(LCA)原理与实现
  • 赋能智慧货运:视频汇聚平台EasyCVR打造货运汽车安全互联网视频监控与管理方案
  • 数图科技赋能长春欧亚:构建零售 “智慧大脑”,让陈列调整、决策响应快人一步
  • MongoDB 正则表达式
  • 【超级详细】正点原子RK3588安装和编译SDK
  • AI 图像生成技术发展时间脉络:从 GAN 到多模态大模型的知名模型概略解析
  • “一网通办”查询响应优化:金仓索引如何支撑政务高效服务
  • 微信小程序(H5)上传文件到阿里云 OSS(使用 STS 临时凭证)
  • 建设局网站打不开是什么原因先做网站先备案
  • Bootstrap 按钮
  • Qt 外观之Qt样式表(QSS)
  • Adobe Lightroom安卓版(手机调色软件)绿色版
  • 服务器 Web 安全:Nginx 配置 X-Frame-Options 与 CSP 头,防御 XSS 与点击劫持
  • 嵌入式AI Arm_linux_第一个Demo_让IPU跑起来
  • Solon 项目升级 JDK 25
  • 手写自己的小型react
  • 3秒传输GB级文件:FastSend让P2P共享告别云存储依赖
  • 【Docker】P2 Docker 命令:从Nginx部署到镜像分享的全流程指南
  • VLAN协议简介
  • 递归专题1 - 递归基础与思维方法
  • 黄金分割与对数螺线
  • Vue 数据绑定深入浅出:从 v-bind 到 v-model 的实战指南
  • python - day10
  • MySQL 中的 行锁(Record Lock) 和 间隙锁(Gap Lock)
  • 【Docker】P1 Docker 基础入门指南
  • 【OD刷题笔记】- API集群负载统计