Qt之QComboBox定制


    說起下拉框,想必大家都比較熟悉,在我們注冊一些網站的時候,會出現大量的地區數據供我們選擇,這個時候出現的就是下拉框列表,再比如字體選擇的時候也是使用的下拉框,如圖1所示。下拉框到處可見,作為一個圖形庫,qt也提供了QtComboBox類來供我們使用,但是有些時候簡單的下拉列表已經滿足不了我們的需求,如圖2所示,是一個下拉表格,這個時候就需要我們自己定制一下QComboBox。

image圖1

    image圖2

    上邊說了我們需求的變化,接下來我就講述一下關於QComboBox定制的一些內容,本片文章我就只講述兩種常用的下拉選項控制,分別是列表表格

一、列表的實現

    首先我上一張qq的登錄框,如圖3所示,下拉列表里不僅僅是一個文字描述或者復選框,而是含有圖片文字和關閉按鈕的復雜窗口,那么這個時候簡單的QComboBox是完成不了這項功能的,要做到像qq這樣美觀的功能是需要我們自己做一定的處理

image圖3

    列表窗口定制步驟如下:

1、首先我們需要自定義一個窗口,上邊有我們需要操作的內容,這個窗口講會是QComboBox下拉框中的一項,我自己定義的類名為CActionContentWidget,頭文件如下:

 1 class CActionContentWidget : public QWidget
 2 {
 3     Q_OBJECT
 4 signals:
 5     void IconClicked();
 6     void showText(const QString &);
 7 
 8 public:
 9     CActionContentWidget(const QString & text, QWidget * parent = nullptr);
10     ~CActionContentWidget();
11 
12 public:
13     void SetContentText(const QString & text);//設置顯示文字
14     void SetItemIcon(const QString & icon, const QString & hover);//設置圖標
15 
16 public:
17     void SetBackgroundRole(bool hover);
18 
19 protected:
20     virtual void enterEvent(QEvent *) Q_DECL_OVERRIDE;//暫時沒用
21     virtual void leaveEvent(QEvent *) Q_DECL_OVERRIDE;//暫時沒用
22     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
23     virtual void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
24 
25 
26     virtual void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE;
27 
28 private:
29     void InitializeUI();
30 
31 private:
32     bool m_Mouse_press = false;
33     QWidget * m_ContentWidget = nullptr;
34     QPushButton * m_ActIcon = nullptr;
35     QLabel * m_ActText = nullptr;
36 };

    這個窗口支持鼠標hover事件,並且我自己實現了鼠標按下和彈起方法,為的是自己控制QComboBox下拉框的隱藏,接口SetBackgroundRole是控制窗口背景色變化的函數,由於enterEvent和leaveEvent方法在成為了代理窗口后,事件觸發我自己也沒有搞清楚是怎么回事,因此我使用了installEventFilter方法把該窗口的事件放到父窗口去處理,在窗口中判斷鼠標當前位置和CActionContentWidget 窗口之間的關系來判斷鼠標是否進入該窗口。

QQ圖片20160726180015圖4

2、如圖4所示,是我實現的結果預覽,接下來我們需要把定制的窗口放到QComboBox的下拉框中,代碼如下:

 1 class combobox : public QWidget
 2 {
 3     Q_OBJECT
 4 
 5 public:
 6     combobox(QWidget *parent = 0);
 7     ~combobox();
 8 
 9 protected:
10     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
11 
12 private:
13     void ConstructList();//列表定制
14     void ConstructTable();//表格定制
15 
16 private:
17     Ui::comboboxClass ui;
18 };

    ConstructList方法是列表的定制,也就是上圖4所示的效果,ConstructTable方法內封裝的是定制表格,后續我會講解。

3、上邊提到定制的窗口和鼠標位置的判斷是在父窗口中進行的,那么接下來我就解釋下父窗口的eventFIlter方法。代碼比較簡單,我就不一一進行解釋了

 1 bool combobox::eventFilter(QObject * watched, QEvent * event)
 2 {
 3     if (watched->inherits("QWidget") && event->type() == QEvent::Paint)
 4     {
 5         if (CActionContentWidget * actionItem = dynamic_cast<CActionContentWidget *>(watched))
 6         {
 7             if (actionItem->rect().contains(actionItem->mapFromGlobal(QCursor::pos())))
 8             {
 9                 actionItem->SetBackgroundRole(true);
10             }
11             else
12             {
13                 actionItem->SetBackgroundRole(false);
14             }
15         }
16     }
17 
18     return QWidget::eventFilter(watched, event);
19 }

