Qt项目实战2:图片查看器QImageViewer

2023-10-31

在博文Qt学习笔记2:QMainWindow和QWidget的区别中介绍了使用空的Qt项目创建带有菜单栏、工具栏的界面。
这里,使用一个简单的图片查看器项目,来熟悉一下Qt的图片显示和基本操作。
该项目实现的主要功能:

  • 实现图片的打开、关闭、居中显示;
  • 实现图片上一张/下一张切换;
  • 实现图片的放大、缩小操作
  • 实现图片的左旋、右旋操作

需要用的Qt类:

QFileDialog  QImage  QPixmap  QFileInfo

使用空的Qt项目创建带有菜单栏和工具栏的界面的操作参考博文Qt学习笔记1:创建一个QT的空项目

页面布局

根据自己的实际需求添加菜单栏和工具栏,参考如下图:

1

工具栏图标依次是:打开图片、关闭图片、上一张、下一张、左旋、右旋、放大、缩小

此项目算是一个小demo,主要是熟悉图片处理的相关的操作,基础类和操作熟悉之后可以加入复杂的功能。

实现图片的打开、关闭

文件打开和显示的步骤:
(1)使用QFileDislog函数选择一个图片文件,获取到文件的路径名;
(2)使用QImage类加载文件,生成image对象;
(3)使用QLabel类的setPixmap函数将图片显示在界面上。

参考代码如下:

void QImageViewer::openActionTriggered(void)
{
    filename = QFileDialog::getOpenFileName(this, tr("Select image:"),
        "D:\\Documents\\Pictures", tr("Images (*.png *.bmp *.jpg *.gif)"));
    if (filename.isEmpty()) {
        return ;
    }

    QImage image;
    if (!image.load(filename)) {
        QMessageBox::information(this, tr("Error"), tr("Open file error"));
        return ;
    }

    QPixmap pixmap = QPixmap::fromImage(image);
    imageSize = pixmap.size();

    imageLabel->setPixmap(pixmap);
    imageLabel->resize(imageSize);
    //qDebug() << "filname: " << filename;

    setWindowTitle(QFileInfo(filename).fileName() + tr(" - imageViewer"));
}

图片关闭时,只需将label清空即可:

void QImageViewer::closeActionTriggered(void)
{
    imageLabel->clear();
    imageLabel->resize(QSize(200, 100));
    setWindowTitle(tr("imageViewer"));
}

图片打开、关闭的函数,作为QAction的槽函数触发即可:

    connect(openAction, SIGNAL(triggered(bool)), this, SLOT(openActionTriggered()));
    connect(closeAction, SIGNAL(triggered(bool)), this, SLOT(closeActionTriggered()));

提示:在图片打开时可以将界面名改为文件名+标题,关闭时去掉文件名,方便用户使用。

实现图片居中显示、页面自动调整

通过上述操作,已经可以创建一个label,并将图片显示在label中。

但是会出现各种问题,例如:label在resize时部分尺寸超出页面的大小显示不全、图片不能居中显示等。

需要做以下操作来解决上述问题:

  • 使用QLabel类的setPixmap函数将图片显示在界面上;
  • 创建一个QScrollArea部件,将label加载到scrollarea中;
  • 设置scrollarea部件为中心对齐,无边框;
  • 设置页面布局方式为格点布局,并将scrollarea部件增加到0,0点上。

参考代码:

void QImageShow::setImageShowWidget(void)
{
    /* label show image */
    imageLabel = new QLabel();

    QScrollArea *imageScrollArea = new QScrollArea();
    imageScrollArea->setAlignment(Qt::AlignCenter);
    imageScrollArea->setFrameShape(QFrame::NoFrame);
    imageScrollArea->setWidget(imageLabel);

    QGridLayout *mainLayout = new QGridLayout();
    mainLayout->addWidget(imageScrollArea, 0, 0);
    centralWidget->setLayout(mainLayout);
}

当打开一张图片时,自动居中对齐;当图片大小超过中心窗口尺寸时,scrollarea部件出现滑动条,显示配合放大、缩小功能,显示效果更好。
效果如下图所示:

2

实现上一张/下一张切换

在打开文件同时,获取到当前目录的文件列表,保存到文件信息链表中。
当需要对打开的文件上、下切换时,只需对当前链表的下表进行轮询并获取图片显示即可。

