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. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM