zoukankan      html  css  js  c++  java
  • qt实现头像上传功能

        想必大家都使用过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接口就可以加载图片,并进行图片剪切

      1 #include <QWidget>
      2 
      3 class QLabel;
      4 
      5 enum ShapeType
      6 {
      7     Rect,//矩形
      8     Round,//圆形
      9 };
     10 
     11 //剪贴图基类 实现了基本的交互功能,并绘制了部分图案,主要的团绘制在子类实现,通过实现paintInit接口
     12 class CutShape : public QWidget
     13 {
     14 public:
     15     CutShape(QWidget * parent = nullptr);
     16     ~CutShape();
     17 
     18 public:
     19     QPainterPath CutRegion();
     20 
     21 protected:
     22     //QWidget
     23     virtual void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
     24     virtual void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE;
     25     virtual void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE;
     26     virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE;
     27     virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
     28 
     29     virtual bool paintInit(QPaintEvent *, QPaintDevice *) = 0;
     30     virtual QPainterPath Region(){ return QPainterPath(); };
     31 
     32 protected:
     33     ShapeType m_Type;
     34     bool m_MouseDown = false;
     35     bool m_IsMoving = false;
     36     bool m_IsFirst = true;
     37     int border = 5;
     38 
     39 private:
     40     QRect getResizeGem(QRect oldgeo, QPoint mousePoint, bool & ignore);
     41 
     42 private:
     43     bool m_EnableRepaint = true;
     44     bool m_Left = false;
     45     bool m_Right = false;
     46     bool m_Bottom = false;
     47     bool m_Top = false;
     48     QPoint m_startPoint;
     49     QPoint m_old_pos;
     50     QLabel * m_Label;
     51     QPixmap m_BufferPix;
     52 };
     53 
     54 class CutRectangle : public CutShape
     55 {
     56 public:
     57     CutRectangle(QWidget * parent = nullptr);
     58     ~CutRectangle();
     59 
     60 protected:
     61     //CutShape
     62     virtual bool paintInit(QPaintEvent *, QPaintDevice *) Q_DECL_OVERRIDE;
     63     virtual QPainterPath Region() Q_DECL_OVERRIDE;
     64 };
     65 
     66 class CutRound : public CutShape
     67 {
     68 public:
     69     CutRound(QWidget * parent = nullptr);
     70     ~CutRound();
     71 
     72 protected:
     73     //CutShape
     74     virtual bool paintInit(QPaintEvent *, QPaintDevice *) Q_DECL_OVERRIDE;
     75     virtual QPainterPath Region() Q_DECL_OVERRIDE;
     76 
     77 private:
     78 };
     79 
     80 class BackgroundWidget : public QWidget
     81 {
     82 public:
     83     BackgroundWidget(QWidget * parent = nullptr, ShapeType type = Round);
     84     ~BackgroundWidget();
     85 
     86 public:
     87     void PictureLoadFinished();
     88 
     89 protected:
     90     virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
     91 
     92 private:
     93     ShapeType m_Type;
     94     CutShape * m_CutShape = nullptr;
     95 };
     96 
     97 class PicturePreviewPanel : public QWidget
     98 {
     99     Q_OBJECT
    100 
    101 public:
    102     PicturePreviewPanel(QWidget * parent = nullptr);
    103     ~PicturePreviewPanel();
    104 
    105 public:
    106     void LoadPicture(const QString & filepath);//加载图片
    107 
    108 protected:
    109     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
    110 
    111 private:
    112     void InitializeUI();
    113     void LoadPicture_p();
    114 
    115 private:
    116     QString m_PicturePath;
    117     QLabel * m_PictureContainer = nullptr;
    118     BackgroundWidget * m_BgWidget = nullptr;
    119 };
    View Code

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

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

     1 void CutShape::paintEvent(QPaintEvent * event)
     2 {
     3     QPainter paint;
     4     if (m_EnableRepaint && paintInit(event, &m_BufferPix))
     5     {
     6         m_EnableRepaint = false;
     7         qDebug() << "event->type()" << event->type();
     8         m_BufferPix = QPixmap(size());
     9         m_BufferPix.fill(Qt::transparent);
    10 
    11         paint.begin(&m_BufferPix);
    12         QPen pen0;
    13         pen0.setColor(QColor(54, 158, 254, 120));
    14         pen0.setWidth(2);
    15         paint.setPen(pen0);
    16         paint.drawRect(border, border, width() - border * 2, width() - border * 2);
    17 
    18         QPen pen;
    19         QVector<qreal> dashes;
    20         qreal space = 3;
    21         dashes << 5 << space << 5 << space;
    22         pen.setDashPattern(dashes);
    23         pen.setColor(Qt::white);
    24 
    25         paint.setPen(pen);
    26         int x_pos = (int)width() / 3.0;
    27         int y_pos = (int)height() / 3.0;
    28         paint.drawLine(x_pos, border, x_pos, height() - 2 * border);
    29         paint.drawLine(2 * x_pos, border, 2 * x_pos, height() - 2 * border);
    30         paint.drawLine(border, y_pos, width() - 2 * border, y_pos);
    31         paint.drawLine(border, 2 * y_pos, width() - 2 * border, 2 * y_pos);
    32         paint.end();
    33     }
    34     paint.begin(this);
    35     paint.drawPixmap(rect(), m_BufferPix);
    36     paint.end();
    37 }

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

      1 void CutShape::mousePressEvent(QMouseEvent * event)
      2 {
      3     m_startPoint = event->pos();
      4     m_MouseDown = event->button() == Qt::LeftButton;
      5 }
      6 
      7 void CutShape::mouseMoveEvent(QMouseEvent * event)
      8 {
      9     QPoint dragPoint = event->pos();
     10     if (!parentWidget()->rect().contains(mapToParent(dragPoint)))
     11     {
     12         return;
     13     }
     14     int x = event->x();
     15     int y = event->y();
     16     if (m_MouseDown)
     17     {
     18         if (m_Left == false && m_Right == false
     19             && m_Bottom == false && m_Top == false)
     20         {
     21             QPoint p = QPoint((pos().x() + dragPoint.x() - m_startPoint.x()), (pos().y() + dragPoint.y() - m_startPoint.y()));
     22             QPoint dragEndge = p;
     23             dragEndge.setX(dragEndge.x() + rect().width());
     24             dragEndge.setY(dragEndge.y() + rect().height());
     25             p.setX(p.x() < 0 ? 0 : p.x());
     26             p.setX(dragEndge.x() > parentWidget()->width() ? parentWidget()->width() - rect().width() : p.x());
     27             p.setY(p.y() < 0 ? 0 : p.y());
     28             p.setY(dragEndge.y() > parentWidget()->height() ? parentWidget()->height() - rect().height() : p.y());
     29             move(p);
     30         }
     31         else
     32         {
     33             bool ignore = false;
     34             QRect g = getResizeGem(geometry(), dragPoint, ignore);
     35             if (parentWidget()->rect().contains(g))
     36                 setGeometry(g);
     37             if (ignore == false)
     38             {
     39                 m_startPoint = QPoint(!m_Right ? m_startPoint.x() : event->x(), !m_Bottom ? m_startPoint.y() : event->y());
     40             }
     41         }
     42     }
     43     else
     44     {
     45         QRect r = rect();
     46         m_Left = qAbs(x - r.left()) < 5;
     47         m_Right = qAbs(x - r.right()) < 5;
     48         m_Bottom = qAbs(y - r.bottom()) < 5;
     49         m_Top = qAbs(y - r.top()) < 5;
     50         bool lorr = m_Left | m_Right;
     51         bool torb = m_Top | m_Bottom;
     52         if (lorr && torb)
     53         {
     54             if ((m_Left && m_Top) || (m_Right && m_Bottom))
     55             {
     56                 setCursor(Qt::SizeFDiagCursor);
     57             }
     58             else
     59                 setCursor(Qt::SizeBDiagCursor);
     60         }
     61         else if (lorr)
     62             setCursor(Qt::SizeHorCursor);
     63         else if (torb)
     64             setCursor(Qt::SizeVerCursor);
     65         else
     66         {
     67             setCursor(Qt::SizeAllCursor);
     68             m_Bottom = m_Left = m_Right = m_Top = false;
     69         }
     70     }
     71 }
     72 
     73 void CutShape::mouseReleaseEvent(QMouseEvent * event)
     74 {
     75     m_MouseDown = false;
     76 }
     77 
     78 void CutShape::resizeEvent(QResizeEvent *event)
     79 {
     80     m_EnableRepaint = true;
     81     update();
     82 
     83     QWidget::resizeEvent(event);
     84 }
     85 
     86 QRect CutShape::getResizeGem(QRect oldgeo, QPoint mousePoint, bool & ignore)
     87 {
     88     QRect g = oldgeo;
     89     bool lorr = m_Left | m_Right;
     90     bool torb = m_Top | m_Bottom;
     91     int dx = mousePoint.x() - m_startPoint.x();
     92     int dy = mousePoint.y() - m_startPoint.y();
     93     ignore = false;
     94     if (lorr && torb)
     95     {
     96         int maxLen = qMax(qAbs(dx), qAbs(dy));
     97         if (m_Left && m_Top && dx*dy > 0)
     98         {
     99             g.setLeft(dx > 0 ? g.left() + maxLen : g.left() - maxLen);
    100             g.setTop(dy > 0 ? g.top() + maxLen : g.top() - maxLen);
    101         }
    102         else if (m_Right && m_Top && dx * dy < 0)
    103         {
    104             g.setRight(dx > 0 ? g.right() + maxLen : g.right() - maxLen);
    105             g.setTop(dy > 0 ? g.top() + maxLen : g.top() - maxLen);
    106         }
    107         else if (m_Right && m_Bottom)
    108         {
    109             if (dx * dy > 0)
    110             {
    111                 g.setRight(dx > 0 ? g.right() + maxLen : g.right() - maxLen);
    112                 g.setBottom(dy > 0 ? g.bottom() + maxLen : g.bottom() - maxLen);
    113             }
    114             else if (dx == 0 && dy != 0
    115                 /*|| dx != 0 && dy == 0*/)
    116             {
    117                 ignore = true;
    118             }            
    119         }
    120         else if (m_Left && m_Bottom && dx*dy < 0)
    121         {
    122             g.setLeft(dx > 0 ? g.left() + maxLen : g.left() - maxLen);
    123             g.setBottom(dy > 0 ? g.bottom() + maxLen : g.bottom() - maxLen);
    124         }
    125         
    126         return g;
    127     }
    128     else if (lorr)
    129     {
    130         if (m_Left)
    131             g.setLeft(g.left() + dx);
    132         if (m_Right)
    133             g.setRight(g.right() + dx);
    134         int len = g.width() - oldgeo.width();
    135         int intHight = (int)len / 2.0;
    136 
    137         g.setTop(g.top() - intHight);
    138         g.setBottom(g.bottom() + len - intHight);
    139     }
    140     else if (torb)
    141     {
    142         if (m_Bottom)
    143             g.setBottom(g.bottom() + dy);
    144         if (m_Top)
    145             g.setTop(g.top() + dy);
    146         int dheigt = g.height() - oldgeo.height();
    147         int intWidth = (int)dheigt / 2.0;
    148 
    149         g.setLeft(g.left() - intWidth);
    150         g.setRight(g.right() + dheigt - intWidth);
    151     }
    152     else
    153     {
    154         ignore = true;
    155     }
    156     return g;
    157 }
    View Code

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

     1 void BackgroundWidget::paintEvent(QPaintEvent *)
     2 {
     3     QPainterPath painterPath;
     4     QPainterPath p;
     5     p.addRect(x(), y(), rect().width(), rect().height());
     6     if (m_CutShape)
     7     {
     8         painterPath.addPath(m_CutShape->CutRegion().translated(m_CutShape->pos()));
     9     }
    10     QPainterPath drawPath = p.subtracted(painterPath);
    11 
    12     QPainter paint(this);
    13     paint.setOpacity(0.5);
    14     paint.fillPath(drawPath, QBrush(Qt::black));
    15 }

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

     1 bool PicturePreviewPanel::eventFilter(QObject * watched, QEvent * event)
     2 {
     3     if (watched == m_PictureContainer)
     4     {
     5         if (event->type() == QEvent::Resize)
     6         {
     7             LoadPicture_p();
     8             if (m_BgWidget)
     9             {
    10                 m_BgWidget->resize(m_PictureContainer->size());
    11             }
    12         }
    13     }
    14     return QWidget::eventFilter(watched, event);
    15 }

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

     1 void PicturePreviewPanel::LoadPicture_p()
     2 {
     3     QPixmap picture;
     4     picture.load(m_PicturePath);
     5     if (!picture.isNull())
     6     {
     7         picture = picture.scaled(m_PictureContainer->width(), m_PictureContainer->height());
     8         m_PictureContainer->setPixmap(picture);
     9         m_BgWidget->PictureLoadFinished();
    10     }
    11 }

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

    图2 效果展示

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

     

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

     

      


    很重要--转载声明

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

  • 相关阅读:
    【转】HBase中的时间维度
    【转】HBase基本原理
    【转】BloomFilter——大规模数据处理利器
    【转】HBase客户端API:管理特性
    es6 模本字符串拼接方法 ``
    Electron桌面应用打包流程
    vue-electron脚手架安装及说明 打包基于Vue的 桌面应用程序
    safari打开的页面数字识别变为蓝色
    使用jquery中$.each()方法来循环一个数据列表
    jquery实现点击控制div的显示和隐藏
  • 原文地址:https://www.cnblogs.com/swarmbees/p/5688885.html
Copyright © 2011-2022 走看看