void QImageViewer::getImgInfoList(QFileInfoList &imgInfoList)
{
    imgInfoList.clear();

    QDir dir = QFileInfo(filename).absolutePath();
    QFileInfoList infoList = dir.entryInfoList(QDir::Files);
    //qDebug() << "GET:" << infoList.count() << dir;

    QFileInfo info;
    for (int i = 0; i < infoList.count(); i++) {
        info = infoList.at(i);
        //qDebug() << i << info.absolutePath();
        QString suffix = info.suffix();

        if (suffix == "jpg" || suffix == "bmp" || suffix == "png") {
            imgInfoList.append(info);
            //qDebug() << "getImgInfoList:" << i << info.absolutePath() << info.suffix();
        }
    }

    QFileInfo curImageInfo = QFileInfo(filename);
    for (int j = 0; j < imgInfoList.count(); j++) {
        info = imgInfoList.at(j);
        if (info.fileName() == curImageInfo.fileName()) {
            index = j;
            //qDebug() << "curImage index:" << index;
        }
    }
}

在菜单栏和工具栏增加上一张/下一张按钮,实现QAction行为触发。

实现步骤:
(1)使用QFileInfoList链表保存下已经打开的文件所在路径下所有的图片文件的信息;
(2)记录下当前文件的下表,上一张/下一张时间触发时,对下标进行增减;
(3)获取到文件路径和文件名;
(4)重新构造image对象,加载图片文件并显示。

参考代码:

void QImageViewer::lastActionTriggered(void)
{
    //getImgInfoList(imgInfoList);

    index = index - 1;
    int count = imgInfoList.count();
    //qDebug() << "left count: " << count << "index: " << index;
    if (index < 0) {
        index = count - 1;
    }

    filename.clear();
    filename.append(path);
    filename += "/";
    filename += imgInfoList.at(index).fileName();
    //qDebug() << "filname: " << filename;

    QImage image;
    if (!image.load(filename)) {
        QMessageBox::information(this, tr("Error"), tr("Open file error"));
        return ;
    }

    QPixmap pixmap = QPixmap::fromImage(image);
    imageSize = pixmap.size();

    imageLabel->setPixmap(pixmap);
    imageLabel->resize(imageSize);

    setWindowTitle(QFileInfo(filename).fileName() + tr(" - imageViewer"));
}

void QImageViewer::nextActionTriggered(void)
{
    //getImgInfoList(imgInfoList);

    index = index + 1;
    int count = imgInfoList.count();
    //qDebug() << "right count: " << count << "index: " << index;
    if (index == count) {
        index = 0;
    }

    filename.clear();
    filename.append(path);
    filename += "/";
    filename += imgInfoList.at(index).fileName();
    //qDebug() << "filname: " << filename;

    QImage image;
    if (!image.load(filename)) {
        QMessageBox::information(this, tr("Error"), tr("Open file error"));
        return ;
    }

    QPixmap pixmap = QPixmap::fromImage(image);
    imageSize = pixmap.size();

    imageLabel->setPixmap(pixmap);
    imageLabel->resize(imageSize);

    setWindowTitle(QFileInfo(filename).fileName() + tr(" - imageViewer"));
}

图片的左旋、右旋操作

图片的旋转使用QImage类的transformed行为来实现,使用成员变量保存下当前图片的旋转角度,每次触发旋转操作,将旋转角度以90度间隔增减。

实现步骤:
(1)通过当前图片文件名静态加载image;
(2)将成员变量旋转角度,以90度间隔增、减;
(3)通过QImage类的transformed行为修改加载的图片角度;
(4)显示修改后的图片,并重置图片尺寸等;

参考代码:

void QImageViewer::toLeftActionTriggered(void)
{
    QImage imgRotate;
    QMatrix matrix;
    QPixmap pixmap;
    QImage image;

    imageAngle += 3;
    imageAngle = imageAngle % 4;
    qDebug() << "angle:%d" << imageAngle;
    matrix.rotate(imageAngle * 90);

    image.load(filename);
    imgRotate = image.transformed(matrix);
    pixmap = QPixmap::fromImage(imgRotate);
    imageSize = pixmap.size();

    imageLabel->resize(imgRotate.size());
    imageLabel->setPixmap(pixmap);
}

void QImageViewer::toRightActionTriggered(void)
{
    QImage imgRotate;
    QMatrix matrix;
    QPixmap pixmap;
    QImage image;

    imageAngle += 1;
    imageAngle = imageAngle % 4;
    //qDebug() << "angle:%d" << imageAngle;
    matrix.rotate(imageAngle * 90);

    image.load(filename);
    imgRotate = image.transformed(matrix);
    pixmap = QPixmap::fromImage(imgRotate);
    imageSize = pixmap.size();

    imageLabel->resize(imgRotate.size());
    imageLabel->setPixmap(pixmap);
}

图片的放大、缩小操作

图片的放大、缩小操作通过QImage类的scaled行为实现。可以重置image的尺寸,返回新的image对象。

