近期在做一个qt项目,在用户选择头像图片后,需要将图片载入,并对其进行用户自定义裁剪。通过研究参照各流行软件的裁剪方式后,发现qq实现的裁剪比较好看,于是,我想那就做一个和qq相似的吧。先放一张qq实现的效果,然后最后再放我实现的效果。
1. 怎样去实现裁剪
对于一张载入的图片,要实现用户自定义裁剪,那么首先我需要有一个能够响应用户自由缩放的边框,当用户缩放到心仪大小,再摆到适当位置,我能知道框的位置及长宽,那么获取框里面的图片就很简单了。另外,我需要阴影,只有在框里面的图像部分是正常的,其他部分均被阴影覆盖。于是,实现该功能,大致需要两件东西,框及阴影。
框的实现可以定义无边框的窗口,如widget,frame,当一个窗口被定义为无边框时,那么该窗口势必无法再响应鼠标的拖动事件,也就是该边框将不能移动,也不能缩放,因此,我们需要重写鼠标事件,实现边框的拖动,以及自由缩放。
阴影的实现可以使用黑色的画笔,并设置透明度,去填充边框之外的区域。
2. 缩放边框的实现
要想缩放边框实现拖动及自由缩放,那么需要重写该边框的mousePressEvent、mouseMoveEvent、mouseReleaseEvent三个事件。具体见如下代码
2.1对mousePressEvent的重写
m_startPoint 用户记录鼠标点击下的事件, 而后期只需要算出移动时鼠标的坐标与初始坐标的差值,进行移动即可。代码如下:
void CutDialog::mousePressEvent(QMouseEvent * event) { m_startPoint = event->pos(); m_mouse_down = event->button() == Qt::LeftButton; }
2.2 对mouseMoveEvent 的重写
在鼠标移动过程中,获取鼠标当前的位置,判断鼠标当前状态,如果是落在边框上,则改变鼠标形状成对应的边界鼠标形状,如果落在窗口区域内,则改变鼠标形状为移动形状。如果鼠标在移动之前有点击事件,则进行相应判断,决定是移动还是放大缩小。
void CutDialog::mouseMoveEvent(QMouseEvent * event) { QPoint dragPoint = event->pos(); int x = event->x(); int y = event->y(); if(m_mouse_down) { QRect g = getResizeGem(geometry(), dragPoint);
<span style="white-space:pre"> </span>//实现对当前窗口的拖放 if(parentWidget()->rect().contains(g)) setGeometry(g); m_startPoint = QPoint(!m_right? m_startPoint.x():event->x(),!m_bottom? m_startPoint.y():event->y()); <span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>//实现对当前的移动 if(!m_left && !m_right && !m_bottom && !m_top) { QPoint p = QPoint((pos().x()+dragPoint.x() - m_startPoint.x()), (pos().y()+dragPoint.y() - m_startPoint.y())); QPoint dragEndge = p; dragEndge.setX(dragEndge.x() + rect().width()); dragEndge.setY(dragEndge.y() + rect().height()); p.setX(p.x() < 0? 0 : p.x()); p.setX(dragEndge.x() > parentWidget()->width()?parentWidget()->width()-rect().width():p.x()); p.setY(p.y() < 0? 0 : p.y()); p.setY(dragEndge.y() > parentWidget()->height()?parentWidget()->height()-rect().height():p.y()); move(p); } } else {
<span style="white-space:pre"> </span>//根据位置判断相应的鼠标形状 QRect r = rect(); m_left = qAbs(x - r.left()) < 5; m_right = qAbs(x - r.right()) < 5; m_bottom = qAbs(y - r.bottom()) < 5; m_top = qAbs(y - r.top()) < 5; bool lorr = m_left | m_right; bool torb = m_top | m_bottom; if(lorr && torb) { if((m_left && m_top) || (m_right && m_bottom)) { setCursor(Qt::SizeFDiagCursor); } else setCursor(Qt::SizeBDiagCursor); } else if(lorr) setCursor(Qt::SizeHorCursor); else if(torb) setCursor(Qt::SizeVerCursor); else { setCursor(Qt::SizeAllCursor); m_bottom = m_left = m_right = m_top = false; } } }
实现鼠标在移动过程中获得新的鼠标欲拉伸的大小,在这里需要注意的是,因为剪切的图片应该是正方形,所以,在改变大小时长和宽都要做等比例的缩放。代码实现如下:
QRect CutDialog::getResizeGem(QRect oldgeo, QPoint mousePoint) { QRect g = oldgeo; bool lorr = m_left | m_right; bool torb = m_top | m_bottom; int dx = mousePoint.x() - m_startPoint.x(); int dy = mousePoint.y() - m_startPoint.y(); if(lorr && torb) { int maxLen = qMin(qAbs(dx),qAbs(dy)); if(m_left && m_top && dx*dy >0) { g.setLeft(dx >0 ?g.left() + maxLen : g.left() - maxLen); g.setTop(dy >0? g.top() + maxLen : g.top() - maxLen); } if(m_right && m_top && dx*dy < 0) { g.setRight(dx>0 ? g.right() + maxLen:g.right() - maxLen); g.setTop(dy >0? g.top() + maxLen : g.top() - maxLen); } if(m_right && m_bottom && dx*dy > 0) { g.setRight(dx>0 ? g.right() + maxLen:g.right() - maxLen); g.setBottom(dy >0? g.bottom() + maxLen : g.bottom() - maxLen); } if(m_left && m_bottom && dx*dy < 0) { g.setLeft(dx >0 ?g.left() + maxLen : g.left() - maxLen); g.setBottom(dy >0? g.bottom() + maxLen : g.bottom() - maxLen); } return g; } else if(lorr) { if(m_left) g.setLeft(g.left() + dx); if(m_right) g.setRight(g.right() + dx); int len = g.width() - oldgeo.width(); int intHight = (int) len/2.0; g.setTop(g.top() - intHight); g.setBottom(g.bottom() + len - intHight); } else if(torb) { if(m_bottom) g.setBottom(g.bottom() + dy); if(m_top) g.setTop(g.top() + dy); int dheigt = g.height() - oldgeo.height(); int intWidth = (int) dheigt/2.0; g.setLeft(g.left() - intWidth); g.setRight(g.right() + dheigt - intWidth); } return g; }
2.3 对mouseReleaseEvent的重写
void CutDialog::mouseReleaseEvent(QMouseEvent * event) { m_mouse_down = false; }
3. 阴影的实现
阴影的实现需要创建新的窗口类,并在该窗口类中初始化上面的缩放边框,并对边框外的区域进行阴影填充。代码如下:
void PhotoShotDialog::paintEvent(QPaintEvent *e) { QPainterPath painterPath; QPainterPath p; p.addRect(x(),y(),rect().width(),rect().height()); painterPath.addRect(dialog->geometry()); QPainterPath drawPath =p.subtracted(painterPath); QPainter paint(this); paint.setOpacity(0.7); paint.fillPath(drawPath,QBrush(Qt::black)); }
4. 切图
切图只需使用到QPixmap的copy函数即可,函数参数即是窗口的位置及大小。
QPixmap pix = scaledPix.copy(pdialog->getShotGeometry()); pix.save("C:/Users/dana/Desktop/1.png","png");
若有下载源码的需要,请戳:http://download.csdn.net/detail/u010511236/9197633
最终实现效果如下: