很重要--轉載聲明
- 本站文章無特別說明,皆爲原創,版權全部,轉載時請用連接的方式,給出原文出處。同時寫上原做者:朝十晚八 or Twowords
- 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時經過修改本文達到有利於轉載者的目的。
上一篇文章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 }
五、表格下拉框
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();
本篇文章我只講述了實現這樣一個下拉框所須要的接口文件,而具體的實現我沒有拉出來將,由於我覺着這個和上一篇文章中的實現相似,只是在接口上有比較大的重構。在寫這篇文章的時候其實我就有一個想法,我應該還會寫關於下拉框定製的第三篇文章,而這第三篇文章主要講的仍是怎麼實現一個下拉框,可是走的徹底是和如今不同的路線。由於最近項目真的很是的緊張,根本沒有時間去寫和測試相關的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 }
注意:這個demo比較粗糙,若是有問題的同窗能夠私聊我,不論是建議仍是問題我都會認真的回答
demo下載連接:http://download.csdn.net/detail/qq_30392343/9608629