如题,如何使用Qt抠一个圆形头像出来?
先来看效果:
首先加载一张图片,显示一个透明圆形,圆形外半透明,滚动鼠标滚轮,圆形区域变大变小。
鼠标按下可以拖动图片移动,来选定要截取的图片位置,按下”剪切并保存“按钮后,将会截取圆形区域下的图片,得到一张圆形图片。比如:
来看一下运行效果:
实现原理如下:
这个窗口使用的是QGraphicsView
,其中图片是继承自QGraphicsPixmapItem
的custompixmap
,这里重新实现了它的itemChange
方法,以限制图片的移动范围:
QVariant custompixmap::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionChange && scene()) // 控件发生移动 { QPointF newPos = value.toPointF(); //即将移动的位置 //限制的区域 QRectF rect(-scene()->width() / 3, -scene()->width() / 3, (scene()->width() / 3) * 2, (scene()->width() / 3) * 2); SHOW(rect) if (!rect.contains(newPos)) // 是否在区域内 { newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left()))); newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top()))); return newPos; } } return QGraphicsItem::itemChange(change, value); }
其中需要注意的是限制的区域的设定,比如这里的
QRectF rect(-scene()->width() / 3, -scene()->width() / 3, (scene()->width() / 3) * 2, (scene()->width() / 3) * 2);
这里scene
的宽度为220,输出rect看一下:
QRectF(-73.3333,-73.3333 146.667x146.667)
那这个矩形的范围大概也就是这样:
也就是图片的左上角只能在这个矩形区域内活动,也就限制了图片移动的范围。
再来看圆形区域的绘制:
这里定义一个CustomView
继承自QGraphicsView
,重新实现它的paintEvent
和wheelEvent
方法:
int radius = 150;//半径 void CustomView::paintEvent(QPaintEvent *e) { //https://www.it610.com/article/3860174.htm //先调用QGraphicsView::paintEvent(e);再画自己的 QGraphicsView::paintEvent(e); //https://www.codenong.com/cs106784396/ QPainter painter(this->viewport()); QPainterPath path1; //构建圆形路径 mPoint.setX((width() / 2) - radius / 2); mPoint.setY((height() / 2) - radius / 2); path1.addEllipse(mPoint.x(), mPoint.y(), radius, radius); SHOW(radius) //设置颜色为半透明 QColor color(192, 192, 192, 150); //填充除去圆形的范围 painter.setBrush(QBrush(color)); QPainterPath path; //整个view的路径 path.addRect(rect()); //path减去圆形的范围 path -= path1; painter.setClipPath(path); //画矩形,注意,这里把圆形范围剔除了 painter.drawRect(rect()); } void CustomView::wheelEvent(QWheelEvent *event) { QGraphicsView::wheelEvent(event); //限制圆形半径范围为[150,180] if(event->delta() > 0 && (radius - 10) >= 150){// 当滚轮远离使用者时 radius -= 10; }else if (event->delta() < 0 && (radius + 10) <= 180){// 当滚轮向使用者方向旋转时 radius += 10; } //https://www.codenong.com/cs106784396/ this->viewport()->update(); }
在paintEvent
方法中填充一个半透明区域,注意要通过setClipPath
减去一个圆形区域。在wheelEvent
方法中根据滚轮,改变半径大小,并通过this->viewport()->update();
刷新界面,重新绘制减去圆形区域的半透明区域,从而达到缩放圆形的效果。
当按下”剪切并保存“按钮后,根据当前圆形区域所在点的坐标及半径,并通过grab
方法截取当前窗体这块区域:
void CustomView::grabPicture() { SHOW(mPoint) //按照圆形所在矩形范围,截取当前截图 QPixmap pixmap = grab(QRect(mPoint.x(), mPoint.y(), radius, radius)); //截完后,扣圆形图片 pixmap = PixmapToRound(pixmap, radius); //保存图片 pixmap.save("here.png"); }
可以发现,这时截取的图片还不是圆形:
这里参考网上的方法,根据传入圆形区域的半径,抠取一个圆形图片:
//网上的方法,扣一个圆形图片出来 QPixmap PixmapToRound(const QPixmap &src, int radius) { if (src.isNull()) { return QPixmap(); } //构建一个透明的图片 QPixmap pixmap(radius,radius); pixmap.fill(Qt::transparent); //在透明的图片上画 QPainter painter(&pixmap); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); QPainterPath path; //画的范围为一个圆形 path.addEllipse(0, 0, radius, radius); painter.setClipPath(path); //画图片 painter.drawPixmap(0, 0, radius, radius, src); //返回这个图片 return pixmap; }
抠完后便得到了一个周围透明,形状为圆形的图片,保存即可!
所以,总结下来,前面做的工作都是根据用户鼠标的拖动及滚动确定要截取的图片大小及位置,之后通过截图截出一个小范围的图片,再根据圆形区域半径,抠一个圆形图片出来。嗯,就是酱~
正文结束。
下面介绍一种好用的调试方法:
我们可以新建一个common.h
文件,里面定义一个调试用的宏:
#ifndef COMMON_H #define COMMON_H #ifdef DEBUG #include <QDebug> #endif #ifdef DEBUG #define SHOW(x) qDebug() << #x << " : " << (x); #else #define SHOW(x) #endif #endif // COMMON_H
在Qt的pro文件中,配置在debug模式下,定义一个DEBUG宏:
CONFIG(debug, debug|release) { DEFINES += DEBUG }
这样,在需要输出某个变量值的时候,只需要引入common.h
头文件,在需要打印变量时使用SHOW即可,比如:
#include "common.h" int a = 1; SHOW(a)
这样做的一个好处是,输出只在debug模式下才有,release模式下不会产生输出,并且不会额外增大源文件。
完~
最后来抠一个小姐姐的头像:
项目地址:https://gitee.com/gao-yuelong/qtdemo/tree/master/cutpicture
欢迎下载试玩!