简易Qt图片查看器

news2024/10/7 12:19:55

本篇使用Qt来实现一个可以查看任意目录下图片的图片查看器,可以电脑中任意目录下图片的查看,并且可以通过鼠标滚轮以及鼠标移动来实现图片的灵活放大、缩小,此外,在打开一个图片后,若该目录下还有其它图片,通过左右切换,可以方便的查看同目录下的其它图片,先来看下最终的效果:

  • 通过点击下方的图片文件夹图标,可以弹出系统文件选择窗口,可以选定任意目录下的图片
  • 选择图片后,图片显示主窗口即可居中显示图片
  • 通过滚轮上下滑动,可以放大和缩小图片
  • 鼠标左键按下再移动,可以移动图片
  • 下方两侧的切换按钮,可以切换上一张、下一张图片

1 总体结构

整个Qt图片查看器项目的代码结构如下:

  • 主代码中是图片查看器相关的代码,包括:
    • src:图片查看器主代码

    • picview.pro:Qt工程文件

    • images:存放各个按钮图标的资源文件

  • build中是编译的中间文件和编译结果存储的目录

下面分类介绍了程序的主要代码实现。

2 软件开发

2.1 整体布局

主界面的通过垂直布局,上方是图片显示,下方是按钮操作。下方的3个按钮再通过水平布局实现。

采用自动布局管理,可随窗口大小自动调整显示。

PicViewWidget::PicViewWidget(QMainWindow *parent)
    : QMainWindow(parent)
{
    m_box = new ImageBox();
    QScrollArea *m_scrollArea = new QScrollArea();
    m_scrollArea->setMinimumSize(800, 600);
    m_scrollArea->setWidgetResizable(true);
    m_scrollArea->setWidget(m_box);

    QHBoxLayout *layout_view = new QHBoxLayout();
    layout_view->addWidget(m_scrollArea);

    m_prevBtn = new QPushButton();
    m_prevBtn->setIcon(QIcon(":/images/prev.png"));
    m_prevBtn->setFlat(true);
    m_prevBtn->setIconSize(QSize(50,50));

    m_openBtn = new QPushButton();
    m_openBtn->setIcon(QIcon(":/images/openimg.png"));
    m_openBtn->setFlat(true);
    m_openBtn->setIconSize(QSize(70,70));

    m_nextBtn = new QPushButton();
    m_nextBtn->setIcon(QIcon(":/images/next.png"));
    m_nextBtn->setFlat(true);
    m_nextBtn->setIconSize(QSize(50,50));

    m_nextBtn->setEnabled(false);
    m_prevBtn->setEnabled(false);

    QHBoxLayout *layout_button = new QHBoxLayout();
    layout_button->addWidget(m_prevBtn);
    layout_button->addWidget(m_openBtn);
    layout_button->addWidget(m_nextBtn);

    //主布局管理
    QVBoxLayout *layout_main = new QVBoxLayout();
    layout_main->setSpacing(5);
    layout_main->addLayout(layout_view);
    layout_main->addLayout(layout_button);
    layout_main->setStretch(0,10);
    layout_main->setStretch(1,1);
    layout_main->setContentsMargins(5,5,5,5);

    QWidget *widget = new QWidget();
    widget->setLayout(layout_main);
    this->setCentralWidget(widget);

    connect(m_nextBtn, SIGNAL(clicked(bool)), this, SLOT(show_next()));
    connect(m_prevBtn, SIGNAL(clicked(bool)), this, SLOT(show_prev()));
    connect(m_openBtn, SIGNAL(clicked(bool)), this, SLOT(open_files()));
}

2.2 操作按钮

操作按钮,即:打开文件按钮、下一张按钮、上一张按钮。

打开指定图片,通过QFileDialog::getOpenFileName来获取图片文件的绝对路径,然后再提取出此图片所处的目录位置,通过QDir的entryInfoList方法再找出此目录下的其它所有图片。

void ImageBox::setImage(QPixmap img)
{
    m_img = img;
    calcAndShow();
}

void PicViewWidget::open_files()
{
    m_file = QFileDialog::getOpenFileName(this, "所有图片", "/home", "Image Files (*.png *.jpg *.bmp)");
    qDebug() << m_file;

    if (m_file.isEmpty())
    {
        return;
    }

    QString file_path = QFileInfo(m_file).absolutePath();
    qDebug() << file_path;

    QDir dir(file_path);
    dir.setFilter(QDir::Files | QDir::NoSymLinks);
    QFileInfoList list = dir.entryInfoList(QStringList() << "*.jpg"
                                                         << "*.png"
                                                         << "*.bmp");
    m_files.clear();
    for (int i = 0; i < list.size(); i++)
    {
        QFileInfo fileInfo = list.at(i);
        qDebug() << fileInfo.absoluteFilePath();
        m_files.push_back(fileInfo.absoluteFilePath());

        if(m_file == m_files[i])
        {
            m_idx = i;
        }
    }

    qDebug() << m_files.size();
    m_box->setImage(QPixmap(m_files[m_idx]));// 显示图片
    m_prevBtn->setEnabled(true);
    m_nextBtn->setEnabled(true);
}

打开图片文件示意:

上一张、下一张按钮的槽函数如下,主要就是在按下按钮后,切换要显示的图片即可。

void PicViewWidget::show_prev()
{
    if (m_idx == 0)
    {
        m_prevBtn->setEnabled(false);
        qDebug() << m_idx;
        return;
    }
    qDebug() << m_idx;

    m_nextBtn->setEnabled(true);
    m_idx--;
    m_box->setImage(QPixmap(m_files[m_idx]));
}

void PicViewWidget::show_next()
{
    if (m_idx == m_files.length() - 1)
    {
        m_nextBtn->setEnabled(false);
        qDebug() << m_idx;
        return;
    }
    qDebug() << m_idx;

    m_prevBtn->setEnabled(true);
    m_idx++;
    m_box->setImage(QPixmap(m_files[m_idx]));
}

2.3 图像显示窗口

图像显示窗口,专门写了一个类来实现图片显示,缩放显示等功能。

2.3.1 图像位置计算与显示

由于每个图片的大小都不一样,为了能让图片显示的更合适,需要根据图片的大小和当前显示窗口的大小,计算出图片初始显示时需要缩放的比例,以及居中显示起始位置。

void ImageBox::calcAndShow()
{
    if (!m_img.isNull())
    {
        int srcWidth = m_img.width();
        int srcHeight = m_img.height();
        qDebug() << "srcWidth: " << srcWidth << ", srcHeight: " << srcHeight;

        int curWinWidth = this->width();
        int curWinHeight = this->height();
        qDebug() << "curWinWidth: " << curWinWidth << ", curWinHeight: " << curWinHeight;

        if (srcHeight / srcWidth > curWinHeight / curWinWidth)
        {
            m_newHeight = curWinHeight;
            m_newWidth = srcWidth * curWinHeight / srcHeight;
            m_scale = (float)curWinWidth / (float)srcHeight;
        }
        else
        {
            m_newHeight = srcHeight * curWinWidth / srcWidth;
            m_newWidth = curWinWidth;
            m_scale = (float)m_newWidth / (float)srcWidth;
        }
        qDebug() << "m_newWidth: " << m_newWidth << ", m_newHeight: " << m_newHeight;

        m_point = QPoint(int((curWinWidth - m_newWidth) * 0.5), int((curWinHeight - m_newHeight) * 0.5));
        qDebug() << "m_scale: " << m_scale << "m_point: " << m_point.x() << " " << m_point.y();

        update();
    }
}

图片显示在paintEvent中实现,通过QPainter的drawPixmap接口进行图片的显示。

void ImageBox::paintEvent(QPaintEvent *event)
{
    if (!m_img.isNull())
    {
        QPainter painter(this);
        painter.scale(m_scale, m_scale);
        if (m_wheelFlag)       // 定点缩放
        {
            m_wheelFlag = false;
            // 判断当前鼠标pos在不在图上
            float this_left_x = m_point.x() * m_lastScale;
            float this_left_y = m_point.y() * m_lastScale;
            float this_scale_width = m_newWidth * m_lastScale;
            float this_scale_height = m_newHeight * m_lastScale;

            // 鼠标点在图上,以鼠标点为中心动作
            float gap_x = m_x - this_left_x;
            float gap_y = m_y - this_left_y;
            if (0 < gap_x < this_scale_width && 0 < gap_y < this_scale_height)
            {
                int new_left_x = int(m_x / m_scale - gap_x / m_lastScale);
                int new_left_y = int(m_y / m_scale - gap_y / m_lastScale);
                m_point = QPoint(new_left_x, new_left_y);
            }
            // 鼠标点不在图上,固定左上角进行缩放
            else
            {
                int true_left_x = int(m_point.x() * m_lastScale / m_scale);
                int true_left_y = int(m_point.y() * m_lastScale / m_scale);
                m_point = QPoint(true_left_x, true_left_y);
            }
        }

        //qDebug() << "---m_scale: " << m_scale << "m_point: " << m_point.x() << " " << m_point.y();
        painter.drawPixmap(m_point, m_img);
    }
}

2.3.2 鼠标缩放与移动

图片的显示还支持鼠标操作,可以实现滚轮的放大缩小,移动显示。

