Qt 完成图片的缩放拖动

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

原链接

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入并广泛了解Redis常见的缓存使用问题

Redis 作为一门主流技术&#xff0c;缓存应用场景非常多&#xff0c;很多大中小厂的项目中都会使用redis作为缓存层使用。 但是Redis作为缓存&#xff0c;也会面临各种使用问题&#xff0c;比如数据一致性&#xff0c;缓存穿透&#xff0c;缓存击穿&#xff0c;缓存雪崩&#…

k8s下搭建redis集群

记录一下近期实现的在k8s上搭建redis集群的过程 1、新建存储类 主要是为了和其它服务的存储类区分一下 redis-beta-storage 2、编写configMap redis启动时从configMap中读取配置 bind&#xff1a;默认的127.0.0.1可能会导致其它ip地址无法远程访问&#xff0c;因此修改为0.0…

stm32定时器中断函数回调函数

方式一&#xff1a;stm32定时器中断可以直接在硬件中断函数TIM3_IRQHandler执行。 在HAL库中可以注册回调函数&#xff0c;在定时器中断发生时调用注册的函数&#xff0c;这样可以统一接口&#xff0c;大大提高函数可读性&#xff0c;和硬件解耦提高程序可移植性。 使用过程如…

用Unity制作正六边形拼成的地面

目录 效果演示 1.在Unity中创建正六边形 2.创建一个用于管理正六边形的类 3.创建一个用于管理正六边形地面的类 4.创建一个空对象并将游戏控制脚本挂上 5.设置正六边形碰撞所需组件 6.创建正六边形行为触发脚本并挂上 7.创建圆柱体——田伯光 8.创建圆柱体移动脚本 运…

搜索与图论——bellman—ford算法、spfa算法求最短路

bellman-ford算法 时间复杂度O(nm) 在一般情况下&#xff0c;spfa算法都优于bf算法&#xff0c;但遇到最短路的边数有限制的题时&#xff0c;只能用bf算法 bf算法和dijkstra很像 #include<iostream> #include<queue> #include<cstring> #include<algori…

华清远见STM32U5开发板助力2024嵌入式大赛ST赛道智能可穿戴设备及IOT选题项目开发

第七届&#xff08;2024&#xff09;全国大学生嵌入式芯片与系统设计竞赛&#xff08;以下简称“大赛”&#xff09;已经拉开帷幕&#xff0c;大赛的报名热潮正席卷而来&#xff0c;高校电子电气类相关专业&#xff08;电子、信息、计算机、自动化、电气、仪科等&#xff09;全…

用navicat进行mysql表结构同步

用navicat进行mysql表结构同步 前言新增一个列然后进行表结构同步删除一个列然后进行表结构同步把Int列转成TinyInt列&#xff0c;看数字溢出的情况下能不能表结构同步总结 前言 从同事那边了解到还能用navicat进行表结构同步&#xff0c;他会在发布更新的时候&#xff0c;直接…

Reasoning on Graphs: Faithful and Interpretable Large Language Model Reasonin

摘要 大型语言模型(llm)在复杂任务中表现出令人印象深刻的推理能力。然而&#xff0c;他们在推理过程中缺乏最新的知识和经验幻觉&#xff0c;这可能导致不正确的推理过程&#xff0c;降低他们的表现和可信度。知识图谱(Knowledge graphs, KGs)以结构化的形式捕获了大量的事实…

在哪买国外服务器便宜?

在哪买国外服务器便宜&#xff1f;在寻找便宜且可靠的国外服务器商家时&#xff0c;我们需要考虑多个因素&#xff0c;包括价格、性能、可靠性、技术支持和扩展性等。下面是一些备受推崇的便宜国外服务器商家。 Amazon Web Services (AWS)。作为全球最大的云服务提供商之一&am…

WebSocket 详解-小案例展示

简介&#xff1a;Websocket是一种用于H5浏览器的实时通讯协议&#xff0c;可以做到数据的实时推送&#xff0c;可适用于广泛的工作环境&#xff0c;例如客服系统、物联网数据传输系统&#xff0c;该测试工具可用于websocket开发初期的测试工作。 文章末尾有此案例的完整源代码。…

从vivo X Fold3看vivo“质”变

撰文 | 何玺 排版 | 叶媛 vivo的气质变了&#xff01;虽然依旧内敛、低调&#xff0c;但更自信、从容&#xff0c;气场也更强大。这是玺哥在本次vivo X Fold3系列新品发布会上的一个直观感受。 是什么改变了vivo的气质&#xff1f;产品&#xff1f;技术&#xff1f;又或是其他…

基于STC12C5A60S2系列1T 8051单片机通过单个按键单击次数实现开关机应用

基于STC12C5A60S2系列1T 8051单片机通过单个按键单击次数实现开关机应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍基于STC12C5A60S2系列1T 8051单片机通过单个按…

代码随想录算法训练营第二十四天(回溯1)|77. 组合(JAVA)

文章目录 回溯理论基础概念类型回溯模板 77. 组合解题思路源码 回溯理论基础 概念 回溯是递归的副产品&#xff0c;本质上是一种穷举 回溯解决的问题可以抽象为一种树形结构 类型 回溯主要用来解决以下问题 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合切割问…

eNSP综合实验(PPP认证、VPN配置、RIP协议、NAT)

题目如上 第一步:配置IP地址 ip分配如下图所示 开始配置IP(PC省略&#xff09; R1&#xff1a; [R1]undo [R1]undo in [R1]undo info-centere [R1]undo info-center e [R1]undo info-center enable Info: Information center is disabled. [R1]int g0/0/0 [R1-Gigabit…

2024年MathorCup数学建模思路A题B题C题D题思路分享

文章目录 1 赛题思路2 比赛日期和时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

蓝桥杯-岛屿个数

solution 数1的块数题&#xff0c;加了内岛不计入的小限制&#xff0c;以及输入数据间没有空格&#xff0c;不是矩阵形式的整数输入。 判断是否是子岛屿&#xff1a;如果该岛屿有边缘部分或者可以通过外海走到边缘部分说明不是子岛屿 #include<iostream> #include<…

蓝桥备赛——堆队列

AC code import os import sys import heapq a [] b [] n,k map(int,input().split())for _ in range(n):x,y map(int,input().split())a.append(x)b.append(y) q []# 第一种情况&#xff1a;不打第n个怪兽# 将前n-1个第一次所需能量加入堆 for i in range(n-1):heapq.h…

科普——芯片的市场价格

以前以为进口的芯片会很贵&#xff0c;其实不然&#xff0c;均在很低&#xff0c;粗略找了几个&#xff0c;批发价格在50元上下 厂商型号&#xff1a;STM32F103RCT7 品牌名称&#xff1a;ST(意法半导体) 元件类别&#xff1a;MCU 封装规格&#xff1a;64-LQFP&#xff08;10x1…

View事件分发

MotionEvent 1.简介 MotionEvent 是Android系统中一个非常重要的类&#xff0c;它代表了屏幕上发生的触摸事件。当用户在屏幕上触摸、滑动或者长按时&#xff0c;都会生成一个MotionEvent对象&#xff0c;这个对象包含了触摸动作的各种信息。 2.事件类型 ACTION_DOWN&#x…

沃尔玛百货有限公司 企业网页设计制作 企业html网页成品 跨国公司网页设计开发 web前端开发,html+css网页设计素材,静态html学生网页成品源码

沃尔玛百货有限公司 WalMart 7页面 企业主题 带jquery图片轮播特效 滚动文字 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.or…