利用Qt制作一个头像获取剪辑窗口,这里重点说的是可移动双窗口的实现流程。
实现效果如下,通过移动中间的裁剪区域可以获得一张裁剪后的图片。右边是两个预览窗口,并且支持本地图片上传和截图的保存。
下面我主要讲解的是中间裁剪区域双窗口的实现逻辑,至于如何上传图片,如何将截图获取出来,以及如何更改预览图片实现裁剪同步。可以参考一下后续发布的源码。
把中间的区域单独拎出来,是这么个效果:
整个窗口分两部分,一部分是用来显示一张完整的图片,另一个小窗口用来获取目前准备裁剪的图片。
底层窗口上,因为它只是显示一张完整的图片,所以很容易想到在QWidget部件的基础上添加一个QLabel,创建一个自定义部件进行实现。
需要注意的是,它的绘图方式有所不同,正常情况下QWidget的部件绘图是全部高亮的,但是这里发现除了裁剪区域以外的部分都是暗黑色。
因此我们这里要做一个区分,把QWidget中显示图片的label分成正在被裁剪窗口选中的矩形区域和未被选中的矩形区域,其中,被选中的矩形区域,因为需要它随着鼠标的拖动而去移动,所以它也是一个自定义部件。并且这个自定义部件要重写mousePressEvent等鼠标移动的函数。
具体实现如下:
这段代码实现了装载图片显示的整个窗口,重点关注它的重绘事件。需要注意的是,
m_cutDialog是裁剪矩形,也就是一个高亮部分的矩形,这个矩形当然也可以用QWidget去实现,但是需要重写mousePressEvent函数,使它能跟着鼠标拖动,并且设置好它的初始坐标和在整个图片中的初始位置。
#include "photoshotdialog.h"
#include <QPainterPath>
#include <QPainter>
#include <QBrush>
PhotoShotDialog::PhotoShotDialog(QWidget *parent):QWidget(parent)
{
this->setGeometry(0,0,parentWidget()->width(),parentWidget()->height());
m_cutDialog = new CutDialog(this);
m_cutDialog->show();
}
PhotoShotDialog::~PhotoShotDialog()
{
}
void PhotoShotDialog::paintEvent(QPaintEvent *event)
{
QPainterPath painterPath;
QPainterPath p;
p.addRect(x(),y(),this->rect().width(),this->rect().height());//整个矩形
painterPath.addRect(m_cutDialog->geometry());//裁剪矩形
QPainterPath drawPath = p.subtracted(painterPath);//整个矩形中除了裁剪矩形之外的窗口
QPainter painter(this);
painter.setOpacity(0.5);//设置透明度,0表示全透明,1表示不透明,这里是部分透明
painter.fillPath(drawPath,QBrush(Qt::black));
}
QRect PhotoShotDialog::getCutGeometry()
{
return m_cutDialog->geometry();
}
m_cutDialog类实现如下:
这里一开始设置好坐标和大小,坐标我选择放在图片的正中间。
m_mousePress是鼠标是否按下的标志位,一开始设置为false。
#include "cutdialog.h"
#define CUT_WIDTH 100
#define CUT_HEIGHT 100
CutDialog::CutDialog(QWidget *parent) : QWidget(parent)
{
setGeometry(parent->width()/2-CUT_WIDTH/2,parent->height()/2-CUT_HEIGHT/2,CUT_WIDTH,CUT_HEIGHT);
m_mousePress = false;
}
CutDialog::~CutDialog()
{
这段代码的初始化效果如下:
重点是下面的鼠标事件处理函数:
void CutDialog::mousePressEvent(QMouseEvent *event)
{
//鼠标开始的位置
if(event->buttons() == Qt::LeftButton)
{
m_startPoint = event->pos();
m_mousePress = true;
}
}
void CutDialog::mouseMoveEvent(QMouseEvent *event)
{
QPoint dragPoint = event->pos();
if(m_mousePress)
{
QPoint p =QPoint(pos().x()+dragPoint.x()-m_startPoint.x(),pos().y()+dragPoint.y()-m_startPoint.y());
QPoint dragEndge;
dragEndge.setX(p.x()+rect().width());
dragEndge.setY(p.y()+rect().height());
if(p.x()<0)p.setX(0);
if(p.y()<0)p.setY(0);
if(dragEndge.x()>parentWidget()->width())p.setX(parentWidget()->width()-rect().width());
if(dragEndge.y()>parentWidget()->height())p.setY(parentWidget()->height()-rect().height());
move(p);
}
}
void CutDialog::mouseReleaseEvent(QMouseEvent *event)
{
//if(event->buttons() == Qt::LeftButton)
//{
m_mousePress = false;
//}
}
逻辑解释:
在鼠标左键按下的时候,获取鼠标位置为m_startPoint,并将标志位置为ture。
鼠标拖动时,获取鼠标当前的位置,然后得出窗口需要移动的点为:
鼠标当前位置减去鼠标开始位置在加上窗口一开始位于部件的位置。
后面是一些不能超出窗口界限的判断。
最后送开时鼠标标志位置为false即可。