Qt之QComboBox定製(二)

    上一篇文章Qt之QComboBox定製講到了qt實現自定義的下拉框,該篇文章主要實現了列表式的下拉框,這一節我還將繼續講解QComboBox的定製,而這一節我將會講述更高級的用法,不只僅是下拉列表框,而能夠實現下拉框爲表格,原理其實上一篇文章中的列表框相似,不過在這篇文章我將會重點講述一下不一樣的地方,好了,下邊我先截取一下demo中的運行效果圖,如圖1所示,效果並非那麼美觀,不過確實有很大的用處。css

圖1 表格下拉框html

    看了上圖中的展現,是否是以爲很眼熟,是的,同窗你說對了,其實這個界面時仿照鐵道部的地區選組框作出來的,只不過是效果上有所差別,而功能上基本差很少,上圖中的標題欄我是爲了實現透明的表頭而故意設置透明的,設置透明也是我後邊重點要說的,怎麼實現下拉框的背景色透明。緩存

    在文章的講解開始前,我先截取下其餘兩個下拉框的截取,讓有所期待的同窗一睹爲快。說實話,如今的下拉框並非那麼好看,不過用好了qss,這個就不是那麼重要了,重要的是下拉框的交互功能,如圖2是下拉框列表框,如圖3是下拉框表格,不一樣於圖1,圖3是不支持根據表頭切換內容的下拉框表格。less

圖2 下拉框列表ide

圖3 下拉框表格函數

    首先聲明一下,在看這篇文章的時候我默認同窗們已經看過Qt之QComboBox定製這篇文章,個人代碼都是在這個demo的基礎上重構出來的,若是有什麼疑問能夠去這篇文章中看看,或者直接私信我。post

    提及這個demo,我主要是按照兩個路線來實現下拉框界面定製,列表和表格,因此我在實現的時候會分出這兩個類來進行封裝,而後在把他們的一些公有的操做提取出來,做爲一層父類,也就是上文中所提到的博客中的combobox類。測試

    接下來就是代碼時刻,demo我會在文章最後給出下載連接,所以文章中我只貼出關鍵的代碼段,this

一、首先先來理解下文章中關鍵的類,理解了這幾個類,這個demo的骨架就清楚了url

CComboBox:下拉框父類,實現了大多數的數據添加接口

CListPopupComboBox:下拉框列表,如圖2

CCheckBoxHeaderView:水平表頭,主要是自繪表頭

CTablePopupComboBox:下拉框表格,如圖3

CTableRowHeaderView:列表頭

CCityComboBox:城市選擇下拉框,如圖1

二、demo中的註釋也是主要集中在接口中,不過在這裏我仍是要在不厭其煩的說一下接口相關的東西,畢竟接口就像是人的眼睛,接口弄明白了才能正確使用。

 1 class CComboBox : public QWidget
 2 {
 3 public:
 4     CComboBox(CustomPopupComboBox::ItemType type, QWidget * parent = nullptr);
 5     ~CComboBox(){}
 6 
 7 public:
 8     //設置分隔符  默認爲'|'
 9     void SetSeparatorSymbol(char symbol);
10     char GetSeparatorSymbol() const { return m_SeparatorSymbol; }
11 
12     //新增數據
13     void AddText(const QString & text);
14     void AddTexts(const QVector<QString> & items);
15 
16     //設置下拉框屬性
17     void SetItemWidth(int width);
18     void SetItemHeight(int height);
19 
20     //設置最多可見條目數
21     void SetMaxVisibleCount(int count);
22 
23     //設置下拉框中項模式
24     void SetItemType(CustomPopupComboBox::ItemType type){ m_Type = type; }
25     CustomPopupComboBox::ItemType GetItemType() const { return m_Type; }
26 
27 protected:
28     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
29 
30 protected:
31     virtual void AddItem(const QString & text) = 0;
32     virtual void ResetItemWidth(int width) = 0;
33     virtual void ResetItemHeight(int height) = 0;
34 
35 protected:
36     QWidget * NewItem(const QString & text);
37 
38 protected:
39     CustomPopupComboBox::ItemType m_Type = CustomPopupComboBox::LabelItem;
40     char m_SeparatorSymbol = '|';//表格選擇項分隔符
41     int m_ItemCount = 0;//表格總的項數
42     QString m_CurrentMemory;//當前選擇字符串
43     QComboBox * m_ComboBox = nullptr;
44     QWidget * m_BottomWidget = nullptr;
45     QWidget * m_PopupWidget = nullptr;
46 
47     //定製項信息    
48     int m_ItemWidth = 150;
49     int m_ItemHeight = 45;//須要和css文件中的QComboBox QAbstractItemView::item{height:45px;}對應
50 
51 private:
52     void InitializeUI();
53 };

    上述代碼中有幾個保護的純虛函數,這幾個接口主要是在具體的下拉框類中實現,而該接口會在父類中被調用,其餘public接口都是含有註釋的,直接看代碼應該也能看懂。

