QComboBox设置下拉框为表格

前言

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的宽度,以达到自动填充整个下拉框的,在网上有搜到两种解决办法:

http://blog.csdn.net/rabinsong/article/details/13161799

一种是重写QAbstractItemModel::data(QModelIndex, int role)方法,当role等于Qt::sizeHintRole时返回大小,但是我将所有的role都打印出来都没有Qt::sizeHintRole,所以这个方法失效,(注意,当QTableView改成QListView时这种方式有效),第二种是重写QStyledItemDelegate的sizeHint()方法返回宽高,这个方法也没有调用,关于sizeHint()方法的作用:

http://blog.csdn.net/ggicci/article/details/8116963

总之,这种常规的两种方式都失效了,后来又看到一种方法:

http://www.cnblogs.com/csuftzzk/p/QTableView.html

总结

看样子QTableView和QListView、QTreeView有点不一样,它设置列宽不是通过角色设置的,而是直接调用setColumnWidth()方法设置的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章