无标题文章

链接:https://www.cnblogs.com/swarmbees/p/5710714.html

说起下拉框,想必大家都比较熟悉,在我们注册一些网站的时候,会出现大量的地区数据供我们选择,这个时候出现的就是下拉框列表,再比如字体选择的时候也是使用的下拉框,如图1所示。下拉框到处可见,作为一个图形库,qt也提供了QtComboBox类来供我们使用,但是有些时候简单的下拉列表已经满足不了我们的需求,如图2所示,是一个下拉表格,这个时候就需要我们自己定制一下QComboBox。

图1

图2

上边说了我们需求的变化,接下来我就讲述一下关于QComboBox定制的一些内容,本片文章我就只讲述两种常用的下拉选项控制,分别是列表表格

一、列表的实现

    首先我上一张qq的登录框,如图3所示,下拉列表里不仅仅是一个文字描述或者复选框,而是含有图片文字和关闭按钮的复杂窗口,那么这个时候简单的QComboBox是完成不了这项功能的,要做到像qq这样美观的功能是需要我们自己做一定的处理

图3

    列表窗口定制步骤如下:

1、首先我们需要自定义一个窗口,上边有我们需要操作的内容,这个窗口讲会是QComboBox下拉框中的一项,我自己定义的类名为CActionContentWidget,头文件如下:

class CActionContentWidget : public QWidget

{

    Q_OBJECT

signals:

    void IconClicked();

    void showText(const QString &);

public:

    CActionContentWidget(const QString & text, QWidget * parent = nullptr);

    ~CActionContentWidget();

public:

    void SetContentText(const QString & text);//设置显示文字

    void SetItemIcon(const QString & icon, const QString & hover);//设置图标

public:

    void SetBackgroundRole(bool hover);

protected:

    virtual void enterEvent(QEvent *) Q_DECL_OVERRIDE;//暂时没用

    virtual void leaveEvent(QEvent *) Q_DECL_OVERRIDE;//暂时没用

    virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;

    virtual void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;

    virtual void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE;

private:

    void InitializeUI();

private:

    bool m_Mouse_press = false;

    QWidget * m_ContentWidget = nullptr;

    QPushButton * m_ActIcon = nullptr;

    QLabel * m_ActText = nullptr;

};

这个窗口支持鼠标hover事件,并且我自己实现了鼠标按下和弹起方法,为的是自己控制QComboBox下拉框的隐藏,接口SetBackgroundRole是控制窗口背景色变化的函数,由于enterEvent和leaveEvent方法在成为了代理窗口后,事件触发我自己也没有搞清楚是怎么回事,因此我使用了installEventFilter方法把该窗口的事件放到父窗口去处理,在窗口中判断鼠标当前位置和CActionContentWidget

窗口之间的关系来判断鼠标是否进入该窗口。

图4

2、如图4所示,是我实现的结果预览,接下来我们需要把定制的窗口放到QComboBox的下拉框中,代码如下:

class combobox : public QWidget

{

    Q_OBJECT

public:

    combobox(QWidget *parent = 0);

    ~combobox();

protected:

    virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;

private:

    void ConstructList();//列表定制

    void ConstructTable();//表格定制

private:

    Ui::comboboxClass ui;

};


ConstructList方法是列表的定制,也就是上图4所示的效果,ConstructTable方法内封装的是定制表格,后续我会讲解。

3、上边提到定制的窗口和鼠标位置的判断是在父窗口中进行的,那么接下来我就解释下父窗口的eventFIlter方法。代码比较简单,我就不一一进行解释了


bool combobox::eventFilter(QObject * watched, QEvent * event)

{

    if (watched->inherits("QWidget") && event->type() == QEvent::Paint)

    {

        if (CActionContentWidget * actionItem = dynamic_cast<CActionContentWidget *>(watched))

        {

            if (actionItem->rect().contains(actionItem->mapFromGlobal(QCursor::pos())))

            {

                actionItem->SetBackgroundRole(true);

            }

            else

            {

                actionItem->SetBackgroundRole(false);

            }

        }

    }

    return QWidget::eventFilter(watched, event);

}

4、ConstructList列表实现,代码如下:

void combobox::ConstructList()