三、列表下拉框

 1 ///    說明:combobox定製  下拉框爲單列數據,支持文本、單選和複選
 2 class CListPopupComboBox : public CComboBox
 3 {
 4     Q_OBJECT
 5 
 6 public:
 7     CListPopupComboBox(CustomPopupComboBox::ItemType type = CustomPopupComboBox::RadioItem, QWidget * parent = nullptr);
 8     ~CListPopupComboBox();
 9 
10 protected:
11     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
12 
13     //新增數據 CComboBox
14     virtual void AddItem(const QString & text) Q_DECL_OVERRIDE;
15     virtual void ResetItemWidth(int width) Q_DECL_OVERRIDE;
16     virtual void ResetItemHeight(int height) Q_DECL_OVERRIDE;
17 
18 private:
19     virtual void ConstructView();//列表定製
20 };

    實現了CComboBox類中的3個純虛接口,主要是重置下拉框項的高度和寬度,還有增長項等接口。列表框增長項代碼以下:

 1 void CListPopupComboBox::AddItem(const QString & text)
 2 {
 3     if (QListWidget * listWidget = dynamic_cast<QListWidget *>(m_PopupWidget))
 4     {
 5         QWidget * itemWidget = NewItem(text);
 6         //itemWidget->setStyleSheet(QString("QCheckBox {background-color:lightgray;}"
 7         //    "QCheckBox:checked{background-color:white;}"));
 8 
 9         itemWidget->setFixedSize(m_ItemWidth, m_ItemHeight);
10 
11         int pos = listWidget->count() - 1 < 0 ? 0 : listWidget->count() - 1;
12         listWidget->insertItem(pos, new QListWidgetItem());
13         listWidget->setItemWidget(listWidget->item(pos), itemWidget);
14     }
15 }

四、表格行表頭定製

 1 ///    說明:table列表頭定製
 2 class CCheckBoxHeaderView : public QHeaderView
 3 {
 4     Q_OBJECT
 5 public:
 6     CCheckBoxHeaderView(int checkColumnIndex,
 7         Qt::Orientation orientation,
 8         QWidget * parent = 0) :
 9         QHeaderView(orientation, parent)
10     {
11         m_checkColIdx = checkColumnIndex;
12     }
13 
14 public:
15     void UpdateSelectColumn(int);
16 
17 signals:
18     void SectionClicked(int);
19 
20 protected:
21     virtual void paintSection(QPainter * painter, const QRect &rect, int logicalIndex) const;
22     virtual void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
23 
24 private:
25     int m_checkColIdx;
26 };

    主要實現了表格行表頭自繪製,繪製代碼在paintSection接口中實現,具體繪製方式以下:

 1 void CCheckBoxHeaderView::paintSection(QPainter * painter, const QRect &rect, int logicalIndex) const
 2 {
 3     QRect r = rect;
 4     r.setTop(r.top() + 20);
 5     r.setLeft(r.left() - 35);
 6     painter->fillRect(r, Qt::white);
 7 
 8     if (logicalIndex == 0)
 9     {
10         r.setTop(r.top() - 20);
11         r.setHeight(20);
12         painter->drawPixmap(rect, QPixmap(QStringLiteral(":/combobox/Resources/bg.png")));
13     }
14     
15 
16     QString text = model()->headerData(logicalIndex, this->orientation(),
17         Qt::DisplayRole).toString();
18 
19     painter->setPen(QColor(239, 241, 241));
20     painter->drawLine(rect.bottomLeft(), rect.bottomRight());
21 
22     painter->setPen(QPen(QColor(Qt::red), 2));
23     if (logicalIndex == m_checkColIdx)
24     {
25 
26         QLine line(rect.bottomLeft() + QPoint(rect.width() / 3, 0), rect.bottomRight() - QPoint(rect.width() / 3, 0));
27         painter->drawLine(line);
28 
29         QFont font = painter->font();
30         font.setBold(true);
31         painter->setFont(font);
32     }
33 
34     painter->drawText(rect, Qt::AlignCenter, text);
35 }
View Code