4、ConstructList列表實現,代碼如下:

 1 void combobox::ConstructList()
 2 {
 3     QListWidget * listWidget = new QListWidget;
 4     listWidget->setViewMode(QListView::ListMode);
 5 
 6     for (int i = 0; i < 5; i++)
 7     {
 8         CActionContentWidget * itemWidget = new CActionContentWidget("fawefawe");
 9         connect(itemWidget, &CActionContentWidget::showText, this, [this, listWidget](const QString & text){
10             ui.comboBox->hidePopup();
11             if (ui.comboBox->isEditable())
12             {
13                 ui.comboBox->setCurrentText(text);
14             }
15             else
16             {
17                 for (int c = 0; c < listWidget->count(); ++c)
18                 {
19                     CActionContentWidget * itemWidget = dynamic_cast<CActionContentWidget *>(listWidget->itemWidget(listWidget->item(c)));
20                     if (itemWidget == sender())
21                     {
22                         ui.comboBox->setCurrentIndex(c);
23                     }
24                 }
25             }
26         });
27         itemWidget->SetItemIcon(":/combobox/Resources/icon1.jpg", ":/combobox/Resources/icon1.jpg");
28         itemWidget->setFixedHeight(45);
29         itemWidget->setFixedWidth(150);
30          QListWidgetItem * item = new QListWidgetItem(listWidget);
31         item->setText(QStringLiteral("%1").arg(i));
32     //    item->setCheckState(Qt::Checked);
33         listWidget->setItemWidget(item, itemWidget);
34         listWidget->addItem(item);
35         itemWidget->installEventFilter(this);
36     }
37 
38     ui.comboBox->setModel(listWidget->model());
39     ui.comboBox->setView(listWidget);
40     ui.comboBox->setEditable(true);//是否支持下拉框可編輯
41     ui.comboBox->setMaxVisibleItems(3);
42 
43     setStyleSheet("QComboBox{border:1px solid gray;}"
44         "QComboBox QAbstractItemView::item{height:45px;}" //下拉選項高度
45         "QComboBox::down-arrow{image:url(:/combobox/Resources/icon1.jpg);}" //下拉箭頭
46         "QComboBox::drop-down{border:0px;}"); //下拉按鈕
47 }
View Code

    其中的setStyleSheet是用來設置下拉框中每一項的信息的,我們自己定制的窗口只是覆蓋在原有窗口之上而已,原有的窗口還是存在的,因此我們的窗口必須和定制的窗口大小一樣,負責會出現一些意外的情況。初次之外還需要注意的是:設置下拉框是否可編輯,如圖5幫助文檔所描述,QComboBox在可編輯狀態下可以通過setCurrentText來設置displayText,但是如果不可編輯,他則會顯示當前原有窗口的text,而非我們自己定制的窗口內容,因此我在showText信號的處理槽中,判斷了下下拉框是否可編輯,並做相應的處理。

image圖5

    還有幾個設置下拉框屬性的接口,我就不一一細說了,想了解的同學自己看幫助文檔吧。其中setModel和setView是兩居重點的代碼,千萬不能忘記,這兩句代碼是把QComboBox的視圖和數據跟QListWidget綁定在一起了。QListWidget還支持ViewMode::IconMode這種現實模式,但是我們的下拉框定制用不到,因此我就不講解這個了。

5、到此我們的列表定制就完成了。

二、表格實現

    看明白了列表的實現,表格的實現就不在話下,不過我們列表的實現和表格的實現我在實現的時候還是有一定的區別的,有興趣的同學可以接着往下看。首先就說下拉框的隱藏這一事件就和列表不一樣,表格我是使用QTableWidget,內容使用QCheckBox,我自己重寫了QCheckBox,並重新是了hitButton這個方法,因為當這個方法返回false時,鼠標點擊不會選中/取消選中選擇框,這個時候事件循環應該是傳遞到了父窗口上,而窗口把彈框隱藏了,這樣做顯然不滿足我們的需求,有時候我們可能需要進行多個選擇。說完需求之后我們來看代碼。

