近期在做一个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/8462929
最终实现效果如下: