想必大家都使用过qt的自定义头像功能吧,那么图1应该不会陌生,本片文章我就是要模拟一个这样的功能,虽然没有这么强大的效果,但是能够满足一定的需求。

图1 qq上传图片

首先在讲解功能之前,我先给出一片文章,QT实现的类似QQ的头像选择框,这篇文章也是讲解头像上传功能的,而我自己的代码是从这个demo中优化而来,不仅对代码进行了重构,而且处理了快速拖动时,边框消失的问题。使用事件和双缓冲来尽量减少重新绘制的几率。接下里我会一步一步进行讲解,怎么实现图片自定义截取功能。
一、概要
首选,我给出4个类,并给出他们的解释
1、PicturePreviewPanel:图标展示框,控件基类
2、BackgroundWidget:阴影窗口,是PicturePreviewPanel子类
3、CutShape:交互图形基类,实现了拖拽和放大缩小功能
4、CutRound:圆形剪贴,父类为CutShape,实现父类的paintInit接口重绘
5、CutRectangle:矩形剪贴,父类为CutShape,实现父类的paintInit接口重绘
二、详情
理解了上述5个类之后,接下来我分别讲解下类的头文件和重要的实现接口,其中自定义图形类也可以自己在扩充
头文件代码如下,有部分注释,代码应该不难理解,开发过程中只需要声明PicturePreviewPanel类,并调用期LoadPicture接口就可以加载图片,并进行图片剪切

 #include <QWidget>

 class QLabel;

 enum ShapeType
{
Rect,//矩形
Round,//圆形
}; //剪贴图基类 实现了基本的交互功能,并绘制了部分图案,主要的团绘制在子类实现,通过实现paintInit接口
class CutShape : public QWidget
{
public:
CutShape(QWidget * parent = nullptr);
~CutShape(); public:
QPainterPath CutRegion(); protected:
//QWidget
virtual void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
virtual void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE;
virtual void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE;
virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE;
virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; virtual bool paintInit(QPaintEvent *, QPaintDevice *) = ;
virtual QPainterPath Region(){ return QPainterPath(); }; protected:
ShapeType m_Type;
bool m_MouseDown = false;
bool m_IsMoving = false;
bool m_IsFirst = true;
int border = ; private:
QRect getResizeGem(QRect oldgeo, QPoint mousePoint, bool & ignore); private:
bool m_EnableRepaint = true;
bool m_Left = false;
bool m_Right = false;
bool m_Bottom = false;
bool m_Top = false;
QPoint m_startPoint;
QPoint m_old_pos;
QLabel * m_Label;
QPixmap m_BufferPix;
}; class CutRectangle : public CutShape
{
public:
CutRectangle(QWidget * parent = nullptr);
~CutRectangle(); protected:
//CutShape
virtual bool paintInit(QPaintEvent *, QPaintDevice *) Q_DECL_OVERRIDE;
virtual QPainterPath Region() Q_DECL_OVERRIDE;
}; class CutRound : public CutShape
{
public:
CutRound(QWidget * parent = nullptr);
~CutRound(); protected:
//CutShape
virtual bool paintInit(QPaintEvent *, QPaintDevice *) Q_DECL_OVERRIDE;
virtual QPainterPath Region() Q_DECL_OVERRIDE; private:
}; class BackgroundWidget : public QWidget
{
public:
BackgroundWidget(QWidget * parent = nullptr, ShapeType type = Round);
~BackgroundWidget(); public:
void PictureLoadFinished(); protected:
virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; private:
ShapeType m_Type;
CutShape * m_CutShape = nullptr;
}; class PicturePreviewPanel : public QWidget
{
Q_OBJECT public:
PicturePreviewPanel(QWidget * parent = nullptr);
~PicturePreviewPanel(); public:
void LoadPicture(const QString & filepath);//加载图片 protected:
virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; private:
void InitializeUI();
void LoadPicture_p(); private:
QString m_PicturePath;
QLabel * m_PictureContainer = nullptr;
BackgroundWidget * m_BgWidget = nullptr;
};

上述头文件,如果觉得麻烦了也可以不具体细看接口,只要理解了标题1下边的5个类的作用就可以了。下边我分别说下交互图形类、背景色类和显示图片类(即控件基类)这三者直接的一些关系,具体如下:

1、CutShape剪贴图基类 实现了基本的交互功能,并绘制了部分图案,主要的绘制在子类实现,通过实现paintInit接口,该接口在剪切图街基类重新绘制时调用,代码如下:

 void CutShape::paintEvent(QPaintEvent * event)
{
QPainter paint;
if (m_EnableRepaint && paintInit(event, &m_BufferPix))
{
m_EnableRepaint = false;
qDebug() << "event->type()" << event->type();
m_BufferPix = QPixmap(size());
m_BufferPix.fill(Qt::transparent); paint.begin(&m_BufferPix);
QPen pen0;
pen0.setColor(QColor(, , , ));
pen0.setWidth();
paint.setPen(pen0);
paint.drawRect(border, border, width() - border * , width() - border * ); QPen pen;
QVector<qreal> dashes;
qreal space = ;
dashes << << space << << space;
pen.setDashPattern(dashes);
pen.setColor(Qt::white); paint.setPen(pen);
int x_pos = (int)width() / 3.0;
int y_pos = (int)height() / 3.0;
paint.drawLine(x_pos, border, x_pos, height() - * border);
paint.drawLine( * x_pos, border, * x_pos, height() - * border);
paint.drawLine(border, y_pos, width() - * border, y_pos);
paint.drawLine(border, * y_pos, width() - * border, * y_pos);
paint.end();
}
paint.begin(this);
paint.drawPixmap(rect(), m_BufferPix);
paint.end();
}

上边是主要的绘制过程,关于剪切图形的交互功能,我给出具体的大姨妈,就不仔细讲解功能了,代码有些长,如下:

 void CutShape::mousePressEvent(QMouseEvent * event)
{
m_startPoint = event->pos();
m_MouseDown = event->button() == Qt::LeftButton;
} void CutShape::mouseMoveEvent(QMouseEvent * event)
{
QPoint dragPoint = event->pos();
if (!parentWidget()->rect().contains(mapToParent(dragPoint)))
{
return;
}
int x = event->x();
int y = event->y();
if (m_MouseDown)
{
if (m_Left == false && m_Right == false
&& m_Bottom == false && m_Top == false)
{
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() < ? : p.x());
p.setX(dragEndge.x() > parentWidget()->width() ? parentWidget()->width() - rect().width() : p.x());
p.setY(p.y() < ? : p.y());
p.setY(dragEndge.y() > parentWidget()->height() ? parentWidget()->height() - rect().height() : p.y());
move(p);
}
else
{
bool ignore = false;
QRect g = getResizeGem(geometry(), dragPoint, ignore);
if (parentWidget()->rect().contains(g))
setGeometry(g);
if (ignore == false)
{
m_startPoint = QPoint(!m_Right ? m_startPoint.x() : event->x(), !m_Bottom ? m_startPoint.y() : event->y());
}
}
}
else
{
QRect r = rect();
m_Left = qAbs(x - r.left()) < ;
m_Right = qAbs(x - r.right()) < ;
m_Bottom = qAbs(y - r.bottom()) < ;
m_Top = qAbs(y - r.top()) < ;
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;
}
}
} void CutShape::mouseReleaseEvent(QMouseEvent * event)
{
m_MouseDown = false;
} void CutShape::resizeEvent(QResizeEvent *event)
{
m_EnableRepaint = true;
update(); QWidget::resizeEvent(event);
} QRect CutShape::getResizeGem(QRect oldgeo, QPoint mousePoint, bool & ignore)
{
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();
ignore = false;
if (lorr && torb)
{
int maxLen = qMax(qAbs(dx), qAbs(dy));
if (m_Left && m_Top && dx*dy > )
{
g.setLeft(dx > ? g.left() + maxLen : g.left() - maxLen);
g.setTop(dy > ? g.top() + maxLen : g.top() - maxLen);
}
else if (m_Right && m_Top && dx * dy < )
{
g.setRight(dx > ? g.right() + maxLen : g.right() - maxLen);
g.setTop(dy > ? g.top() + maxLen : g.top() - maxLen);
}
else if (m_Right && m_Bottom)
{
if (dx * dy > )
{
g.setRight(dx > ? g.right() + maxLen : g.right() - maxLen);
g.setBottom(dy > ? g.bottom() + maxLen : g.bottom() - maxLen);
}
else if (dx == && dy !=
/*|| dx != 0 && dy == 0*/)
{
ignore = true;
}
}
else if (m_Left && m_Bottom && dx*dy < )
{
g.setLeft(dx > ? g.left() + maxLen : g.left() - maxLen);
g.setBottom(dy > ? 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);
}
else
{
ignore = true;
}
return g;
}

2、BackgroundWidget背景色窗口,该窗口作为剪切窗口的父类窗口,但是没有布局,目的就是可以让剪切窗口自由的移动,并保持在背景色窗口之上,当背景色窗口重新绘制的时候,只绘制除剪切窗口以外的部分。这样就实现了剪切窗口是透明的但是其余部分都是半透明的,代码如下:

 void BackgroundWidget::paintEvent(QPaintEvent *)
{
QPainterPath painterPath;
QPainterPath p;
p.addRect(x(), y(), rect().width(), rect().height());
if (m_CutShape)
{
painterPath.addPath(m_CutShape->CutRegion().translated(m_CutShape->pos()));
}
QPainterPath drawPath = p.subtracted(painterPath); QPainter paint(this);
paint.setOpacity(0.5);
paint.fillPath(drawPath, QBrush(Qt::black));
}

3、PicturePreviewPanel图片上传控件基类,当图片加载后,需要重置背景色窗口的大小,以便覆盖到图片之上,代码如下:

 bool PicturePreviewPanel::eventFilter(QObject * watched, QEvent * event)
{
if (watched == m_PictureContainer)
{
if (event->type() == QEvent::Resize)
{
LoadPicture_p();
if (m_BgWidget)
{
m_BgWidget->resize(m_PictureContainer->size());
}
}
}
return QWidget::eventFilter(watched, event);
}

之所以需要重新加载图片是,放置图片因为拖拉而失真,图片的加载的加载代码如下:

 void PicturePreviewPanel::LoadPicture_p()
{
QPixmap picture;
picture.load(m_PicturePath);
if (!picture.isNull())
{
picture = picture.scaled(m_PictureContainer->width(), m_PictureContainer->height());
m_PictureContainer->setPixmap(picture);
m_BgWidget->PictureLoadFinished();
}
}

好了,有了上边的代码之后,这个图片上传空间的基本交互功能就完成了,点击保存截取图片,可以使用剪贴图基类的CutRegion方法获取剪贴的路径,并保存成指定图片格式。效果如图2所示

图2 效果展示

实例代码下载:http://download.csdn.net/detail/qq_30392343/9581238

如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

 

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。

qt实现头像上传功能的更多相关文章

  1. 【Bootstrap-插件使用】Jcrop+fileinput组合实现头像上传功能

    作者:Dreawer链接:https://zhuanlan.zhihu.com/p/24465742来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者:梦游的龙猫(转 ...

  2. [Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能

    很久没有更新博客了,再不写点东西都烂了. 这次更新一个小内容,是两个插件的组合使用,实现头像上传功能. 业务需求: 头像上传功能,要对上传的文件进行剪切,且保证头像到服务器时必须是正方形的. 优化&l ...

  3. springboot整合项目-商城个人头像上传功能

    上传头像的功能 持久层 1.sql语句的规划 avatar varchar(50) str - 字节流 将对象文件保存在操作系统上,然后在把这个文件的路径个记录下来,保存在avatar中,因为相比于字 ...

  4. php实现头像预览上传功能

    最近在做php第二阶段的项目,需要用到头像上传的功能 我们要完成头像上传功能,一共要写两个php页面,第一个页面我们叫做touxiang.php,第二个页面我们叫做upload.php 1.touxi ...

  5. qt实现头像上传功能(写了4个类,朝十晚八的博客,非常好)

    想必大家都使用过qt的自定义头像功能吧,那么图1应该不会陌生,本片文章我就是要模拟一个这样的功能,虽然没有这么强大的效果,但是能够满足一定的需求. 图1 qq上传图片 首先在讲解功能之前,我先给出一片 ...

  6. 【javascript】html5中使用canvas编写头像上传截取功能

    [javascript]html5中使用canvas编写头像上传截取功能 本人对canvas很是喜欢,于是想仿照新浪微博头像上传功能(前端使用canvas) 本程序目前在谷歌浏览器和火狐浏览器测试可用 ...

  7. JSP+SpringMVC框架使用WebUploader插件实现注册时候头像图片的异步上传功能

    一.去官网下载webuploader文件上传插件 https://fex.baidu.com/webuploader/ 下载好后把它放到Javaweb项目的文件夹中(我放到了webcontent下面的 ...

  8. 强大的flash头像上传插件(支持旋转、拖拽、剪裁、生成缩略图等)

    今天介绍的这款flash上传头像功能非常强大,支持php,asp,jsp,asp.net 调用 头像剪裁,预览组件插件. 本组件需要安装Flash Player后才可使用,请从http://dl.pc ...

  9. Canvas处理头像上传

    未分类 最近社区系统需要支持移动端,其中涉及到用户头像上传,头像有大中小三种尺寸,在PC端,社区用Flash来处理头像编辑和生成,但该Flash控件的界面不友好而且移动端对Flash的支持不好,考虑到 ...

随机推荐

  1. 难受的ESlint语法检测

    相信写过vue的各位小白都有过这样的体验,明明引入的文件语法是对的,明明自己写的代码是对的,但是总会报语法错误,没错,就是ESlint代码检测搞的鬼, 就算你在注释后面多打一个空格,它都会去搞事情,简 ...

  2. SVM:根据大量图片来精确实现人脸识别—Jason niu

    from __future__ import print_function from time import time import logging import matplotlib.pyplot ...

  3. 简单使用WebSocket实现聊天室

    环境需求:flask,websocket第三方包 目录结构 web中实现群聊 ws_群聊.py文件 # 实现一个websocket 先下载包 gevent-websocket from flask i ...

  4. 一小时学会ECMAScript6新特性(一)

    ECMAScript 简介 简称es,是一套标准,javascript就是使用这套标准的语言.主流的浏览器使用的是ECAMScript5,ECAMScript6(ECAMScript2015)是一涛新 ...

  5. linux shell 发送qq邮件失败

    安装 发送邮件功能 yum -y install postfix yum -y install mailx 使用 mail -s “fsfds” @qq.com < hh 第二次返回此信息 您在 ...

  6. CI 框架 隐藏index.php 入口文件 和 设置访问application下子目录

    1.隐藏根目录下 index.php, 在根目录下创建 .htaccess文件 内容如下: <IfModule mod_rewrite.c> RewriteEngine on Rewrit ...

  7. 微信跳转ticket值怎么得到?浏览器跳到微信?哪里有微信跳转接口?跳转功能能用多久?

    目前很多实用微信跳转技术的电商朋友,网站文章头部或者文章中部出现了点击关注微信关注的二维码,用户点击进去直接跳转到微信内打开指定的二维码,识别即可关注,方便省事,比以往的一键复制—粘贴微信号,转化效果 ...

  8. data parameter is nil 异常处理

    似乎是NSData的问题,用排除法分析了一下 NSString *urlStr = [NSString stringWithFormat:@"%@/config/AppConfig.json ...

  9. 大数据框架对比:Hadoop、Storm、Samza、Spark和Flink

    转自:https://www.cnblogs.com/reed/p/7730329.html 今天看到一篇讲得比较清晰的框架对比,这几个框架的选择对于初学分布式运算的人来说确实有点迷茫,相信看完这篇文 ...

  10. 购物车自己sql错误

    $user_id=$_GET['user_id']; if(!$user_id){ $arr=array('code'=>-1,'data'=>"用户不存在"); ec ...