Qt 完成图片的缩放拖动

news2024/10/5 21:14:59

1. 事件和函数

主要使用事件paintEvent(QPaintEvent *event)和drawTiledPixmap函数实现绘图。

  1. paintEvent事件在改变窗口大小、移动窗口、手动调用update等情形下会被调用。
  2. 需先了解下绘图该函数的用法。 - QPainter::drawTiledPixmap(int x, int y, int w, int h, const QPixmap &pm, int sx, int sy)
  3. 效果:

2. 建立项目

假设已新建了mainwindow项目。现再添加一界面用于单独显示图片,作为子窗口供调用(当然,也可在main.c直接调用,作为父窗口)。
1)右击界面文件—添加新文件—QT—QT设计师界面类—choose—Dialog without buttons。类名为CImgPaint。
打开ui文件,添加控件,主要是label和button,

具体属性列表如下:

其中,label控件仅仅是提供绘图的区域,但最终绘图不是绘在label控件上。

3. 头文件添加变量,编写构造函数

头文件中包含头文件

#define USE_OPENCV 0

#include <QDialog>
#include "mainwindow.h"
#include "QPoint"
using namespace std;
#if USE_OPENCV
    #include "opencv2/opencv.hpp"
    #include <opencv2/core.hpp>
    using namespace cv;
#endif

namespace Ui {
class CImgPaint;
}
类中添加成员变量
private:
    QPixmap  pix_src,pix_fix;//原始图片及缩放图片
    float ratio=1.0;        //缩放比
    QRect paint_rect;           //绘画限制区域
    int paint_xe,paint_ye,paint_we,paint_he;//期望绘画区域
    int paint_x,paint_y,paint_w,paint_h;//实际绘画区域
    int pix_sx,pix_sy;          //选择图片的起始x、y开始绘画
    int step_val = 20;          //单次移动步长
    bool mouse_press_flag = false;//鼠标是否按下
    QPoint mouse_pos_pre,mouse_pos_cur;//记录鼠标位置

 构造函数中,添加输入图片的读取,以及变量初始化。

#include "CImgPaint.h"
#include "ui_cimgpaint.h"
#include "QDebug"
#include "QPainter"
#include "QWheelEvent"
#include "QImage"
CImgPaint::CImgPaint(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::CImgPaint)
{
    ui->setupUi(this);

#if USE_OPENCV
    cv::Mat img = cv::imread("lenaGray.jpg",-1);
    pix_src = get_pixmap_from_mat(img);
#else
    QImage qimg ;
    qimg.load("./lenaGray.jpg");
    pix_src = pix_src.fromImage(qimg);
#endif
    paint_rect.setRect(ui->label_paint->x()+1,ui->label_paint->y()+1,ui->label_paint->width()+1,ui->label_paint->height()+1);
    pix_fix = pix_src.scaled(paint_rect.width(),paint_rect.height(),Qt::KeepAspectRatio,Qt::FastTransformation);
    reset_para();
    update_para();
}

void CImgPaint::reset_para(void)
{
    ratio = 1.0;
    paint_xe = paint_x =paint_rect.x()  ;
    paint_ye = paint_y = paint_rect.y();
    paint_we = paint_w = paint_rect.width();
    paint_he = paint_h = paint_rect.height();
    pix_sx = 0;
    pix_sy = 0;
}

#if USE_OPENCV
QPixmap CImgPaint::get_pixmap_from_mat(Mat img)
{
    cv::Mat img_temp = img.clone();
    QImage::Format format = QImage::Format_RGB888;
    if(img_temp.channels()==1)
    {
        format  = QImage::Format_Grayscale8;
    }
    if(img_temp.channels()==3)
    {
        cv::cvtColor(img_temp,img_temp,CV_BGR2RGB);//opencv 按BGR处理
    }

    if(img_temp.depth()==CV_16U)
    {
        img_temp.convertTo(img_temp,CV_8U,1.0/257.0);
    }

    QPixmap pixmap = QPixmap::fromImage(QImage(img_temp.data,img_temp.cols,img_temp.rows,(int)img_temp.step,format));
    return pixmap;

}
#endif
CImgPaint::~CImgPaint()
{
    delete ui;
}


update_para函数用来确定drawTiledPixmap函数的各参数。

void CImgPaint::update_para(void)
{
    //*************** 0. 处理放大和缩小 **********************//
    // 放大缩小仅仅改变width和height,图像的起始点,不变
    int w,h;
    w = ratio*paint_rect.width();//放大或缩小,统一对标到画布尺寸
    h = ratio*paint_rect.height();
    pix_fix = pix_src.scaled(w,h,Qt::KeepAspectRatio,Qt::FastTransformation);//对输入的原图进行放大、缩小
    paint_we = pix_fix.width();//得到初始的图像w、h期望值
    paint_he = pix_fix.height();
    //*************** 1. 处理Y方向 **********************//
    //1.1 首先确定实际绘图的起点,绘图的起点应在画布的大小范围内 //
    // 若期望的起点超出画布上限,则设置截取图像的起始位置sy
    paint_y = paint_ye;
    if(paint_y < paint_rect.y())
    {
        pix_sy =  paint_rect.y() - paint_y;
        pix_sy = pix_sy>pix_fix.height()?pix_fix.height():pix_sy;
        paint_y = paint_rect.y();
    }
    else
    {
        pix_sy = 0;
    }
    //若期望的起点超出画布下限,不允许
    if(paint_y>=(paint_rect.y()+paint_rect.height()-1))
    {
        paint_y = (paint_rect.y()+paint_rect.height()-1);
    }
    //1.2 再确定终点,即高度
     //对于图片本身,减去sy,得到图片本身剩余的高度
    paint_he -= pix_sy;
    // 图片本身的高度,同样不可超过画图区域剩余的高度
    paint_he = paint_he>( paint_rect.height()+paint_rect.y()-paint_y )?(paint_rect.height()+paint_rect.y()-paint_y):paint_he;
    //*************** 2. 处理X方向 **********************//
    //1.1 首先确定实际绘图的起点,绘图的起点应在画布的大小范围内 //
    // 若期望的起点超出画布上限,则设置截取图像的起始位置sx
    paint_x = paint_xe;
    if(paint_x < paint_rect.x())
    {
        pix_sx =  paint_rect.x() - paint_x;
        pix_sx = pix_sx>pix_fix.width()?pix_fix.width():pix_sx;
        paint_x = paint_rect.x();
    }
    else
    {
        pix_sx = 0;
    }
    //若期望的起点超出画布下限,不允许
    if(paint_x>=(paint_rect.x()+paint_rect.width()-1))
    {
        paint_x = (paint_rect.x()+paint_rect.width()-1);
    }
    //1.2 再确定终点,即宽度
     //对于图片本身,减去sx,得到图片本身剩余的宽度
    paint_we -= pix_sx;
    // 图片本身的宽度,同样不可超过画图区域剩余的宽度
    paint_we = paint_we>( paint_rect.width()+paint_rect.x()-paint_x )?(paint_rect.width()+paint_rect.x()-paint_x):paint_we;

    paint_h = paint_he;
    paint_w = paint_we;

    qDebug()<<paint_rect;
    qDebug()<<paint_x<<paint_y<<paint_w<<paint_h<<pix_fix<<pix_sx<<pix_sy;



}

4. paintEvent事件

头文件声明void paintEvent(QPaintEvent *event);并且在cpp文件中重写该事件函数


public slots:
    void paintEvent(QPaintEvent *event);
void CImgPaint::paintEvent(QPaintEvent *event)
{

   QPainter painter(this);
   painter.setRenderHints(QPainter::SmoothPixmapTransform|QPainter::Antialiasing|QPainter::TextAntialiasing);
   painter.drawRect(paint_rect.x()-1,paint_rect.y()-1,paint_rect.width()+1,paint_rect.height()+1); //画框
   painter.drawTiledPixmap(paint_x,paint_y,paint_w,paint_h,pix_fix,pix_sx,pix_sy);//绘图

}

 5. 编写按钮的回调函数

按钮的回调函数主要用来修改变量的值。修改完后,调用update_para、update,重绘。
update_para函数用来确定drawTiledPixmap函数的各参数。

void CImgPaint::on_pushButton_zoomIn_clicked()
{
    ratio = ratio + 0.1*ratio;
    update_para();
    this->update();
}

void CImgPaint::on_pushButton_zoomOut_clicked()
{
    ratio = ratio - 0.1*ratio;
    update_para();
    this->update();
}

void CImgPaint::on_pushButton_Up_clicked()
{
    paint_ye -=step_val;
    update_para();
    this->update();
}

void CImgPaint::on_pushButton_Down_clicked()
{
    paint_ye +=step_val;
    update_para();
    this->update();

}

void CImgPaint::on_pushButton_Left_clicked()
{
    paint_xe -= step_val;
    update_para();
    this->update();

}

void CImgPaint::on_pushButton_Right_clicked()
{
    paint_xe += step_val;
    update_para();
    this->update();

}

void CImgPaint::on_pushButton_reset_clicked()
{
    reset_para();
    update_para();
    this->update();
}

这样,可以通过点击按钮来完成图片的移动、缩放。

6.鼠标拖动、滚轮实现图片拖动及缩放

重写如下虚函数

public slots:
    void wheelEvent(QWheelEvent *event);
    bool event(QEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);
void CImgPaint::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(event->button()==Qt::LeftButton && paint_rect.contains(event->pos()))
    {
        reset_para();
        update_para();
        this->update();
    }
}
bool CImgPaint::event(QEvent *event)
{
    qDebug()<<"event";

    if(event->type() == QEvent::MouseButtonPress )
    {
        QMouseEvent *mouse = dynamic_cast<QMouseEvent* >(event);
        //QPoint temp = mouse->pos();
        if(mouse->button()==Qt::LeftButton && paint_rect.contains(mouse->pos()))
        {
            mouse_press_flag = true;
            QApplication::setOverrideCursor(Qt::OpenHandCursor);
            mouse_pos_pre = mouse->pos();
        }
        qDebug()<<"MouseButtonPress";
    }
    else if(event->type() == QEvent::MouseButtonRelease)
    {
           QMouseEvent *mouse = dynamic_cast<QMouseEvent* >(event);

           //判断鼠标是否是左键释放,且之前是在绘画区域
           if(mouse->button()==Qt::LeftButton && mouse_press_flag )
           {
               QApplication::setOverrideCursor(Qt::ArrowCursor); //改回鼠标样式
               mouse_press_flag=false;
           }
           qDebug()<<"MouseButtonRelease";
    }

    if(event->type() == QEvent::MouseMove)              //移动图片
        {
             if(mouse_press_flag)
            {
               QMouseEvent *mouse = dynamic_cast<QMouseEvent* >(event);
               mouse_pos_cur = mouse->pos();
               int dx = mouse_pos_cur.x() - mouse_pos_pre.x();
               int dy = mouse_pos_cur.y() - mouse_pos_pre.y();
               mouse_pos_pre = mouse_pos_cur;
               paint_xe += dx;
               paint_ye += dy;

               update_para();
               this->update();
            }
             qDebug()<<"MouseMove";
        }
    return QWidget::event(event);
}

void CImgPaint::wheelEvent(QWheelEvent *event)
{

    if (event->delta()>0) //上滑,缩小
    {
        on_pushButton_zoomIn_clicked();
    }
    else //下滑,放大
    {
        on_pushButton_zoomOut_clicked();
    }
    event->accept();
}

可实现鼠标拖动、滚轮的方式来完成图片的拖动、缩放。
源码下载地址(附使用方法):
https://download.csdn.net/download/xiaohuolong1827/78238541

原链接

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

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

相关文章

kafka集群介绍+部署Filebeat+Kafka+ELK

一、消息队列 1、为什么需要消息队列&#xff08;MQ&#xff09; 主要原因是由于在高并发环境下&#xff0c;同步请求来不及处理&#xff0c;请求往往会发生阻塞。比如大量的请求并发访问数据库&#xff0c;导致行锁表锁&#xff0c;最后请求线程会堆积过多&#xff0c;从而触…

安卓Activity上滑关闭效果实现

最近在做一个屏保功能&#xff0c;需要支持如图的上滑关闭功能。 因为屏保是可以左右滑动切换的&#xff0c;内部是一个viewpager 做这个效果的时候&#xff0c;关键就是要注意外层拦截触摸事件时&#xff0c;需要有条件的拦截&#xff0c;不能影响到内部viewpager的滑动处理…

【I.MX6ULL移植】Ubuntu-base根文件系统移植

1.下载Ubuntu16.04根文件系统 http://cdimage.ubuntu.com/ 1 2 3 4 5 2.解压ubuntu base 根文件系统 为了存放 ubuntu base 根文件系统&#xff0c;先在 PC 的 Ubuntu 系统中的 nfs 目录下创建一个名为 ubuntu_rootfs 的目录&#xff0c;命令如下&#xff1a; 【注意&…

C# 学习第五弹——语句

一、if语句 —简单if语句 —if else 语句 —if else if else 语句 1、简单if语句 if&#xff08;表达式&#xff09;{语句} &#xff08;1&#xff09;表达式必须使用圆括号括起来&#xff1b; &#xff08;2&#xff09;表达式&#xff1a;关系表达式或逻辑表达…

数字孪生|初识山海鲸可视化

哈喽,你好啊,我是雷工! 最近开始学习了解数字孪生的软件,看山海鲸可视化介绍的不错,便准备下载了试一下。 01 、概述 该软件是一套技术自主可控的、国产自研的、零代码数字孪生可视化工具集, 02、产品定位 该软件致力于数字孪生应用的推广,通过提供一站式的多快好省…

接口自动化框架搭建(五):生成allure报告

1&#xff0c;安装allure 参考连接&#xff1a; https://blog.csdn.net/lixiaomei0623/article/details/120185069 2&#xff0c;安装python的allure依赖 pip install allure-pytest或者从pycharme上安装 3&#xff0c;生成报告 执行前目录 执行测试用例 import pytest …

pulsar: kafka on pulsar之把pulsar当kafka用

一、下载协议包&#xff08;要和pulsar版本比较一致&#xff09; https://github.com/streamnative/kop/releases?q2.8.0&expandedtrue二、在pulsar的根目录创建一个protocols目录&#xff0c;将上述包放到这个目录里 三、编辑broker.conf(如果是集群)或者standalone.con…

Node爬虫:原理简介

在数字化时代&#xff0c;网络爬虫作为一种自动化收集和分析网络数据的技术&#xff0c;得到了广泛的应用。Node.js&#xff0c;以其异步I/O模型和事件驱动的特性&#xff0c;成为实现高效爬虫的理想选择。然而&#xff0c;爬虫在收集数据时&#xff0c;往往面临着诸如反爬虫机…

前端性能优化:掌握解决方案

课程介绍 我们常说性能永远是第一需求&#xff0c;作为一个前端工程师&#xff0c;不管使用什么框架&#xff0c;不管从事什么类型的网站或应用开发&#xff0c;只要是项目被用户使用&#xff0c;性能优化就永远是你需要关注的问题。通常情况下&#xff0c;工程师们在深入了解…

企业微信机器人java代码对接

如何使用群机器人 假设webhook是&#xff1a;https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa 特别特别要注意&#xff1a;一定要保护好机器人的webhook地址&#xff0c;避免泄漏&#xff01;不要分享到github、博客等可被公开查…

QMT量化策略实盘(二)交易触发定时器run_time

上一篇分享中&#xff0c;介绍了QMT量化实盘中最常用的下单函数passorder&#xff0c;和它主要的参数。 如果再结合一个交易触发函数&#xff0c;就可以实现简单的量化交易策略了&#xff01;比如下面的代码可以实现&#xff1a; 在集合竞价期间以指定价买入中信证券100股 #c…

小狐狸JSON-RPC:钱包连接,断开连接,监听地址改变

detect-metamask 创建连接&#xff0c;并监听钱包切换 一、连接钱包&#xff0c;切换地址&#xff08;监听地址切换&#xff09;&#xff0c;断开连接 使用npm安装 metamask/detect-provider在您的项目目录中&#xff1a; npm i metamask/detect-providerimport detectEthereu…

蓝桥杯23年第十四届省赛真题-填充|DFS,贪心

题目链接&#xff1a; 1.填充 - 蓝桥云课 (lanqiao.cn) 蓝桥杯2023年第十四届省赛真题-填充 - C语言网 (dotcpp.com) 说明&#xff1a; dfs就不再多说了&#xff0c;对于每个?都有0和1两个分支&#xff0c;数据范围是&#xff1a; 那么有m个 ?&#xff0c;时间复杂度就是…

️ 如何掌握服务器硬件基础知识:为高效运维打下坚实基础

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

GTC 2024 火线评论:DPU 重构文件存储访问

编者按&#xff1a;英伟达2024 GTC 大会上周在美国加州召开&#xff0c;星辰天合 CTO 王豪迈在大会现场参与了 GPU 与存储相关的最新技术讨论&#xff0c;继上一篇《GTC 2024 火线评论&#xff1a;GPU 的高效存储利用》之后&#xff0c;这是他发回的第二篇评论文章。 上一篇文章…

网络七层模型之表示层:理解网络通信的架构(六)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

服务器被挖矿了怎么办,实战清退

当我们发现服务器资源大量被占用的时候&#xff0c;疑似中招了怎么办 第一时间重启服务是不行的&#xff0c;这些挖矿木马一定是会伴随着你的重启而自动重启&#xff0c;一定时间内重新霸占你的服务器资源 第一步检查高占用进程 top -c ps -ef 要注意这里%CPU&#xff0c;如果…

企微这个工具太好用,提升企业销售业绩效果好!

在商海浮沉中&#xff0c;销售业绩的提升始终是企业的核心追求。想要把产品卖出去&#xff0c;首要任务便是吸引客户。如今&#xff0c;线上线下的销售模式已然成为主流&#xff0c;短视频社交媒体如抖音、快手等平台更是成为了流量争夺的热门战场。但面对这些平台上的海量且流…

自动发卡平台源码优化版,支持个人免签支付

源码下载地址&#xff1a;自动发卡平台源码优化版.zip 环境要求&#xff1a; php 8.0 v1.2.6◂ 1.修复店铺共享连接时异常问题 2024-03-13 23:54:20 v1.2.5 1.[新增]用户界面硬币增款扣款操作 2.[新增]前台对接库存信息显示 3.[新增]文件缓存工具类[FileCache] 4.[新增]库存同…

营销大师:小米汽车定价的道道!喝酒买车你沾了吗?——早读(逆天打工人爬取热门微信文章解读)

雷神之锤降临&#xff0c;睡不着的是车企&#xff0c;不应该是你 引言Python 代码第一篇 雷军&#xff1a;小米SU7 现已开启定购&#xff5c;人车合一&#xff0c;我心澎湃第二篇 人民日报 来啦新闻早班车要闻社会政策 结尾 “物有所值乃生存之基石&#xff0c;性价比则为选择之…