news 2026/3/23 17:48:57

一个QT开发的简易版图片查看器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一个QT开发的简易版图片查看器

基于QT开发的ImageViewer轻量级图片查看器

前言: 学习QT的时候,简单实现了一个好玩的图片查看器,这个是基于QT开发的,无边框、可拖动、支持全屏以及本地目录加载。方便学习一些事件的简单处理.

一、环境要求

  • Qt 5.15+
  • c++11
  • windows上的vs2022

二、项目的学习点

  1. Qt资源系统.qrc的动态访问

好像QT在版本5.13+的时候可以支持遍历访问.qrc资源文件中的一些文件

QDir dir("://res/res/");// 如果访问不了,可能需要://res/res/这样的双斜杠 for (const QString& fileName : dir.entryList({ "*.jpg", "*.png" }, QDir::Files)) { QString fullPath = dir.filePath(fileName); QPixmap img; if (img.load(fullPath)) { images.append(img); } }
  1. 事件驱动

在这个项目中,因为把窗口的标志设置成了无边框、工具窗口(任务栏不会显示图标),各种工具栏、菜单栏都没有了。所以对于窗口的一些拖动、点击、按键等的事件我们需要自己处理

绘制事件(直接绘制背景图片)

void ImageViewer::paintEvent(QPaintEvent*) { // 绘制背景图片(左上角对齐) QPainter painter(this); painter.drawPixmap(0, 0, images[currentImageIndex]); // 绘制右上角得关闭区域 painter.setBrush(QColor(0, 0, 0, 10)); // 半透红 painter.setPen(Qt::NoPen); painter.drawRect(closeRect); }

窗口变化的事件(需要更新某些区域的位置)

void ImageViewer::resizeEvent(QResizeEvent*) { // 更新关闭按钮位置(始终右上角) closeRect = QRect(width() - 30, 0, 30, 30); }

键盘事件(没有设置什么按钮菜单之类的,所以需要键盘处理一些图片目录的选择、图片显示的切换、全屏、关闭等)

void ImageViewer::keyPressEvent(QKeyEvent* event) { switch (event->key()) { // 按键ESV退出程序 case Qt::Key_Escape: qApp->quit(); break; // 按键F10全屏切换 case Qt::Key_F10: m_bIsFullScreen = !m_bIsFullScreen; adjustAndCenter(); if (m_bIsFullScreen) { showFullScreen(); } else { showNormal(); } break; // 空格切换图片 case Qt::Key_Space: currentImageIndex = (currentImageIndex + 1) % images.size(); loadCurrentImage(); break; // tab键可以选择加载新的图片目录 case Qt::Key_Tab: { QString dir = QFileDialog::getExistingDirectory( this, tr("选择图片目录"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); if (!dir.isEmpty()) { loadImagesFromDirectory(dir); } } break; default: QWidget::keyPressEvent(event); // 其他按键交给基类 break; } }

鼠标事件(目前重写的是鼠标的按下事件,移动事件,需要记录鼠标的位置以便于移动窗口)

void ImageViewer::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { // 新的左上角位置: 当前鼠标的全局位置-偏移量 // 原理: 鼠标按下点的相对于屏幕的位置=窗口左上角坐标+鼠标按下点相对于窗口的位置 // 那么要保持鼠标按下点相对于窗口的位置保持不变,那么新的窗口左上角坐标=新鼠标移动点相对于屏幕坐标-鼠标按下点相对于窗口的位置 // 平滑拖动:全局位置 - 偏移 = 新窗 move(event->globalPos() - dragOffset); } } void ImageViewer::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { // 记录当前鼠标相对于当前窗口位置(不是相对于屏幕) dragOffset = event->pos(); currentImageIndex = (currentImageIndex + 1) % images.size(); // 循环切换图片 loadCurrentImage(); } if (closeRect.contains(event->pos())) { qApp->quit(); return; } }
  1. 全屏与窗口位置大小等状态管理

我需要根据屏幕的大小去调整我的程序窗口的大小和位置

void ImageViewer::adjustAndCenter() { // 获取主屏可用屏幕区域(这个函数排除任务栏区域) QScreen* screen = QApplication::primaryScreen(); QRect avail = screen->availableGeometry(); int maxWidth = 100; int maxHeight = 100; if (m_bIsFullScreen) { maxWidth = static_cast<int>(avail.width()); maxHeight = static_cast<int>(avail.height()); } else { maxWidth = static_cast<int>(avail.width() * 0.5); maxHeight = static_cast<int>(avail.height() * 0.5); } // 如果图片太大,就缩放到不超过 90% 屏幕可用区域,保持比例 // if (images[currentImageIndex].width() > maxWidth || images[currentImageIndex].height() > maxHeight) { // images[currentImageIndex] = images[currentImageIndex].scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); // } // 这里强制进行缩放了,不会保持宽高比和平滑缩放 for (int i = 0; i < images.size(); ++i) { images[i] = images[i].scaled(maxWidth, maxHeight); } if(images.size()) // 设置窗口大小为图片大小 resize(images[0].size()); // 居中显示(主屏中心位置-当前窗口中心位置其实是当前窗口得宽高)=>当前窗口左上角坐标位置 move(avail.center() - rect().center()); // 因为设置了resize,所以关闭区域需要重新更新。如果resizeEvent触发得话也会更新 closeRect = QRect(width() - 30, 0, 30, 30); }

三、项目结构及源码

imageviewer - res // 存放一些默认的图片资源文件 - 可执行程序 // 是编译好的可执行程序,包含运行的QT环境之类的 - imageviewer.cpp - imageviewer.filters - imageviewer.h - imageviewer.qrc - imageviewer.vcxproj - main.cpp

imageviewer.h

#pragma once #include <QtWidgets/QWidget> #include <QVector> #include <QPixmap> class QPaintEvent; class QMouseEvent; class QResizeEvent; class QKeyEvent; class ImageViewer : public QWidget { Q_OBJECT public: ImageViewer(QWidget *parent = nullptr); ~ImageViewer(); protected: private slots: void loadCurrentImage(); void paintEvent(QPaintEvent*); void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resizeEvent(QResizeEvent*) override; void keyPressEvent(QKeyEvent* event) override; void loadImagesFromDirectory(const QString& dirPath); private: void adjustAndCenter(); int currentImageIndex = 0; bool m_bIsFullScreen = false; QPoint dragOffset; QRect closeRect; QVector<QPixmap> images; };

imageviewer.cpp

#if __cplusplus >= 201103L // 显式指定源码为 UTF-8 #pragma execution_character_set("utf-8") #endif #include "imageviewer.h" #include <QIcon> #include <QPainter> #include <QMouseEvent> #include <QScreen> #include <QApplication> #include <QDir> #include <QKeyEvent> #include <QFileDialog> #include <QMessageBox> ImageViewer::ImageViewer(QWidget* parent) : QWidget(parent) { // QT 5.13+以上支持扫描.qrc目录,如果:/res/res/扫不出来,则可以试试://res/res/被解析为资源路径 QDir dir(":/res/res/"); for (const QString& fileName : dir.entryList({ "*.jpg", "*.png" }, QDir::Files)) { QString fullPath = dir.filePath(fileName); QPixmap img; if (img.load(fullPath)) { images.append(img); } } if (images.isEmpty()) { QPixmap fallback(800, 600); fallback.fill(Qt::black); images.append(fallback); } // 调整应用程序位置,刚开始居中 adjustAndCenter(); // 关闭按钮区域,右上角(30*30) closeRect = QRect(width() - 30, 0, 30, 30); // 设置为无边框、工具窗口(状态栏不会出现图标),并且设置背景不透明 setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::Tool); //setWindowFlags(windowFlags() | Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground, false); // 设置焦点事件() setFocusPolicy(Qt::StrongFocus); setFocus(); //setMouseTracking(true); } void ImageViewer::paintEvent(QPaintEvent*) { // 绘制背景图片(左上角对齐) QPainter painter(this); painter.drawPixmap(0, 0, images[currentImageIndex]); // 绘制右上角得关闭区域 painter.setBrush(QColor(0, 0, 0, 10)); // 半透红 painter.setPen(Qt::NoPen); painter.drawRect(closeRect); } void ImageViewer::loadCurrentImage() { // 这样可以触发重绘paintEvent this->update(); } ImageViewer::~ImageViewer() { } void ImageViewer::adjustAndCenter() { // 获取主屏可用屏幕区域(这个函数排除任务栏区域) QScreen* screen = QApplication::primaryScreen(); QRect avail = screen->availableGeometry(); int maxWidth = 100; int maxHeight = 100; if (m_bIsFullScreen) { maxWidth = static_cast<int>(avail.width()); maxHeight = static_cast<int>(avail.height()); } else { maxWidth = static_cast<int>(avail.width() * 0.5); maxHeight = static_cast<int>(avail.height() * 0.5); } // 如果图片太大,就缩放到不超过 90% 屏幕可用区域,保持比例 // if (images[currentImageIndex].width() > maxWidth || images[currentImageIndex].height() > maxHeight) { // images[currentImageIndex] = images[currentImageIndex].scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); // } // 这里强制进行缩放了,不会保持宽高比和平滑缩放 for (int i = 0; i < images.size(); ++i) { images[i] = images[i].scaled(maxWidth, maxHeight); } if(images.size()) // 设置窗口大小为图片大小 resize(images[0].size()); // 居中显示(主屏中心位置-当前窗口中心位置其实是当前窗口得宽高)=>当前窗口左上角坐标位置 move(avail.center() - rect().center()); // 因为设置了resize,所以关闭区域需要重新更新。如果resizeEvent触发得话也会更新 closeRect = QRect(width() - 30, 0, 30, 30); } void ImageViewer::resizeEvent(QResizeEvent*) { // 更新关闭按钮位置(始终右上角) closeRect = QRect(width() - 30, 0, 30, 30); } void ImageViewer::keyPressEvent(QKeyEvent* event) { switch (event->key()) { // 按键ESV退出程序 case Qt::Key_Escape: qApp->quit(); break; // 按键F10全屏切换 case Qt::Key_F10: m_bIsFullScreen = !m_bIsFullScreen; adjustAndCenter(); if (m_bIsFullScreen) { showFullScreen(); } else { showNormal(); } break; // 空格切换图片 case Qt::Key_Space: currentImageIndex = (currentImageIndex + 1) % images.size(); loadCurrentImage(); break; // tab键可以选择加载新的图片目录 case Qt::Key_Tab: { QString dir = QFileDialog::getExistingDirectory( this, tr("选择图片目录"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); if (!dir.isEmpty()) { loadImagesFromDirectory(dir); } } break; default: QWidget::keyPressEvent(event); break; } } void ImageViewer::loadImagesFromDirectory(const QString& dirPath) { // 查询当前目录下得图片资源(不会递归) QDir dir(dirPath); QStringList nameFilters; nameFilters << "*.jpg" << "*.jpeg" << "*.png" << "*.bmp" << "*.gif"; QFileInfoList fileList = dir.entryInfoList(nameFilters, QDir::Files, QDir::Name); if (fileList.isEmpty()) { QMessageBox::critical(this, "错误", "未找到任何图片资源"); return; } images.clear(); for (const QFileInfo& fileInfo : fileList) { QPixmap img(fileInfo.absoluteFilePath()); if (!img.isNull()) { images.append(img); } } // 需要保证images里面至少有一个 if (images.isEmpty()) { images.append(QPixmap(800, 600)); images.back().fill(Qt::black); } // 重新缩放并居中第一张图 currentImageIndex = 0; adjustAndCenter(); loadCurrentImage(); } void ImageViewer::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { // 新的左上角位置: 当前鼠标的全局位置-偏移量 // 原理: 鼠标按下点的相对于屏幕的位置=窗口左上角坐标+鼠标按下点相对于窗口的位置 // 那么要保持鼠标按下点相对于窗口的位置保持不变,那么新的窗口左上角坐标=新鼠标移动点相对于屏幕坐标-鼠标按下点相对于窗口的位置 // 平滑拖动:全局位置 - 偏移 = 新窗 move(event->globalPos() - dragOffset); } } void ImageViewer::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { // 记录当前鼠标相对于当前窗口位置(不是相对于屏幕) dragOffset = event->pos(); currentImageIndex = (currentImageIndex + 1) % images.size(); // 循环切换图片 loadCurrentImage(); } if (closeRect.contains(event->pos())) { qApp->quit(); return; } }

main.cpp

#include "imageviewer.h" #include <QtWidgets/QApplication> #include <QTimer> #include <QIcon> int main(int argc, char *argv[]) { QApplication app(argc, argv); QEventLoop loop; QTimer::singleShot(1500, &loop, SLOT(quit())); loop.exec(); ImageViewer window; window.show(); return app.exec(); }

四、效果

项目源码下载路径

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/16 18:20:59

使用Tweepy进行Twitter数据分析:从入门到实战应用

想要了解Twitter上的热门话题趋势吗&#xff1f;想从海量推文中挖掘有价值的市场洞察吗&#xff1f;Tweepy数据分析工具正是你需要的利器&#xff01;通过Tweepy Python库&#xff0c;你可以轻松实现Twitter数据挖掘、趋势分析和用户行为分析。 【免费下载链接】tweepy tweepy/…

作者头像 李华
网站建设 2026/3/12 23:08:57

FreeGPT WebUI:零门槛体验顶级AI对话能力的完整指南

FreeGPT WebUI&#xff1a;零门槛体验顶级AI对话能力的完整指南 【免费下载链接】freegpt-webui GPT 3.5/4 with a Chat Web UI. No API key required. 项目地址: https://gitcode.com/gh_mirrors/fre/freegpt-webui 在这个人工智能技术飞速发展的时代&#xff0c;想要体…

作者头像 李华
网站建设 2026/3/12 6:00:13

GoView实战:3步构建疫情数据监控大屏

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个疫情数据监控大屏项目。功能要求&#xff1a;1.世界地图展示各国感染数据2.折线图显示趋势变化3.顶部重要指标看板4.支持时间范围筛选5.自适应多种屏幕尺寸。使用GoViewECh…

作者头像 李华
网站建设 2026/3/19 7:43:35

如何利用U-2-Net深度学习模型实现医疗影像精准分割

如何利用U-2-Net深度学习模型实现医疗影像精准分割 【免费下载链接】U-2-Net U-2-Net - 用于显著对象检测的深度学习模型&#xff0c;具有嵌套的U型结构。 项目地址: https://gitcode.com/gh_mirrors/u2/U-2-Net 在医疗影像分析领域&#xff0c;U-2-Net深度学习模型正以…

作者头像 李华
网站建设 2026/3/14 14:30:29

免安装体验!基于Docker的JDK1.8快速原型环境

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个Docker化的JDK1.8开发环境模板&#xff0c;包含&#xff1a;1.预配置的JDK1.8镜像 2.示例Maven项目结构 3.常用开发工具(vim/git) 4.端口映射配置 5.数据卷支持。要求提供d…

作者头像 李华
网站建设 2026/3/15 2:07:06

Pock终极指南:完美适配你的MacBook Touch Bar

Pock终极指南&#xff1a;完美适配你的MacBook Touch Bar 【免费下载链接】pock Widgets manager for MacBook Touch Bar 项目地址: https://gitcode.com/gh_mirrors/po/pock 还在为MacBook Touch Bar上的控件错位、图标显示不全而烦恼吗&#xff1f;作为专为MacBook To…

作者头像 李华