这里增加一个功能:支持图片旋转之后进行放大、缩小操作。只需对放大、缩小后新的image对象再进行旋转操作即可。

实现步骤:
(1)通过当前图片文件名静态加载image;
(2)修改成员变量图片尺寸,设置放大倍数为1.2,缩小倍数为0.8,保存新的尺寸;
(3)通过QImage类的transformed行为修改加载的图片角度;
(5)显示修改后的图片,并重置图片尺寸等;

参考代码:

void QImageViewer::toEnlargeActionTriggered(void)
{
    QImage imgScaled;
    QPixmap pixmap;
    QImage image;
    QImage imgRotate;
    QMatrix matrix;

    image.load(filename);
    matrix.rotate(imageAngle * 90);
    imgRotate = image.transformed(matrix);

    imgScaled = imgRotate.scaled(imageSize.width() * 1.2,
                             imageSize.height() * 1.2,
                             Qt::KeepAspectRatio);

    pixmap = QPixmap::fromImage(imgScaled);
    imageSize = pixmap.size();
    //qDebug() << "width:%d, height:%d" << imageSize.width() << imageSize.height();

    imageLabel->setPixmap(pixmap);
    imageLabel->resize(imageSize);
}

void QImageViewer::toLessenActionTriggered(void)
{
    QImage imgScaled;
    QPixmap pixmap;
    QImage image;
    QImage imgRotate;
    QMatrix matrix;

    image.load(filename);
    matrix.rotate(imageAngle * 90);
    imgRotate = image.transformed(matrix);

    imgScaled = imgRotate.scaled(imageSize.width() * 0.8,
                             imageSize.height() * 0.8,
                             Qt::KeepAspectRatio);

    pixmap = QPixmap::fromImage(imgScaled);
    imageSize = pixmap.size();
    //qDebug() << "width:%d, height:%d" << imageSize.width() << imageSize.height();

    imageLabel->setPixmap(pixmap);
    imageLabel->resize(imageSize);
}

需要注意的地方

  • 图片关闭、上一张、下一张操作是在图片打开成功之后才能操作,所以误点按钮导致程序崩溃,需要加使能操作。

  • 此处图片对象的创建没有使用new操作,理由是需要手动delete比较麻烦;直接使用局部变量,编译器会自动释放资源。

  • 此处使用空的Qt模板创建,不使用系统的ui文件,需要手动初始化菜单栏、工具栏和中心窗口等。

源码下载链接:https://github.com/gitorup/QImageViewer

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt项目实战2:图片查看器QImageViewer 的相关文章

