Qt之QComboBox定制(二)

上一篇文章Qt之QComboBox定制讲到了qt实现自定义的下拉框,该篇文章主要实现了列表式的下拉框,这一节我还将继续讲解QComboBox的定制,而这一节我将会讲述更高级的用法,不仅仅是下拉列表框,而可以实现下拉框为表格,原理其实上一篇文章中的列表框类似,不过在这篇文章我将会重点讲述一下不同的地方,好了,下边我先截取一下demo中的运行效果图,如图1所示,效果并不是那么美观,不过确实有很大的用处。

图1 表格下拉框

看了上图中的展示,是不是觉得很眼熟,是的,同学你说对了,其实这个界面时仿照铁道部的地区选组框做出来的,只不过是效果上有所差异,而功能上基本差不多,上图中的标题栏我是为了实现透明的表头而故意设置透明的,设置透明也是我后边重点要说的,怎么实现下拉框的背景色透明。

在文章的讲解开始前,我先截取下其他两个下拉框的截取,让有所期待的同学一睹为快。说实话,现在的下拉框并不是那么好看,不过用好了qss,这个就不是那么重要了,重要的是下拉框的交互功能,如图2是下拉框列表框,如图3是下拉框表格,不同于图1,图3是不支持根据表头切换内容的下拉框表格。

图2 下拉框列表

图3 下拉框表格

首先声明一下,在看这篇文章的时候我默认同学们已经看过Qt之QComboBox定制这篇文章,我的代码都是在这个demo的基础上重构出来的,如果有什么疑问可以去这篇文章中看看,或者直接私信我。

说起这个demo,我主要是按照两个路线来实现下拉框界面定制,列表和表格,所以我在实现的时候会分出这两个类来进行封装,然后在把他们的一些公有的操作提取出来,作为一层父类,也就是上文中所提到的博客中的combobox类。

接下来就是代码时刻,demo我会在文章最后给出下载链接,因此文章中我只贴出关键的代码段,

1、首先先来理解下文章中关键的类,理解了这几个类,这个demo的骨架就清楚了

CComboBox:下拉框父类,实现了大多数的数据添加接口

CListPopupComboBox:下拉框列表,如图2

CCheckBoxHeaderView:水平表头,主要是自绘表头

CTablePopupComboBox:下拉框表格,如图3

CTableRowHeaderView:列表头

CCityComboBox:城市选择下拉框,如图1

2、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接口都是含有注释的,直接看代码应该也能看懂。

3、列表下拉框

 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 }

4、表格行表头定制

 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 }

5、表格下拉框

 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就没有时间修改。

6、行表头定制

 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 };

7、城市列表下拉框

 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

时间: 08-19

Qt之QComboBox定制(二)的相关文章

QT开发(十二)——QT事件处理机制

QT开发(十二)--QT事件处理机制 一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 鼠标事件: 鼠标移动,鼠标按键的按下和松开 拖放事件: 用鼠标进行拖放 滚轮事件: 鼠标滚轮滚动 绘屏事件: 重绘屏幕的某些部分 定时事件: 定时器到时 焦点事件: 键盘焦点移动 进入和离开事件: 鼠标移入widget之内,或是移出 移动事件: widget的

Qt DLL总结【二】-创建及调用QT的 DLL(三篇)good

