1. 事件和函数
主要使用事件paintEvent(QPaintEvent *event)和drawTiledPixmap函数实现绘图。
- paintEvent事件在改变窗口大小、移动窗口、手动调用update等情形下会被调用。
- 需先了解下绘图该函数的用法。 - QPainter::drawTiledPixmap(int x, int y, int w, int h, const QPixmap &pm, int sx, int sy)
- 效果:
2. 建立项目
假设已新建了mainwindow项目。现再添加一界面用于单独显示图片,作为子窗口供调用(当然,也可在main.c直接调用,作为父窗口)。
1)右击界面文件---添加新文件---QT---QT设计师界面类---choose---Dialog without buttons。类名为CImgPaint。
打开ui文件,添加控件,主要是label和button,
具体属性列表如下:
其中,label控件仅仅是提供绘图的区域,但最终绘图不是绘在label控件上。
3. 头文件添加变量,编写构造函数
头文件中包含头文件
cpp
#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;//记录鼠标位置
构造函数中,添加输入图片的读取,以及变量初始化。
cpp
#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函数的各参数。
cpp
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文件中重写该事件函数
cpp
public slots:
void paintEvent(QPaintEvent *event);
cpp
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函数的各参数。
cpp
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.鼠标拖动、滚轮实现图片拖动及缩放
重写如下虚函数
cpp
public slots:
void wheelEvent(QWheelEvent *event);
bool event(QEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
cpp
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();
}
可实现鼠标拖动、滚轮的方式来完成图片的拖动、缩放。
源码下载地址(附使用方法):