五、表格下拉框

 1 class CTablePopupComboBox : public CComboBox
 2 {
 3     Q_OBJECT
 4 
 5 public:
 6     CTablePopupComboBox(CustomPopupComboBox::ItemType type = CustomPopupComboBox::CheckBoxItem, QWidget * parent = 0);
 7     ~CTablePopupComboBox();
 8 
 9 public:
10 
11     //設置列數
12     void SetTableColumn(int column);
13     void SetHorizontalHeaderLabels(const QStringList & headerNames);
14 
15     //設置下拉框屬性
16     void SetItemWidth(int width);
17     void SetItemHeight(int height);
18 
19     //設置表頭是否可見
20     void SetHorizontalHeaderVisible(bool visible);
21 
22     void CompletedData();
23 
24 protected:
25     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
26 
27     virtual void AddItem(const QString & text) Q_DECL_OVERRIDE;//新增數據
28     virtual void ResetItemWidth(int width) Q_DECL_OVERRIDE;
29     virtual void ResetItemHeight(int height) Q_DECL_OVERRIDE;
30     virtual void CompleteItems();//補齊沒有填充完的行  防止combobox的view接手鼠標事件
31 
32     virtual void ColumnHeaderClicked(int column);//列表頭被點擊處理
33     
34 
35 private slots:
36     void HeaderClicked(int index);
37 
38 private:
39     virtual void ConstructView();//表格定製
40 
41 private:
42     //定製項信息    //表格
43     int m_TableColumnCount = 2;
44 };

    表格下拉框比列表下拉框複雜,它除了實現列表下拉框的接口外還實現了額外的接口,好比支持列表頭和行表頭的顯示,並支持滾動條的顯示等,這個表格下拉框實現起來沒有那麼完善,其中不乏有大量的bug,若是有同窗發現了什麼解決不了的問題能夠私信我,或許這個bug我已經在項目中修正了,可是demo就沒有時間修改。

六、行表頭定製

 1 ///    說明:table列表頭定製
 2 class CTableRowHeaderView : public QHeaderView
 3 {
 4     Q_OBJECT
 5 public:
 6     CTableRowHeaderView(QWidget * parent = 0) :
 7         QHeaderView(Qt::Vertical, parent)
 8     {
 9     }
10 
11 protected:
12     virtual void paintSection(QPainter * painter, const QRect &rect, int logicalIndex) const;
13 
14 private:
15 };

