Qt表格排序例子

表格排序是Qt内建支持的,用起来很简单。只需要在QtCreator界面给QTableView或QTableWidget的SortingEnabled属性设置为true就行了。

本文将对这两种控件分别展示一下效果和一个自定义的排序例子。

一、QTableWidget

这个不需要任何代码,只需要设计界面时候启用排序就行了。下面直接截图:

二、QTableView

这种方式需要自己写代码,下面给出一个6行3列的例子。代码中QtTest是主窗口类,ui.tbContent是QTableView控件:

QtTest::QtTest(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    QStandardItemModel* myModel = new QStandardItemModel(ui.tbContent);
    myModel->setHorizontalHeaderLabels({ u8"列1", u8"列2", u8"列3", });
    myModel->setVerticalHeaderLabels({ u8"行1", u8"行2", u8"行3", u8"行4", u8"行5", u8"行6",});
    for (int i = 0; i < 6; i++)
    {
        QStandardItem *col1 = new QStandardItem();
        col1->setData(1000 + i, Qt::DisplayRole);
        myModel->setItem(i, 0, col1);
        QStandardItem *col2 = new QStandardItem();
        col2->setData(u8"dsaasf" + QString::number(i), Qt::DisplayRole);
        myModel->setItem(i, 1, col2);
        QStandardItem *col3 = new QStandardItem();
        col3->setData(32.123f + 2.5f * i, Qt::DisplayRole);
        myModel->setItem(i, 2, col3);
    }
    ui.tbContent->setSortingEnabled(true);
    ui.tbContent->setModel(myModel);
}

Qt排序的原理是对Model中的数据排序,然后按照新顺序重新显示数据。程序运行之后的界面如下图:

三、自定义排序

在Qt帮助中没有说明默认的排序的细节。不同于标准库里的std::sort(...)直接说明了它引用的是operator<(...)函数。Qt没有这样说,所以大概率是不能重载operator<(...)函数实现对自定义数据的排序。那怎么让自定义的数据支持排序功能呢?放心,Qt提供了其它的方式实现自己的排序功能。就是

  • 继承QAbstractItemModel并实现它的sort(...)函数,或者;
  • 用QSortFilterProxyModel包含你的Model然后实现其lessThan(...)函数,这个代理模型功能非常强大除了排序之外还支持数据查找过滤的功能。

下面将给出一个例子阐述如何自定义排序。该例子使用第一种方法。作者也是第一次做这个功能,对下方代码是否地道的把握不是很大,可能有些不合理之处。主要是QAbstractTableModel里的虚函数很多是默认空函数,所以不太清楚它们的功能和使用时机。比如:下方的函数beginResetModel()和endResetModel()什么时候使用Qt帮助也没说的很明确。这里就认为只要Model中的数据改变就调用。头文件:

class QStandardItem;

class MItemModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    struct MyDataType
    {
        int type;
    };

public:
    MItemModel(QObject* parent = 0);
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

private:
    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;

private:
    QVector<QVector<QStandardItem*>> datas;
};

Q_DECLARE_METATYPE(MItemModel::MyDataType);

class MyDataDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    MyDataDelegate(QObject* parent = 0);
    QString displayText(const QVariant &value, const QLocale &locale) const override;
};

CPP文件:

MItemModel::MItemModel(QObject* parent) : 
    QAbstractTableModel(parent)
{
}

MItemModel::~MItemModel()
{
    for (auto& item : datas)
    {
        qDeleteAll(item);
    }
}

QModelIndex MItemModel::index(int row, int column, const QModelIndex &parent) const
{
    return createIndex(row, column, nullptr);
}

int MItemModel::rowCount(const QModelIndex &parent) const
{
    return datas.size();
}

int MItemModel::columnCount(const QModelIndex &parent) const
{
    auto maxv = std::max_element(datas.begin(), datas.end(), 
        [](const QVector<QStandardItem*>& x, const QVector<QStandardItem*>& y) { return x.size() < y.size(); });
    return maxv->size();
}

QVariant MItemModel::data(const QModelIndex &index, int role) const
{
    const QVector<QStandardItem*>& rowData = datas[index.row()];
    if (index.column() < rowData.size() && rowData[index.column()])
    {
        return rowData[index.column()]->data(role);
    }
    return QVariant();
}

bool MItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    int r = index.row();
    int c = index.column();
    if (r >= datas.size())
    {
        datas.resize(r + 1);
    }
    if (c >= datas[r].size())
    {
        datas[r].resize(c + 1);
    }
    if (!datas[r][c])
    {
        datas[r][c] = new QStandardItem;
    }
    datas[r][c]->setData(value, role);
    return true;
}

/* 这是行头和列头 */
QVariant MItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole)
    {
        if (orientation == Qt::Orientation::Horizontal)
        {
            QString header = u8"";
            return header + QString::number(section + 1);
        }
        if (orientation == Qt::Orientation::Vertical)
        {
            QString header = u8"";
            return header + QString::number(section + 1);
        }
    }
    return QVariant();
}

void MItemModel::sort(int column, Qt::SortOrder order)
{
    if (column != 0)
    {
        /* Qt默认为空,点击其他列是不会排序的 */
        QAbstractTableModel::sort(column, order);
        return;
    }
    beginResetModel();
    /* 这里使用的是选择排序 */
    int count = rowCount();
    for (int i = 0; i < count; i++)
    {
        int maxi = i;
        MyDataType maxv = data(index(i, column), Qt::DisplayRole).value<MyDataType>();
        for (int j = i + 1; j < count; j++)
        {
            MyDataType curr = data(index(j, column), Qt::DisplayRole).value<MyDataType>();
            if ((curr.type < maxv.type) ^ (order == Qt::DescendingOrder))
            {
                maxv = curr;
                maxi = j;
            }
        }
        if (maxi != i)
        {
            std::swap(datas[maxi], datas[i]);
        }
    }
    endResetModel();
}

/////////////////////////////////////////////////////////////////////////////////////////

MyDataDelegate::MyDataDelegate(QObject* parent) : 
    QStyledItemDelegate(parent)
{
}

QString MyDataDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
    MItemModel::MyDataType data = value.value<MItemModel::MyDataType>();
    return u8":" + QString::number(data.type);
}

主函数中初始化。代码中的QtTest是主窗口类,ui.tvContent是QTableView类:

QtTest::QtTest(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    
    MItemModel* myModel = new MItemModel(ui.tvContent);
    for (int i = 0; i < 6; i++)
    {
        myModel->setData(myModel->index(i, 0), QVariant::fromValue<MItemModel::MyDataType>({ 2 + i }), Qt::DisplayRole);
        myModel->setData(myModel->index(i, 1), u8"实例", Qt::DisplayRole);
        myModel->setData(myModel->index(i, 2), 324 + i, Qt::DisplayRole);
    }
    ui.tvContent->setModel(myModel);
    ui.tvContent->setItemDelegateForColumn(0, new MyDataDelegate(ui.tvContent));
    ui.tvContent->setSortingEnabled(true);
}

下面是运行时候的截图:

 

posted @ 2024-03-04 09:57  兜尼完  阅读(61)  评论(0编辑  收藏  举报