在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_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中常用的控件之一,用于在界面中显示下拉选择项。一般来说,自定义下拉列表可以分为以下几个步骤:
-
创建下拉列表按钮:使用QPushButton或QLineEdit等Qt提供的控件作为下拉列表的按钮,用于显示当前选择的选项。
-
创建下拉列表框:使用QFrame或QListWidget等Qt提供的控件作为下拉列表的框,用于显示所有选项。
-
设置下拉列表框属性:可以设置下拉列表框的大小、位置、边框等属性,以及确定是否默认展开。
-
添加选项:使用addItem()或insertItem()等方法向下拉列表框中添加选项。
-
设置选项属性:可以设置选项的文本、图标、状态等属性。
-
处理选项选择事件:可以通过重写下拉列表的事件处理函数来实现对选项的选择。
-
更新按钮显示:在选项被选择后,需要更新按钮的显示文本,以反映所选选项。
谢谢您的阅读,希望本文能为您带来一些帮助和启发。如果您有任何问题或意见,请随时与我联系。祝您度过美好的一天!