滚动的移动,使用的wheelEvent来获取滚轮事件,当滚轮向前滑动时,增大缩放比例,当滚轮向后滑动时,减小缩放比例,然后调用update函数触发图像重绘。

void ImageBox::wheelEvent(QWheelEvent *event)
{
    float angleY = event->delta()/8;
    m_lastScale = m_scale;
    m_wheelFlag = true;
    // 获取当前鼠标相对于view的位置
    m_x = event->x();
    m_y = event->y();
    if (angleY > 0)
    {
        m_scale *= 1.08;
    }
    else  //滚轮下滚
    {
        m_scale *= 0.92;
    }

    if (m_scale < 0.1)
    {
        m_scale = 0.1;
    }
    else if (m_scale > 100)
    {
        m_scale = 100;
    }

    adjustSize();
    update();
}

鼠标的移动,使用的mouseMoveEvent来获取鼠标移动事件,并结合mousePressEvent来检查鼠标左键是否按下,当鼠标左键按下且鼠标移动时,才进行图片的移动显示。

void ImageBox::mouseMoveEvent(QMouseEvent *event)
{
    if (m_leftClick)
    {
        m_endPos = event->pos() - m_startPos;          //当前位置-起始位置=差值
        m_point = m_point + m_endPos / m_scale ;       //左上角的距离变化
        m_startPos = event->pos();
        update();
    }
}

void ImageBox::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_leftClick = true;
        m_startPos = event->pos();
    }
}

void ImageBox::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_leftClick = false;
    }
}

3 总结

本篇实现了示意Qt实现一个j简易的图片查看器,可以实现任意目录下图片的查看,鼠标与滚轮操作图片放大缩小移动等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot+Vue实现前后端分离的电影院管理系统

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

004:搭建常规项目框架「Window、TabBar、NavigationController、ViewController」

常见App页面结构分析&#xff1a; 单页面展示&#xff1a; 列表页面的展示「UITableView」。滚动页面的展示「UIScrollow」。 多页面展示&#xff1a; 通过底部标签栏「TabBar」。通过Push栈的方式进行页面的切换。UITabBarController&#xff1a; 介绍&#xff1a;通过底部…

打响跨域/中央计算「攻坚」战,这些头部企业已经抢先布局

从域控架构到中央集成式架构&#xff0c;跨域融合已经加速到来&#xff0c;从单一域控制器&#xff0c;到多域融合中央计算&#xff0c;市场门槛进一步抬升&#xff0c;市场也进入新一轮「攻坚」阶段。 高工智能汽车研究院发布《2023-2025年智能网联产业趋势报告》显示&#x…

Flink Checkpoint 问题排查实用指南

在 Flink 中,状态可靠性保证由 Checkpoint 支持,当作业出现 failover 的情况下, Flink 会从最近成功的 Checkpoint 恢复。在实际情况中,我们可能会遇到 Checkpoint 失败,或者 Checkpoint 慢的情况,本文会统一聊一聊 Flink 中 Checkpoint 异常的情况(包括失败和慢),以及…

【单片机】串口通信/LED点阵

目录 一、串口介绍 1、双向串口通信 2、电平标准 3、常用通信协议 4、时序图 二、串口收发数据&#xff08;模式1&#xff09; 1、串行控制&#xff08;模式选择&#xff09;寄存器SCON&#xff08;可位寻址&#xff09; 2、串行口数据缓冲寄存器SBUF 3、电源控制&…

引擎入门 | Unity UI简介–第2部分(7)

本期我们继续为大家进行Unity UI简介&#xff08;第二部分&#xff09;的后续教程 本篇内容 12.在菜单场景中添加音乐 13.开启和关闭音乐 文章末尾可免费获取教程源代码 本篇本篇Unity UI简介&#xff08;第二部分&#xff09;篇幅较长&#xff0c;分为八篇&#xff0c;本…

对称加密算法(三)(DES)

文章目录DES EncryptionDES DecryptionExampleThe Avalanche EffectThe Strength of DESThe Use of 56-Bit KeysThe Nature of the DES AlgorithmReferences在 2001 年引入 AEC&#xff08;Advanced Encryption Standard&#xff09;之前&#xff0c;最为普遍使用的加密机制就是…

Python里面的xlrd模块详解

那我就一下面积个问题对xlrd模块进行学习一下&#xff1a; 1.什么是xlrd模块&#xff1f; 2.为什么使用xlrd模块&#xff1f; 3.怎样使用xlrd模块&#xff1f; 1.什么是xlrd模块&#xff1f; ♦python操作excel主要用到xlrd和xlwt这两个库&#xff0c;即xlrd是读excel&…

SQL 入门篇之什么是别名?