随机推荐

  • 高防CDN对于网站、平台有着至关重要作用?

    1 减轻服务器的占用率和网站服务器的带宽资源 通过使用CDN服务 用户可以在CDN节点上分发对主要频道 包括页面和图片 的访问 这样可以减少源设备上的负载压力和带宽资源 并将资源保存到相同的带宽消耗服务中 如邮件 论坛和服务器资源 以保证网
  • 热区的使用方法

    1 如图所示 热区的位置是在元件库中 这样的一个标识 2 热区的使用经常会搭配一些比较小的文字或者图片等区域 只要是在热区中 随便点击哪一个地方都是属于这个区域 3 我们做了几个页面 样式不同 4 如图 我们创建一个热区在02的选区中 5
  • python + selenium实现巨潮资讯网指定范围年报下载

    大家好 第一次写文章 紧张滴捏 这段时间在做课设 课设里需要下载沪市600000到601000号的年报原文做数字化关键词的词频分析 想着用程序帮我批量下载一下 但是找了一下貌似没有类似的代码 就写了一个应用selenium库来做模拟下载的p
  • 各类打印机驱动官网下载安装

    前言概述 找驱动很简单 但是网上有时候找起来有点费劲呢 不安全 目前市面上打印机驱动搜索软件好用的基本都要付费 或者不全 比如下图这个就是付费的 常用的打印机品牌 惠普 HP 佳能 Canon 爱普生 Epson 京瓷 Kyocera 三星
  • 二流计算机学校,学校可以是二流的,但你不是

    我每天都会看大家在微博里给我的留言 时常看到深夜 私信的 每一条都看 问的最多的一种问题 是这么开头的 我的学校不好 或者 我是一个来自二 三 本学校的学生 我该怎么办 我不知道怎么回答 因为我不觉得来自一个二流的学校就应该过着二流的生活
  • C语言实现队列(链表实现)

    队列 Queue 也是运算受限的线性表 是一种先进先出 First In First Out 简称 FIFO 的线性表 只允许在表的一端进行插入 而在另一端进行删除 队首 front 允许进行删除的一端称为队首 队尾 rear 允许进行插入
  • 浅析Linux内核中的链表

    1 内核中的链表 linux内核链表与众不同 他不是把将数据结构塞入链表 而是将链表节点塞入数据 在2 1内核中引入了官方链表 从此内核中所有的链表使用都采用此链表 千万不要在重复造车轮子了 链表实现定义在
  • 第五届蓝桥杯Java A组决赛试题

    1 标题 海盗分金币 有5个海盗 相约进行一次帆船比赛 比赛中天气发生突变 他们被冲散了 恰巧 他们都先后经过途中的一个无名的荒岛 并且每个人都信心满满 觉得自己是第一个经过该岛的人 第一个人在沙滩上发现了一堆金币 他把金币分成5等份 发现
  • STM32CubeMX驱动MPU6050模块

    文章目录 1 MPU6050模块简介 2 MPU6050重要寄存器介绍 2 1 数字低通滤波器配置寄存器CONFIG 2 2 采样率分频寄存器SMPRT DIV 2 3 加速度计配置寄存器ACCEL CONFIG 2 4 角速度计配置寄存器
  • 区别:OrderedDict vs dict

    OrderedDict 和 dict 是两种字典类型 都用于存储键值对 key value pairs dict 是Python的内置字典类型 它是无序的 即它不会按照元素插入的顺序来保存键值对 当你通过键来访问 dict 中的值时 Pyt
  • sqoop query时单双引号选用以及$CONDITION使用的探究

    这段时间碰见了一个奇怪的sqoop导入问题 我的业务目标是想将postgresql库里的某张表内的数据导入到hive里 而且在导入的时候需要做一步查询 但在导入的时候 围绕着 CONDITION 这个参数 会有不同的运行结果 有的报错 有的
  • MySQL基础架构与日志详解

    一 MySQL基础架构 MySQL可以分为Server层和存储引擎层两部分 Server层包括连接器 查询缓存 分析器 优化器 执行器等 涵盖MySQL的大多数核心服务功能 以及所有的内置函数 如日期 时间 数学和加密函数等 所有跨存储引擎
  • 软件需求之DFD图

    DFD图是一种以数据和数据的封闭性为基础 从问题空间到某种表示的映射方法 是一种结构化分析方法 DFD图在软件的需求分析中发挥着不可替代的作用 DFD图在软考中是必考的内容 在软件工程中也是一个非常中要的图 下面是我结合网上的资料和自己的所
  • Linux系统里压缩PDF文件大小

    sudo apt get install ghostscript gs sDEVICE pdfwrite dCompatibilityLevel 1 4 dPDFSETTINGS screen dNOPAUSE dQUIET dBATCH
  • JavaFX 控件 ImageView

    ImageView 支持格式 BMP GIF JPEG PNG 加载图片 如果设置了 requestedXXX 尺寸 ImageView中 设置 FitXXX 尺寸是基于requestedXXX 尺寸缩放 Image image new I
  • Linux & Docker常用命令

    目录 一 Docker服务相关命令 二 镜像相关命令 查看镜像 查看本地所有的镜像 搜索镜像 从网络中查找需要的镜像 拉取镜像 删除镜像 三 容器相关命令 查看容器 创建容器 进入容器 启动容器 停止容器 重启应用 删除容器 查看容器信息
  • OpenCV教程——加载、修改、保存图像

    1 颜色空间 颜色空间 也称彩色模型 又称彩色空间或彩色系统 本质上 彩色模型是坐标系统和子空间的阐述 位于系统的每种颜色都有单个点表示 RGB 红绿蓝 是依据人眼识别的颜色定义出的空间 可表示大部分颜色 但在科学研究中一般不采用RGB颜色
  • ARouter 源码分析

    ARouter基本使用 在开始分析源码之前 先了解一下ARoute如何使用的 使用ARoute可以概括为以下3步 项目中引入ARouter 及配置 初始化ARouter 开始使用 下面详细的看下每一步怎么操作 项目中引入ARouter及配置
  • 计算机一级2010的试题,全国计算机等级考试一级office2010试题

    计算机一级是要求考生对基础的计算机知识进行掌握 下面给大家整理了全国计算机等级考试一级office2010试题 欢迎阅读 全国计算机等级考试一级office2010试题 选择题答案 1 5 C A D B B 6 10 B C D B D
  • Qt项目实战2:图片查看器QImageViewer

    在博文Qt学习笔记2 QMainWindow和QWidget的区别中介绍了使用空的Qt项目创建带有菜单栏 工具栏的界面 这里 使用一个简单的图片查看器项目 来熟悉一下Qt的图片显示和基本操作 该项目实现的主要功能 实现图片的打开 关闭 居中