Qt自定义下拉列表-可为选项设置标题、可禁用选项

         在Qt中,ComboBox(组合框)是一种常用的用户界面控件,它提供了一个下拉列表,允许用户从预定义的选项中选择一个。在项目开发中,如果简单的QComboBox无法满足需求,可以通过自定义QComboBox来实现更复杂的功能。本文介绍一个自定义的下拉列表,并为选项设置标题、可禁用选项。

一、简述

         本示例介绍一个Qt自定义下拉列表,可为选项设置标题、可禁用选项。

二、 设计思路             
  1. 继承QComboBox类: 创建一个新的类,继承自QComboBox类。在该类中,可以重写QComboBox的方法以实现自定义功能。

  2. 使用QListWidget和QListWidgetItem: QComboBox默认使用QStandardItemModel来管理下拉列表的数据。扩展时使用QListWidget的QListModel来替代QStandardItemModel。在QListWidget的QListWidgetItem中,可以自定义每个列表项的显示和交互逻辑。

  3. 使用自定义代理类: 通过自定义QItemDelegate类,可以实现对下拉列表中每个项的自定义绘制和交互逻辑。可以通过setItemDelegate方法将自定义的代理类设置为QComboBox的项代理。

三、效果 

四、核心代码  
1、头文件
#ifndef CustomCombo_H
#define CustomCombo_H

#include <QComboBox>
#include <QListWidget>
#include <QItemDelegate>

class CustomCombo : public QComboBox
{
    Q_OBJECT

public:
    CustomCombo(QWidget *parent=0);

    QListWidget *list() const{ return list_; }

    void addTitle(const QString &text);
    void addItem (const QString &text);

    void setIsTitle(int ind, bool b);
    void setSelectable(int ind, bool b);

private:
    void resetCurrentInd();

private:
    QListWidget *list_;
};

//------------------------------------------------------------------------------------------------------------
class CustomComboItem : public QListWidgetItem {
public:
    CustomComboItem(CustomCombo *combo, const QString &str);

    virtual ~CustomComboItem() { }

    CustomCombo *combo() const { return combo_; }

    void setIsTitle(bool b);

    inline bool isTitle() const { return isTitle_; }

    void setSelectable(bool selectable);

    inline bool isSelectable() const {return (flags() & (Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled));}

private:
    CustomCombo *combo_;
    bool isTitle_;
    bool selectable_;
};

//------------------------------------------------------------------------------------------------------------
class CustomComboTitle : public CustomComboItem {
public:
    CustomComboTitle(CustomCombo *combo, const QString &str) :
        CustomComboItem(combo, str) {
        setSelectable(false);

        setIsTitle(true);
    }
};

//------------------------------------------------------------------------------------------------------------
class CustomComboDelegate : public QItemDelegate {
public:
    CustomComboDelegate(CustomCombo *combo) ;

    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const override;

    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;

    QStyleOptionMenuItem getStyleOption(const QStyleOptionViewItem &option,
                                        const QModelIndex &index, CustomComboItem *item) const;

    static bool isSeparator(const QModelIndex &index) {return (index.data(Qt::AccessibleDescriptionRole).toString() == "separator");}

private:
    CustomCombo *combo_;
};

#endif
2、实现代码
#include "CustomCombo.h"

#include <QPainter>
#include <QApplication>

CustomCombo::CustomCombo(QWidget *parent) :QComboBox(parent), list_(nullptr)
{
    list_ = new QListWidget;
    list_->setItemDelegate(new CustomComboDelegate(this));

    setModel(list_->model());
    setView(list_);
}

void CustomCombo::addTitle(const QString &text)
{
    list_->addItem(new CustomComboTitle(this, text));

    resetCurrentInd();
}

void CustomCombo::addItem(const QString &text)
{
    list_->addItem(new CustomComboItem(this, text));

    resetCurrentInd();
}

void CustomCombo::setIsTitle(int ind, bool b)
{
    auto *item = dynamic_cast<CustomComboItem *>(list_->item(ind));

    item->setIsTitle(b);

    resetCurrentInd();
}

void CustomCombo::setSelectable(int ind, bool b)
{
    auto *item = dynamic_cast<CustomComboItem *>(list_->item(ind));

    item->setSelectable(b);

    resetCurrentInd();
}

void CustomCombo::resetCurrentInd()
{
    auto *item = static_cast<CustomComboItem *>(list()->item(currentIndex()));

    if (item->isSelectable())
        return;

    for (int i = 0; i < count(); ++i) {
        auto *item = static_cast<CustomComboItem *>(list()->item(i));

        if (item->isSelectable()) {
            setCurrentIndex(i);
            break;
        }
    }
}

//CustomComboItem
//------------------------------------------------------------------------------------------------------------
CustomComboItem::CustomComboItem(CustomCombo *combo, const QString &str) :
    QListWidgetItem(str), combo_(combo), isTitle_(false), selectable_(true){

}

void CustomComboItem::setIsTitle(bool b) {
    isTitle_ = b;

    if (isTitle_)
        setSelectable(false);
}


void CustomComboItem::setSelectable(bool selectable) {
    selectable_ = selectable;

    if (selectable_)
        setFlags(flags() |  (Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled));
    else
        setFlags(flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled));
}

//CustomComboDelegate
//------------------------------------------------------------------------------------------------------------
CustomComboDelegate::CustomComboDelegate(CustomCombo *combo) :combo_(combo) {

}

void CustomComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
           const QModelIndex &index) const {
    auto *item = static_cast<CustomComboItem *>(combo_->list()->item(index.row()));

    QStyleOptionMenuItem opt = getStyleOption(option, index, item);

    painter->fillRect(opt.rect, opt.palette.window());

    combo_->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, combo_);
}