1、首先我自己重寫了QCheckBox,頭文件代碼如下:

 1 class CheckBox : public QCheckBox
 2 {
 3 public:
 4     CheckBox(QWidget * parent = nullptr) :QCheckBox(parent){}
 5     ~CheckBox(){};;
 6 
 7 protected:
 8     virtual void checkStateSet() Q_DECL_OVERRIDE;
 9     virtual bool hitButton(const QPoint & pos) const Q_DECL_OVERRIDE;
10     virtual void nextCheckState() Q_DECL_OVERRIDE;
11 };

    大多數函數都是沒有做處理,僅僅對hitButton方法做了重寫,返回值直接返回true。這個時候就我們就可以進行多項選擇,而彈框也不會消失。最終的實現效果如圖6所示

QQ圖片20160727123954圖6

2、ConstructTable方法的實現如下:

 1 void combobox::ConstructTable()
 2 {
 3     setStyleSheet("QComboBox QAbstractItemView {selection-background-color: transparent;}");
 4 
 5     QTableWidget * tableWidget = new QTableWidget(3, 3);
 6     tableWidget->verticalHeader()->setVisible(false);
 7     tableWidget->horizontalHeader()->setVisible(false);
 8     tableWidget->setShowGrid(false);
 9     for (int i = 0; i < 3; ++i)
10     {
11         tableWidget->setColumnWidth(i, 49);
12         tableWidget->setRowHeight(i, 50);
13         for (int j = 0; j < 3; ++j)
14         {
15         /*    CActionContentWidget *itemWidget = new CActionContentWidget("fawefawe");
16             itemWidget->SetItemIcon(":/combobox/Resources/icon1.jpg", ":/combobox/Resources/icon1.jpg");
17             itemWidget->setFixedHeight(50);
18             itemWidget->setFixedWidth(50);*/
19             CheckBox * itemWidget = new CheckBox;
20             itemWidget->setFixedSize(50, 50);
21             itemWidget->setStyleSheet(QString("QCheckBox {background-color:lightgray;}"
22                 "QCheckBox:checked{background-color:white;}"));
23             connect(itemWidget, &QCheckBox::clicked, this, [this]{
24                 QObject * sender = this->sender();
25                 if (QCheckBox * item = dynamic_cast<QCheckBox *>(sender))
26                 {
27                     QString text = ui.comboBox->currentText();
28                     if (text.isEmpty() == false)
29                     {
30                         if (item->isChecked())
31                         {
32                             if (text.split("|").indexOf(item->text()) == -1)
33                             {
34                                 text.append("|" + item->text());
35                             }
36                         }
37                         else
38                         {
39                             text.remove("|" + item->text());
40                             text.remove(item->text());
41                             if (text.size() != 0 && text.at(0) == '|')
42                             {
43                                 text.remove(0, 1);
44                             }
45                         }
46                     }
47                     else
48                     {
49                         if (item->isChecked())
50                         {
51                             text.append(item->text());
52                         }
53                     }
54                     ui.comboBox->setCurrentText(text);
55                 }
56             });
57             itemWidget->setText(QStringLiteral("好%1").arg(i));
58             itemWidget->setCheckable(true);
59         //    tableWidget->setItem(i, j, new QTableWidgetItem(QStringLiteral("熱分%1").arg(i)));
60             tableWidget->setCellWidget(i, j, itemWidget);
61 
62             itemWidget->installEventFilter(this);
63         }
64     }
65 
66     ui.comboBox->setModel(tableWidget->model());
67     ui.comboBox->setView(tableWidget);
68 //    ui.comboBox->setEditable(true);
69 }
View Code

    上邊的代碼都比較簡單,我就不多了,我只說一個點,在設置表格列寬度的時候設置的為49,因為表格具有boder,如果設置50就會超出tableWidget,而出現左右滾動的現象

demo下載鏈接:http://download.csdn.net/detail/qq_30392343/9587529

 

如果您覺得文章不錯,不妨給個 打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!! 

 


 

  


很重要--轉載聲明

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

 

 


免責聲明!

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



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