目录 Qt DLL总结[一]-链接库预备知识 Qt DLL总结[二]-创建及调用QT的 DLL Qt DLL总结[三]-VS2008+Qt 使用QPluginLoader访问DLL 开发环境:VS2008+Qt4.7.4 最近看了不少Qt的DLL例子,总结一下如何创建和调用QT 动态链接库. 先讲一下对QT动态链接库的调用方法,主要包括: 1.显式链接DLL,调用DLL的全局函数,采用Qt的QLibrary方法 2.显示链接DLL,调用DLL中类对象.成员函数.(通过对象即可实现类成员函数的调用

Qt DLL总结【二】-创建及调用QT的 DLL

开发环境:VS2008+Qt4.7.4 最近看了不少Qt的DLL例子,总结一下如何创建和调用QT 动态链接库. 先讲一下对QT动态链接库的调用方法,主要包括: 1.显式链接DLL,调用DLL的全局函数,采用Qt的QLibrary方法 2.显示链接DLL,调用DLL中类对象.成员函数.(通过对象即可实现类成员函数的调用) ①用虚函数表的方法,这也是COM使用的方法,利用Qt的QLibrary技术调用: ②用GetProcAddress直接调用. ③用Qt的QPluginLoader类直接调用生成的

从Qt谈到C++(二):继承时的含参基类与初始化列表

提出疑问 当我们新建一个Qt的图形界面的工程时,我们可以看看它自动生成的框架代码,比如我们的主窗口名称为MainWindow,我们来看看mainwindow.cpp文件: MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { } 不同于一般的继承,这里的父类的括号里带有参数,我们通常都使用过不带参数,甚至不带括号的基类名称.这里的基类为什么带着参数呢? C++继承与构造函数

QT开发(二十八)——QT常用类(二)

QT开发(二十八)--QT常用类(二) 一.QDir 1.QDir简介 QDir提供对目录结构及其内容的访问. QDir通过相对或绝对路径指向一个文件. 2.QDir成员函数 QDir主要成员函数如下: QDir::QDir ( const QDir & dir ) QDir::QDir ( const QString & path = QString() ) Dir::QDir ( const QString & path, const QString & nameFil

使用QT画颗树 二

第一个版 请移步: http://blog.csdn.net/wzwxiaozheng/article/details/6963086 第二版增加内容: 鼠标点击窗口,树会慢慢长大. 一直摁鼠标就是了. QT的工程在这里下载: http://download.csdn.net/detail/wzwxiaozheng/7412583 这次就不要分了,哈哈哈哈 大家有什么想法 ,可以留言,我在后续会考虑添加. 使用QT画颗树 二

Bootstrap定制(二)less基础语法

前几日花了一天去看less,这几日在捣鼓其他东西,项目也在有序的进行中,今天花点时间整理下less的基础语法,也分享实际中的一些经验,与众人共享. 本篇笔者以less的基础语法着手,并配合bootstrap的逻辑结构给大家梳理下less的语法,方面以后实战中快速开发. 1.变量 与许多后台编辑语法类似,less也有着自己的变量,不过less中的变量更确切来说是一种常量,一次赋值永不改变. @font-size:14px; p{font-size:@font-size} -->p{font-siz

Qt初体验(二)

QT初体验(二) 1.窗口跳转 想到窗口跳转的实现方式,应该很容易想到新建打开新的窗口,然后关闭当前窗口 首先我们得新建一个窗口的类,然后在当前类中创建这个类的对象,调用Show()函数显示出新窗口,close()函数关闭当前窗口 实现的方式大概就是下面这个样子: ui2 = new Select(); ui2->show(); this->close(); 接下来说一下创建新的窗口类的方法: 选择widget,添加 注意,这里只会给你生成一个.ui文件,而.h和.cpp则需要自己再去创建,也

二维码防伪标签制,定制二维码防伪有这些优点呢?

商品定制二维码防伪标签是二维码技术性基本上,在再加315防伪一物一码技术性才称为二维码防伪标签,技术性以二维码为信息内容为规范,会对没每一个商品信息.真假.跟中.查寻.定制等,315防伪一物一码创建在产品信息定制数据链及商品流转全过程管控系统软件,顾客能够根据手机上内置扫一扫认证真假,或微信扫一扫二维码防伪标签,顾客就能查寻到产品信息辨别真假,避免商品被仿,仿冒商品流入销售市场,下边315防伪网编详细介绍一下二维码防伪标签定制为何挑选定制,有这些优点. 二维码防伪有什么优点呢? 1.维修保养著名