QSize CustomComboDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
    auto *item = static_cast<CustomComboItem *>(combo_->list()->item(index.row()));

    QStyleOptionMenuItem opt = getStyleOption(option, index, item);

    return combo_->style()->sizeFromContents(QStyle::CT_MenuItem, &opt, option.rect.size(), combo_);
}

QStyleOptionMenuItem CustomComboDelegate::getStyleOption(const QStyleOptionViewItem &option,
                                    const QModelIndex &index, CustomComboItem *item) const {
    QStyleOptionMenuItem menuOption;

    QPalette resolvedpalette = option.palette.resolve(QApplication::palette("QMenu"));

    QBrush textBrush = resolvedpalette.brush(QPalette::Active, QPalette::Text);

    QVariant value = index.data(Qt::ForegroundRole);

    if (value.canConvert<QBrush>())
        textBrush = qvariant_cast<QBrush>(value);

    if (! item->isSelectable())
        textBrush = resolvedpalette.brush(QPalette::Disabled, QPalette::Text);

    if (item->isTitle())
        textBrush = resolvedpalette.brush(QPalette::Active, QPalette::HighlightedText);

    resolvedpalette.setBrush(QPalette::WindowText, textBrush);
    resolvedpalette.setBrush(QPalette::ButtonText, textBrush);
    resolvedpalette.setBrush(QPalette::Text      , textBrush);

    menuOption.palette = resolvedpalette;
    menuOption.state   = QStyle::State_None;

    if (combo_->window()->isActiveWindow())
        menuOption.state = QStyle::State_Active;

    if ((option.state & QStyle::State_Enabled) && (index.model()->flags(index) & Qt::ItemIsEnabled))
        menuOption.state |= QStyle::State_Enabled;
    else
        menuOption.palette.setCurrentColorGroup(QPalette::Disabled);

    if (option.state & QStyle::State_Selected)
        menuOption.state |= QStyle::State_Selected;

    menuOption.checkType = QStyleOptionMenuItem::NonExclusive;
    menuOption.checked   = (combo_->currentIndex() == index.row());

    if (isSeparator(index))
        menuOption.menuItemType = QStyleOptionMenuItem::Separator;
    else
        menuOption.menuItemType = QStyleOptionMenuItem::Normal;

    QBrush bgBrush = menuOption.palette.brush(QPalette::Window);

    if (index.data(Qt::BackgroundRole).canConvert<QBrush>())
        bgBrush = qvariant_cast<QBrush>(index.data(Qt::BackgroundRole));

    if (item->isTitle())
        bgBrush = resolvedpalette.brush(QPalette::Active, QPalette::Highlight);

    menuOption.palette.setBrush(QPalette::All, QPalette::Window, bgBrush);

    menuOption.text = index.model()->data(index, Qt::DisplayRole).toString();

    menuOption.tabWidth     = 0;
    menuOption.maxIconWidth = option.decorationSize.width() + 4;
    menuOption.menuRect     = option.rect;
    menuOption.rect         = option.rect;
    menuOption.font         = combo_->font();
    menuOption.fontMetrics  = QFontMetrics(menuOption.font);

    if (item->isTitle())
        menuOption.font.setBold(true);

    return menuOption;
}

        以上本文实现自定义ComboBox的方法,实际上自定义下拉列表可以根据具体的需求进行定制和扩展,适应不同的应用场景。

五、使用示例

以下是一个简单的示例代码,演示了如何在Qt中使用此控件:

#include "mainwindow.h"
#include "CustomCombo.h"
#include <QVBoxLayout>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QWidget *pWidget = new QWidget;
    QHBoxLayout *layout = new QHBoxLayout(pWidget);

    CustomCombo *combo = new CustomCombo;
    combo->addTitle("文学类");
    combo->addItem ("红楼梦");
    combo->addItem ("三国演义");
    combo->addItem ("背影");
    combo->addItem ("荷塘月色");

    combo->addTitle("科技类");
    combo->addItem ("几何原本");
    combo->addItem ("自然哲学的数学原理");
    combo->addItem ("天工开物");
    combo->addItem ("梦溪笔谈");

    combo->setSelectable(3, false);//禁用选项

    layout->addWidget(combo);
    setCentralWidget(pWidget);
}

MainWindow::~MainWindow()
{
}

        总结一下,自定义下拉列表是Qt中常用的控件之一,用于在界面中显示下拉选择项。一般来说,自定义下拉列表可以分为以下几个步骤:

  1. 创建下拉列表按钮:使用QPushButton或QLineEdit等Qt提供的控件作为下拉列表的按钮,用于显示当前选择的选项。

  2. 创建下拉列表框:使用QFrame或QListWidget等Qt提供的控件作为下拉列表的框,用于显示所有选项。

  3. 设置下拉列表框属性:可以设置下拉列表框的大小、位置、边框等属性,以及确定是否默认展开。

  4. 添加选项:使用addItem()或insertItem()等方法向下拉列表框中添加选项。

  5. 设置选项属性:可以设置选项的文本、图标、状态等属性。

  6. 处理选项选择事件:可以通过重写下拉列表的事件处理函数来实现对选项的选择。

  7. 更新按钮显示:在选项被选择后,需要更新按钮的显示文本,以反映所选选项。

        谢谢您的阅读,希望本文能为您带来一些帮助和启发。如果您有任何问题或意见,请随时与我联系。祝您度过美好的一天!

六、源代码下载
  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值