七、城市列表下拉框

 1 ///    說明:城市選擇下拉框 支持行表頭顯示  不支持滾動條顯示
 2 class CCityComboBox : public CTablePopupComboBox
 3 {
 4     Q_OBJECT
 5 
 6 public:
 7     CCityComboBox(QWidget *parent);
 8     ~CCityComboBox();
 9 
10 public:
11     void SetVerticalHeaderLabels(const QStringList & headerNames);//設置表頭列顯示數據  不建議主動設置
12     void SetVerticalHeaderWidth(int width);//設置表頭列寬度
13     void SetVerticalHeaderVisible(bool visible);//設置表頭列是否可見
14 
15     ///***********************************///18     ///    說明:設置當前默認添加城市分組  配合AddText(AddTexts)一塊兒使用   單次添加的數據  最後必須調用CompletedData接口
19     ///***********************************///
20     void SetCurrentCityKey(char key){ m_CurrentCityKey = QChar(key).toLower().toLatin1(); }
21     void CompletedData();
22 
23     ///***********************************///26     ///    說明:設置當前城市分組
27     ///***********************************///
28     void SetCistyMaps(const std::map < char, std::list<QString> > & citys);
29 
30 protected:
31     virtual void AddItem(const QString & text);
32     virtual void ColumnHeaderClicked(int column);
33 
34 private slots:
35 
36 
37 private:
38     void InitializeUI();
39     void FillData();//填充數據
40     int ResetTableData(int column);//根據點擊列重置表格數據
41 
42 private:
43     bool m_NeedFixedHeight = true;
44     char m_CurrentCityKey = 'a';
45     int m_CurrentDisplayColumnOrder = 0;
46     int m_VerticalHeaderWidth = 35;
47     QStringList m_VerticalHeaderName;
48     std::map< char, std::list<QString> > m_CitysMap;//按字母存儲的城市列表
49     std::map < int, std::list<int> > m_RowsMap;//以列爲鍵存儲所在行
50 };

    本篇文章的核心主要是想講解這個類,該類能夠實現上述列表下拉框和表格下拉框的功能,只須要調用相應的接口就能夠,好比說列表下拉框,那麼我只須要調用隱藏行和列表頭,並把列設置爲一列便可。

    這個城市列表框其實也是在Qt之QComboBox定製文章中重構過來的,雖然接口相似,可是內部的實現細節有了不小的變化,就好比這個表格下拉框在列表頭點擊切換功能實現時,我改變了數據的存儲模式,之前都是把數據刪除從新添加,可是如今我是把全部數據都添加在表格中,而根據所須要顯示的行進行show,而其餘行直接hide,這樣不只實現起來方便並且不須要維護大量的緩存數據。

    在這篇文章最開始的效果展現圖中就能看到,下拉框有一部分是透明的了,這個其實也是我想實現的功能的一部分,主要是想實現一個帶有小三角的下拉框,按照上述的方式應該也是可以實現,關於這個下拉框北京透明我也是搞了很久才搞明白,其實只要簡單的幾行代碼就能夠,代碼以下:

