前言
QComboBox是個系統提供的帶有下拉框的控件,樣式比較單一,默認的下拉框是以列表形式,如下圖:
這是一般情況,但是在某些特殊的場景之下我們需要下拉框顯示錶格,剛好QComboBox給我們提供了方便
api文檔
我們看一下QComboBox的文檔:
void QComboBox::setView(QAbstractItemView * itemView)
Sets the view to be used in the combobox popup to the given itemView. The combobox takes ownership of the view.
Note: If you want to use the convenience views (like QListWidget, QTableWidget or QTreeWidget), make sure to call setModel() on the combobox with the convenience widgets model before calling this function.
QComboBox的這個方法可以設置下拉框顯示的控件,它的參數是QAbstractItemView,我們都知道QTableView、QTreeView、QListView都是從這個類繼承出來的,那麼也就是說,這三個都可以用於QComboBox類的下拉框了。
注意,使用這個方法之前必須先調用QComboBox::setModel方法
QComboBox::setModel():
void QComboBox::setModel(QAbstractItemModel * model)
Sets the model to be model. model must not be 0. If you want to clear the contents of a model, call clear().
定義model/View的方式跟使用普通的表格控件差不多,不同的只是,原先我們將model交給view,現在將兩個都交給QComboBox。
代碼
既然上面所說model的調用必須在view前面,所以簡單的用了一下建造者模式,下面具體上代碼:
首先是自定義ComboBoxTableView的代碼:
#include <QComboBox>
#include "constructorexception.h"
class ComboBoxTableView : public QComboBox
{
Q_OBJECT
private:
explicit ComboBoxTableView(QWidget *parent = 0);
signals:
void resize_signal(QRect );
public slots:
public:
void resizeEvent(QResizeEvent *);
// void ressizeView(QRect r);
//內部類,用來建造ComboBoxTableView
class ComboBoxBuild
{
public:
ComboBoxBuild(QWidget *parent) :
parent(parent),
comboBox(NULL),
view(NULL),
model(NULL)
{
}
void setView(QAbstractItemView *itemView)
{
this->view = itemView;
}
void setModel(QAbstractItemModel *model)
{
this->model = model;
}
ComboBoxTableView *build() throw(ConstructorException)
{
if( !model || !view)
throw ConstructorException("model或這個View不能爲空!");
comboBox = new ComboBoxTableView(parent);
comboBox->setModel(model);
comboBox->setView(view);
comboBox->view = view;
comboBox->model = model;
return comboBox;
}
private:
QWidget *parent;
ComboBoxTableView *comboBox;
QAbstractItemView *view;
QAbstractItemModel *model;
};
private:
QAbstractItemView *view;
QAbstractItemModel *model;
};
comboBoxtableView.cpp文件的內容:
#include "comboboxtableview.h"
#include <QDebug>
#include <QTableView>
ComboBoxTableView::ComboBoxTableView(QWidget *parent) :
QComboBox(parent)
{
}
void ComboBoxTableView::resizeEvent(QResizeEvent *e)
{
qDebug() << "geometry:" << geometry();
int row = model->rowCount();
int column = model->columnCount();
int width = geometry().width();
int cell = width / column;
QTableView *view = static_cast<QTableView *>(this->view);
for(int i = 0; i < column; i++)
{
view->setColumnWidth(i, cell);
}
emit resize_signal(geometry());
}
上面的代碼比較簡單,沒什麼需要說的,先將構造ComboBoxTableView所需要的model/view先交給ComboBoxBuild,真正構造ComboBoxTableView的代碼在build裏面,這樣的話就不會出現View在model之前被調用了。
TableModel的定義:
#include <QAbstractItemModel>
typedef QList<QString> StringList;
/**
* @brief The TableModel class
* 表格model,自定義model繼承QAbstractItemModel
*/
class TableModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit TableModel(QObject *parent = 0);
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
void setList(StringList *list, int row, int column);
signals:
public slots:
private:
StringList *list;
int row, column; //多少行、多少列
};
TableModel.cpp文件:
#include "tablemodel.h"
#include <QDebug>
#include <QColor>
#include <QSize>
TableModel::TableModel(QObject *parent) :
QAbstractItemModel(parent)
{
}
//list的setter方法
void TableModel::setList(StringList *list, int row, int column)
{
this->list = list;
this->column = column;
this->row = row;
}
//建立索引
QModelIndex TableModel::index(int row, int column, const QModelIndex &parent) const
{
int i = row * this->column + column;
return createIndex(row, column);
}
QModelIndex TableModel::parent(const QModelIndex &child) const
{
return QModelIndex();
}
int TableModel::rowCount(const QModelIndex &parent) const
{
return row;
}
int TableModel::columnCount(const QModelIndex &parent) const
{
return column;
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if( role == Qt::CheckStateRole)
return QVariant();
if( role == Qt::TextAlignmentRole)
return (Qt::AlignVCenter);
if( role == Qt::ForegroundRole)
return QColor(250, 150, 150);
int row = index.row();
int column = index.column();
int i = row * this->column + column;
return list->at(i);
}
上面是自定義model的代碼,關於這方面的學習可以參考《qt學習之路2》
https://www.devbean.net/2013/05/qt-study-road-2-bool-tree-model/
現在看看我使用的代碼:
QWidget *dictWidget = new QWidget;
QVBoxLayout *dicLayout = new QVBoxLayout(dictWidget);
dictWidget->setLayout(dicLayout);
QTableView *view = new QTableView(dictWidget);
//準備數據
StringList *list = new QStringList;
list->append("漢-->英");
list->append("英-->漢");
list->append("漢-->日");
list->append("日-->漢");
list->append("漢-->韓");
list->append("韓-->漢");
list->append("漢-->法");
list->append("法-->漢");
list->append("漢-->俄");
list->append("俄-->漢");
list->append("漢-->西");
list->append("西-->漢");
TableModel *model = new TableModel;
model->setList(list, 3, 4);
//隱藏表頭
view->horizontalHeader()->setVisible(false);
view->verticalHeader()->setVisible(false);
view->setAlternatingRowColors(true);
ComboBoxTableView::ComboBoxBuild *build = new ComboBoxTableView::ComboBoxBuild(dictWidget);
build->setModel(model);
build->setView(view);
comboBox = build->build();
dicLayout->addWidget(comboBox);
效果
這個是效果,使用上面的代碼,可以完美的顯示一個表格,至於顯示的顏色、對齊、等等其他設置可以在自定義的TableModel::data()函數裏面根據角色返回不同的值,就可以達到設置的目的
使用過程中遇到的問題
在使用QTableView的過程中,本來設置每一個item的寬度,以達到自動填充整個下拉框的,在網上有搜到兩種解決辦法:
一種是重寫QAbstractItemModel::data(QModelIndex, int role)方法,當role等於Qt::sizeHintRole時返回大小,但是我將所有的role都打印出來都沒有Qt::sizeHintRole,所以這個方法失效,(注意,當QTableView改成QListView時這種方式有效),第二種是重寫QStyledItemDelegate的sizeHint()方法返回寬高,這個方法也沒有調用,關於sizeHint()方法的作用:
總之,這種常規的兩種方式都失效了,後來又看到一種方法:
總結
看樣子QTableView和QListView、QTreeView有點不一樣,它設置列寬不是通過角色設置的,而是直接調用setColumnWidth()方法設置的