SQL 入门篇之什么是别名&#xff1f; &#x1f4d2;博客主页&#xff1a; ​​开心档博客主页​​ &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由开心档原创&#xff01; &#x1f4c6;51CTO首发时间&#xff1a;&#x1…

计算机研究生就业方向之考公

我一直跟学生们说你考计算机的研究生之前一定要想好你想干什么&#xff0c;如果你只是转码&#xff0c;那么你不一定要考研&#xff0c;至少以下几个职位研究生是没有啥优势的&#xff1a; 1&#xff0c;软件测试工程师&#xff08;培训一下就行&#xff09; 2&#xff0c;前…

一篇文章带你了解Linux内核进程上下文切换

&#xff11;&#xff0e;进程上下文的概念 进程上下文是进程执行活动全过程的静态描述。我们把已执行过的进程指令和数据在相关寄存器与堆栈中的内容称为进程上文&#xff0c;把正在执行的指令和数据在寄存器与堆栈中的内容称为进程正文&#xff0c;把待执行的指令和数据在寄…

【SpringMVC】HiddenHttpMethodFilter 转换请求方式

由于浏览器只支持发送get和post方式的请求&#xff0c;那么该如何发送put和delete请求呢&#xff1f; SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求 HiddenHttpMethodFilter 处理put和delete请求的条件&#xff1a; 当前请求的请求…

软考《系统集成项目管理工程师》必备100题(1)

新一轮软考备考来啦~为了帮助大家提高备考效率&#xff0c;将2023上半年软考《系统集成项目管理工程师》必备100题&#xff0c;分享给大家&#xff0c;快来跟着一起打卡学习吧&#xff01; 有电子版的&#xff0c;可以打印下来背诵~ 1.项目管理过程组有哪些? 启动过程组:定…

aspose win/linux WORD转PDF(及其解决乱码方式)

aspose win/linux WORD转PDF&#xff08;及其解决乱码方式&#xff09;1.工具类2.控制台3.解决乱码4.JAR包之前自己用的docm4j 本地进行转换是ok 在服务器中就异常了&#xff1b; 后来在网上查询之后 do4j无法支持liunx系统&#xff1b; 1.工具类 package com.aostar.ida.fra…

LabVIEW在面向对象编程中利用硬件抽象层(HAL)设计2

LabVIEW在面向对象编程中利用硬件抽象层(HAL)设计2 这部分是< LabVIEW在面向对象编程中利用硬件抽象层(HAL)设计1>的下半部分。 一体化项目 因此&#xff0c;部署为打包库&#xff0c;实际上有多种方法来开发代码:首先&#xff0c;将查看all-in-one项目方法。父类和子…

python numpy 的输出控制

示例代码&#xff1a; import numpy as np #precision: xnp.array([3.1415926]) print(x) np.set_printoptions(precision4) print(x)#threshold: xnp.arange(0,12,1) print(x) np.set_printoptions(threshold7) print(x)#edgeitems: xnp.arange(0,12,1) print(x) np.set_prin…

【大数据】clickhouse 常用语法规则优化策略详解

一、前言 在之前的文章中&#xff0c;我们了解到clickhouse作为一款列式存储数据库 &#xff0c;查询性能非常高效&#xff0c;一方面与其自身的存储引擎设计有关&#xff0c;另一方面&#xff0c;在执行查询语句时&#xff0c;底层做了大量的语法规则的优化&#xff0c;本文将…

一体化闸门控制机如何使用

一体化闸门控制机是一款集水位采集、流量计算、图片视频采集、远程通讯、远程控制、本地控制于一体的闸门自动化、信息化测控设备&#xff0c;能够在监控中心远程启闭以及闸门手/自动控制;并通过实时图像监控可以直观了解闸门的运行工况以及周边环境。 1、设备介绍 闸门自动化…

Windows11 Docker镜像存储路径更改(非C盘路径)

Windows11 Docker镜像存储路径更改&#xff08;非C盘路径&#xff09; 基于WSL2安装docker后&#xff0c;在使用过程中会发现大量的docker镜像文件&#xff0c;使系统C盘容量激增&#xff0c;对电脑后续使用造成不便&#xff0c;所以需要在安装的时候&#xff0c;手动修改dock…

已开发多款原型,或明年发布新品,三星XR专利布局大揭秘

近期据韩媒Naver新闻爆料&#xff0c;三星电子或将于明年面向开发者推出XR头显&#xff0c;以构建XR生态系统。据悉&#xff0c;三星计划通过打造XR生态系统&#xff0c;来整合元宇宙、软件、内容、元件开发和科研等上下游领域。据称&#xff0c;为了开发XR设备&#xff0c;三星…