1 if (QWidget * parent = m_ComboBox->view()->parentWidget())
2     {
3     //    parent->installEventFilter(this);
4         parent->setAttribute(Qt::WA_TranslucentBackground);
5         parent->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
6     }

    只須要獲取下拉框的頂層父類,而後設置其屬性並設置其窗口風格便可,但是就這麼幾句話,不設置根本不可以實現背景色透明。因此看到這個信息的同窗真是幸福。

    城市表格下拉框的使用方式也比較簡單,在添使用AddText加完數據以後須要調用CompletedData接口類告訴該類數據添加完畢;若是調用SetCistyMaps接口設置數據,則不須要,demo中使用方式以下:

 1 CCityComboBox w3(&p);
 2 
 3     w3.SetItemType(CustomPopupComboBox::LabelItem);
 4     w3.SetHorizontalHeaderLabels(QStringList() << QStringLiteral("ABCDEF") << QStringLiteral("GHIJ")
 5         << QStringLiteral("KLMN")
 6         << QStringLiteral("PQRSTUVM")
 7         << QStringLiteral("XYZ"));
 8     w3.SetVerticalHeaderLabels(QStringList() << QStringLiteral("A") << QStringLiteral("B")
 9         << QStringLiteral("C")
10         << QStringLiteral("D")
11         << QStringLiteral("E"));
12 
13     w3.SetHorizontalHeaderVisible(true);
14     w3.SetVerticalHeaderVisible(true);
15     w3.setFixedSize(150, 30);
16     w3.AddText(QStringLiteral("a"));
17     w3.AddText(QStringLiteral("a"));
18     w3.AddText(QStringLiteral("a"));
19     w3.AddText(QStringLiteral("a"));
20     w3.AddText(QStringLiteral("a"));
21     w3.AddText(QStringLiteral("a"));
22     w3.AddText(QStringLiteral("a"));
23     w3.AddText(QStringLiteral("a"));
24     w3.AddText(QStringLiteral("a"));
25     w3.AddText(QStringLiteral("a"));
26     w3.AddText(QStringLiteral("a"));
27     w3.AddText(QStringLiteral("a"));
28     w3.AddText(QStringLiteral("a"));
29 
30     w3.SetCurrentCityKey('B');
31     w3.AddText(QStringLiteral("b"));
32     w3.AddText(QStringLiteral("b"));
33     w3.AddText(QStringLiteral("b"));
34     w3.AddText(QStringLiteral("b"));
35     w3.AddText(QStringLiteral("b"));
36     w3.SetCurrentCityKey('C');
37     w3.AddText(QStringLiteral("cC"));
38     w3.AddText(QStringLiteral("cC"));
39     w3.AddText(QStringLiteral("cC"));
40     w3.AddText(QStringLiteral("cC"));
41     w3.AddText(QStringLiteral("cC"));
42     w3.AddText(QStringLiteral("cC"));
43     w3.AddText(QStringLiteral("cC"));
44     w3.SetCurrentCityKey('M');
45     w3.AddText(QStringLiteral("m"));
46     w3.AddText(QStringLiteral("m"));
47     w3.AddText(QStringLiteral("m"));
48     w3.AddText(QStringLiteral("m"));
49     w3.AddText(QStringLiteral("m"));
50     w3.CompletedData();
View Code

    本篇文章我只講述了實現這樣一個下拉框所須要的接口文件,而具體的實現我沒有拉出來將,由於我覺着這個和上一篇文章中的實現相似,只是在接口上有比較大的重構。在寫這篇文章的時候其實我就有一個想法,我應該還會寫關於下拉框定製的第三篇文章,而這第三篇文章主要講的仍是怎麼實現一個下拉框,可是走的徹底是和如今不同的路線。由於最近項目真的很是的緊張,根本沒有時間去寫和測試相關的demo,不事後邊若是有時間我會盡快補上。

    關於全新的下拉框定製,個人想法是徹底定製一個下拉框,而不是重寫QComboBox,這樣的話能夠省去不少的麻煩,好比:下拉框的大小,下拉框的視圖位置等問題,說到這兒,我忽然想起來這個demo的一個bug,那就是在表格下拉框出現下拉框滾動條的時候,若是在滾動了垂直滾動條以後隱藏下拉框,那麼下次展現下拉框窗口時,視圖會顯示不正確,這個問題我本身已經解決了,可是沒有在demo體現,解決辦法是:在隱藏下拉框窗口時,把其視圖滾動到頂端,實現代碼以下:

 1 bool CTablePopupComboBox::eventFilter(QObject * watched, QEvent * event)
 2 {
 3     if (m_BottomWidget && m_BottomWidget == watched)
 4     {
 5         if (QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event))
 6         {
 7             if (mouseEvent->type() == QEvent::MouseButtonPress
 8                 || mouseEvent->type() == QEvent::MouseButtonRelease)
 9             {
10                 return true;
11             }
12         }
13     }
14     else if (watched == m_ComboBox->view()->parentWidget()
15         && event->type() == QEvent::Hide)
16     {
17         if (QTableWidget * tableWidget = dynamic_cast<QTableWidget *>(m_PopupWidget))
18         {
19             tableWidget->scrollToTop();
20         }
21     }
22 
23     return QWidget::eventFilter(watched, event);
24 }
View Code

注意:這個demo比較粗糙,若是有問題的同窗能夠私聊我,不論是建議仍是問題我都會認真的回答

 

demo下載連接:http://download.csdn.net/detail/qq_30392343/9608629

 

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

 

  


很重要--轉載聲明

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

相關文章
相關標籤/搜索