如何用Qt实现头像剪辑窗口---手把手系列

利用Qt制作一个头像获取剪辑窗口,这里重点说的是可移动双窗口的实现流程。

bfeb861c43294b8bb4afe3d2d7d51f54.png

实现效果如下,通过移动中间的裁剪区域可以获得一张裁剪后的图片。右边是两个预览窗口,并且支持本地图片上传和截图的保存。

下面我主要讲解的是中间裁剪区域双窗口的实现逻辑,至于如何上传图片,如何将截图获取出来,以及如何更改预览图片实现裁剪同步。可以参考一下后续发布的源码。

把中间的区域单独拎出来,是这么个效果:

998caf02da1d4850a31d5d4bae74ad66.png

整个窗口分两部分,一部分是用来显示一张完整的图片,另一个小窗口用来获取目前准备裁剪的图片。

底层窗口上,因为它只是显示一张完整的图片,所以很容易想到在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()
{

这段代码的初始化效果如下:

92f53ef678674da5b087d985cdf7df8c.png

重点是下面的鼠标事件处理函数:

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。

鼠标拖动时,获取鼠标当前的位置,然后得出窗口需要移动的点为:

鼠标当前位置减去鼠标开始位置在加上窗口一开始位于部件的位置。

dd8edf12ed12410591f7aef22dfdb783.png

后面是一些不能超出窗口界限的判断。

最后送开时鼠标标志位置为false即可。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值