{

    QListWidget * listWidget = new QListWidget;

    listWidget->setViewMode(QListView::ListMode);

    for (int i = 0; i < 5; i++)

    {

        CActionContentWidget * itemWidget = new CActionContentWidget("fawefawe");

        connect(itemWidget, &CActionContentWidget::showText, this, [this, listWidget](const QString & text){

            ui.comboBox->hidePopup();

            if (ui.comboBox->isEditable())

            {

                ui.comboBox->setCurrentText(text);

            }

            else

            {

                for (int c = 0; c < listWidget->count(); ++c)

                {

                    CActionContentWidget * itemWidget = dynamic_cast<CActionContentWidget *>(listWidget->itemWidget(listWidget->item(c)));

                    if (itemWidget == sender())

                    {

                        ui.comboBox->setCurrentIndex(c);

                    }

                }

            }

        });

        itemWidget->SetItemIcon(":/combobox/Resources/icon1.jpg", ":/combobox/Resources/icon1.jpg");

        itemWidget->setFixedHeight(45);

        itemWidget->setFixedWidth(150);

        QListWidgetItem * item = new QListWidgetItem(listWidget);

        item->setText(QStringLiteral("%1").arg(i));

    //    item->setCheckState(Qt::Checked);

        listWidget->setItemWidget(item, itemWidget);

        listWidget->addItem(item);

        itemWidget->installEventFilter(this);

    }

    ui.comboBox->setModel(listWidget->model());

    ui.comboBox->setView(listWidget);

    ui.comboBox->setEditable(true);//是否支持下拉框可编辑

    ui.comboBox->setMaxVisibleItems(3);

    setStyleSheet("QComboBox{border:1px solid gray;}"

        "QComboBox QAbstractItemView::item{height:45px;}" //下拉选项高度

        "QComboBox::down-arrow{image:url(:/combobox/Resources/icon1.jpg);}" //下拉箭头

        "QComboBox::drop-down{border:0px;}"); //下拉按钮

}


其中的setStyleSheet是用来设置下拉框中每一项的信息的,我们自己定制的窗口只是覆盖在原有窗口之上而已,原有的窗口还是存在的,因此我们的窗口必须和定制的窗口大小一样,负责会出现一些意外的情况。初次之外还需要注意的是:设置下拉框是否可编辑,如图5帮助文档所描述,QComboBox在可编辑状态下可以通过setCurrentText来设置displayText,但是如果不可编辑,他则会显示当前原有窗口的text,而非我们自己定制的窗口内容,因此我在showText信号的处理槽中,判断了下下拉框是否可编辑,并做相应的处理。

图5


还有几个设置下拉框属性的接口,我就不一一细说了,想了解的同学自己看帮助文档吧。其中setModel和setView是两居重点的代码,千万不能忘记,这两句代码是把QComboBox的视图和数据跟QListWidget绑定在一起了。QListWidget还支持ViewMode::IconMode这种现实模式,但是我们的下拉框定制用不到,因此我就不讲解这个了。

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

二、表格实现


看明白了列表的实现,表格的实现就不在话下,不过我们列表的实现和表格的实现我在实现的时候还是有一定的区别的,有兴趣的同学可以接着往下看。首先就说下拉框的隐藏这一事件就和列表不一样,表格我是使用QTableWidget,内容使用QCheckBox,我自己重写了QCheckBox,并重新是了hitButton这个方法,因为当这个方法返回false时,鼠标点击不会选中/取消选中选择框,这个时候事件循环应该是传递到了父窗口上,而窗口把弹框隐藏了,这样做显然不满足我们的需求,有时候我们可能需要进行多个选择。说完需求之后我们来看代码。

1、首先我自己重写了QCheckBox,头文件代码如下:



其中的setStyleSheet是用来设置下拉框中每一项的信息的,我们自己定制的窗口只是覆盖在原有窗口之上而已,原有的窗口还是存在的,因此我们的窗口必须和定制的窗口大小一样,负责会出现一些意外的情况。初次之外还需要注意的是:设置下拉框是否可编辑,如图5帮助文档所描述,QComboBox在可编辑状态下可以通过setCurrentText来设置displayText,但是如果不可编辑,他则会显示当前原有窗口的text,而非我们自己定制的窗口内容,因此我在showText信号的处理槽中,判断了下下拉框是否可编辑,并做相应的处理。

图5


还有几个设置下拉框属性的接口,我就不一一细说了,想了解的同学自己看帮助文档吧。其中setModel和setView是两居重点的代码,千万不能忘记,这两句代码是把QComboBox的视图和数据跟QListWidget绑定在一起了。QListWidget还支持ViewMode::IconMode这种现实模式,但是我们的下拉框定制用不到,因此我就不讲解这个了。

5、到此我们的列表定制就完成了。二、表格实现

看明白了列表的实现,表格的实现就不在话下,不过我们列表的实现和表格的实现我在实现的时候还是有一定的区别的,有兴趣的同学可以接着往下看。首先就说下拉框的隐藏这一事件就和列表不一样,表格我是使用QTableWidget,内容使用QCheckBox,我自己重写了QCheckBox,并重新是了hitButton这个方法,因为当这个方法返回false时,鼠标点击不会选中/取消选中选择框,这个时候事件循环应该是传递到了父窗口上,而窗口把弹框隐藏了,这样做显然不满足我们的需求,有时候我们可能需要进行多个选择。说完需求之后我们来看代码。

1、首先我自己重写了QCheckBox,头文件代码如下:

class CheckBox : public QCheckBox

{

public:

    CheckBox(QWidget * parent = nullptr) :QCheckBox(parent){}

    ~CheckBox(){};;

protected:

    virtual void checkStateSet() Q_DECL_OVERRIDE;

    virtual bool hitButton(const QPoint & pos) const Q_DECL_OVERRIDE;

    virtual void nextCheckState() Q_DECL_OVERRIDE;

};

大多数函数都是没有做处理,仅仅对hitButton方法做了重写,返回值直接返回true。这个时候就我们就可以进行多项选择,而弹框也不会消失。最终的实现效果如图6所示

图6

2、ConstructTable方法的实现如下


void combobox::ConstructTable()

{

    setStyleSheet("QComboBox QAbstractItemView {selection-background-color: transparent;}");

    QTableWidget * tableWidget = new QTableWidget(3, 3);

    tableWidget->verticalHeader()->setVisible(false);

    tableWidget->horizontalHeader()->setVisible(false);

    tableWidget->setShowGrid(false);

    for (int i = 0; i < 3; ++i)

    {

        tableWidget->setColumnWidth(i, 49);

        tableWidget->setRowHeight(i, 50);

        for (int j = 0; j < 3; ++j)

        {

        /*    CActionContentWidget *itemWidget = new CActionContentWidget("fawefawe");

            itemWidget->SetItemIcon(":/combobox/Resources/icon1.jpg", ":/combobox/Resources/icon1.jpg");

            itemWidget->setFixedHeight(50);

            itemWidget->setFixedWidth(50);*/

            CheckBox * itemWidget = new CheckBox;

            itemWidget->setFixedSize(50, 50);

            itemWidget->setStyleSheet(QString("QCheckBox {background-color:lightgray;}"

                "QCheckBox:checked{background-color:white;}"));

            connect(itemWidget, &QCheckBox::clicked, this, [this]{

                QObject * sender = this->sender();

                if (QCheckBox * item = dynamic_cast<QCheckBox *>(sender))

                {

                    QString text = ui.comboBox->currentText();

                    if (text.isEmpty() == false)

                    {

                        if (item->isChecked())

                        {

                            if (text.split("|").indexOf(item->text()) == -1)

                            {

                                text.append("|" + item->text());

                            }

                        }

                        else

                        {

                            text.remove("|" + item->text());

                            text.remove(item->text());

                            if (text.size() != 0 && text.at(0) == '|')

                            {

                                text.remove(0, 1);

                            }

                        }

                    }

                    else

                    {

                        if (item->isChecked())

                        {

                            text.append(item->text());

                        }

                    }

                    ui.comboBox->setCurrentText(text);

                }

            });

            itemWidget->setText(QStringLiteral("好%1").arg(i));

            itemWidget->setCheckable(true);

        //    tableWidget->setItem(i, j, new QTableWidgetItem(QStringLiteral("热分%1").arg(i)));

            tableWidget->setCellWidget(i, j, itemWidget);

            itemWidget->installEventFilter(this);

        }

    }

    ui.comboBox->setModel(tableWidget->model());

    ui.comboBox->setView(tableWidget);

//    ui.comboBox->setEditable(true);

}


上边的代码都比较简单,我就不多了,我只说一个点,在设置表格列宽度的时候设置的为49,因为表格具有boder,如果设置50就会超出tableWidget,而出现左右滚动的现象

demo下载链接:http://download.csdn.net/detail/qq_30392343/9587529

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,208评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,502评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,496评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,176评论 1 302
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,185评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,630评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,992评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,973评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,510评论 1 325
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,546评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,659评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,250评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,990评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,421评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,569评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,238评论 3 382
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,732评论 2 366