在Qt中,ComboBox(组合框)是一种常用的用户界面控件,它提供了一个下拉列表,允许用户从预定义的选项中选择一个。在项目开发中,如果简单的QComboBox无法满足需求,可以通过自定义QComboBox来实现更复杂的功能。本文介绍一个自定义的下拉列表,并为选项设置标题、可禁用选项。
一、简述
本示例介绍一个Qt自定义下拉列表,可为选项设置标题、可禁用选项。
二、 设计思路
-
继承QComboBox类: 创建一个新的类,继承自QComboBox类。在该类中,可以重写QComboBox的方法以实现自定义功能。
-
使用QListWidget和QListWidgetItem: QComboBox默认使用QStandardItemModel来管理下拉列表的数据。扩展时使用QListWidget的QListModel来替代QStandardItemModel。在QListWidget的QListWidgetItem中,可以自定义每个列表项的显示和交互逻辑。
-
使用自定义代理类: 通过自定义QItemDelegate类,可以实现对下拉列表中每个项的自定义绘制和交互逻辑。可以通过setItemDelegate方法将自定义的代理类设置为QComboBox的项代理。
三、效果
四、核心代码
1、头文件
#ifndef CustomCombo_H
#define CustomCombo_H#include <QComboBox>
#include <QListWidget>
#include <QItemDelegate>class CustomCombo : public QComboBox
{Q_OBJECTpublic: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));elsesetFlags(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;elsemenuOption.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;elsemenuOption.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中常用的控件之一,用于在界面中显示下拉选择项。一般来说,自定义下拉列表可以分为以下几个步骤:
-
创建下拉列表按钮:使用QPushButton或QLineEdit等Qt提供的控件作为下拉列表的按钮,用于显示当前选择的选项。
-
创建下拉列表框:使用QFrame或QListWidget等Qt提供的控件作为下拉列表的框,用于显示所有选项。
-
设置下拉列表框属性:可以设置下拉列表框的大小、位置、边框等属性,以及确定是否默认展开。
-
添加选项:使用addItem()或insertItem()等方法向下拉列表框中添加选项。
-
设置选项属性:可以设置选项的文本、图标、状态等属性。
-
处理选项选择事件:可以通过重写下拉列表的事件处理函数来实现对选项的选择。
-
更新按钮显示:在选项被选择后,需要更新按钮的显示文本,以反映所选选项。
谢谢您的阅读,希望本文能为您带来一些帮助和启发。如果您有任何问题或意见,请随时与我联系。祝您度过美好的一天!