zoukankan      html  css  js  c++  java
  • qt实现头像上传功能(写了4个类,朝十晚八的博客,非常好)

    想必大家都使用过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 };
    复制代码

        上述头文件,如果觉得麻烦了也可以不具体细看接口,只要理解了标题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 }
    复制代码

    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

    http://www.cnblogs.com/swarmbees/p/5688885.html

  • 相关阅读:
    HDU1029 Ignatius and the Princess IV
    UVA11039 Building designing【排序】
    UVA11039 Building designing【排序】
    POJ3278 HDU2717 Catch That Cow
    POJ3278 HDU2717 Catch That Cow
    POJ1338 Ugly Numbers(解法二)
    POJ1338 Ugly Numbers(解法二)
    UVA532 Dungeon Master
    UVA532 Dungeon Master
    POJ1915 Knight Moves
  • 原文地址:https://www.cnblogs.com/findumars/p/5738949.html
Copyright © 2011-2022 走看看