qt實現頭像上傳功能

    想必你們都使用過qt的自定義頭像功能吧,那麼圖1應該不會陌生,本片文章我就是要模擬一個這樣的功能,雖然沒有這麼強大的效果,可是可以知足必定的需求。ide

圖1 qq上傳圖片佈局


    首先在講解功能以前,我先給出一片文章,QT實現的相似QQ的頭像選擇框,這篇文章也是講解頭像上傳功能的,而我本身的代碼是從這個demo中優化而來,不只對代碼進行了重構,並且處理了快速拖動時,邊框消失的問題。使用事件和雙緩衝來儘可能減小從新繪製的概率。接下里我會一步一步進行講解,怎麼實現圖片自定義截取功能。
1、概要
首選,我給出4個類,並給出他們的解釋
一、PicturePreviewPanel:圖標展現框,控件基類
二、BackgroundWidget:陰影窗口,是PicturePreviewPanel子類
三、CutShape:交互圖形基類,實現了拖拽和放大縮小功能
四、CutRound:圓形剪貼,父類爲CutShape,實現父類的paintInit接口重繪
五、CutRectangle:矩形剪貼,父類爲CutShape,實現父類的paintInit接口重繪
2、詳情
理解了上述5個類以後,接下來我分別講解下類的頭文件和重要的實現接口,其中自定義圖形類也能夠本身在擴充
頭文件代碼以下,有部分註釋,代碼應該不難理解,開發過程當中只須要聲明PicturePreviewPanel類,並調用期LoadPicture接口就能夠加載圖片,並進行圖片剪切post

  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個類的做用就能夠了。下邊我分別說下交互圖形類、背景色類和顯示圖片類(即控件基類)這三者直接的一些關係,具體以下:

一、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 }

   上邊是主要的繪製過程,關於剪切圖形的交互功能,我給出具體的大姨媽,就不仔細講解功能了,代碼有些長,以下:this

  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

二、BackgroundWidget背景色窗口,該窗口做爲剪切窗口的父類窗口,可是沒有佈局,目的就是可讓剪切窗口自由的移動,並保持在背景色窗口之上,當背景色窗口從新繪製的時候,只繪製除剪切窗口之外的部分。這樣就實現了剪切窗口是透明的可是其他部分都是半透明的,代碼以下:spa

 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 }

三、PicturePreviewPanel圖片上傳控件基類,當圖片加載後,須要重置背景色窗口的大小,以便覆蓋到圖片之上,代碼以下:.net

 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 }

之因此須要從新加載圖片是,放置圖片由於拖拉而失真,圖片的加載的加載代碼以下:3d

 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所示code

圖2 效果展現blog

實例代碼下載:http://download.csdn.net/detail/qq_30392343/9581238

 

若是您以爲文章不錯,不妨給個 打賞,寫做不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!! 

 

  


很重要--轉載聲明

  1. 本站文章無特別說明,皆爲原創,版權全部,轉載時請用連接的方式,給出原文出處。同時寫上原做者:朝十晚八 or Twowords
  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時經過修改本文達到有利於轉載者的目的。 

相關文章
相關標籤/搜索