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()方法設置的

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