QML QtQuick 2 TableView的使用:展示表格数据

在Qt5.12中,QtQuick 2 添加了 TableView 组件,功能和 QtQuick Control 1 中的 TableView 类似,但是接口大不一样(QtQuick Control 1已经处于弃用状态,不建议使用)。

Qt Creator中有两个 QtQuick 2 TableView 的示例,但是都不是数据类型的,参考起来不大方便,我也是别人的 Demo 以及 Qt 文档学习了下(参考链接 https://github.com/yuriyoung/qml-examples)。文本主要是用代码演示TableView的基本操作,即数据的展示,对于编辑或者不同类型的 item delegate 会写另外的博客来记录。下面是效果图(样式随意写的,不同的颜色便于调试时区分):

代码主要由两部分,一是自定义 TableView(包括 item 的 delegate ,滚动条、列宽拖动等),二是使用C++定义一个TableModel(数据通过 json 格式转换)。这里面比较有意思的就是 TableView 用 xxxWidthProvider 使用JS函数来做回调,用于设置行列宽高。

话不多说,直接上代码:

(先是QML部分)

//TableWidget.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import EasyModel 1.0

//自定义QtQuick 2中的TableView
Item {
    id: control
    implicitHeight: 300
    implicitWidth: 500

    //行表头-竖向的
    property int verHeaderHeight: 30
    property int verHeaderWidth: 30
    //列表头-横向的
    property int horHeaderHeight: 30
    //property int horHeaderWidth: 30
    //滚动条
    property color scrollBarColor: "cyan"
    property int scrollBarWidth: 6
    //列宽
    property variant columnWidthArr: [100,100,100,200]

    EasyTableModel{
        id: table_model
        horHeaders: ["Id","Name","Age","Note"]
        datas: [
            {"id":1,"name":"gonge","age":20,"note":"test model view"},
            {"id":2,"name":"gonge","age":21,"note":"test model view"},
            {"id":3,"name":"gonge","age":22,"note":"test model view"},
            {"id":4,"name":"gonge","age":23,"note":"test model view"},
            {"id":5,"name":"gonge","age":24,"note":"test model view"},
            {"id":6,"name":"gonge","age":25,"note":"test model view"},
            {"id":7,"name":"gonge","age":26,"note":"test model view"},
            {"id":8,"name":"gonge","age":27,"note":"test model view"}
        ]
    }

    //表格内容(不包含表头)
    TableView{
        id: table_view
        anchors{
            fill: parent
            leftMargin: control.verHeaderWidth
            topMargin: control.horHeaderHeight
        }

        clip: true
        boundsBehavior: Flickable.StopAtBounds
        columnSpacing: 1
        rowSpacing: 1
        //视图的高度
        //contentHeight: rowHeightProvider(0) * rows + rowHeightProvider(rows-1)
        //视图的宽度
        //contentWidth:
        //content内容区域边距,但是不影响滚动条的位置
        //leftMargin:
        //topMargin:
        //此属性可以包含一个函数,该函数返回模型中每行的行高
        rowHeightProvider: function (row) {
            return control.verHeaderHeight;
        }
        //此属性可以保存一个函数,该函数返回模型中每个列的列宽
        columnWidthProvider: function (column) {
            return control.columnWidthArr[column];
            //return Math.max(1, (table_view.width - leftMargin) / table_view.columns)
        }
        ScrollBar.vertical: ScrollBar {
            id: scroll_vertical
            anchors.right: parent.right
            anchors.rightMargin: 2
            //active: table_view.ScrollBar.vertical.active
            //policy: ScrollBar.AsNeeded
            contentItem: Rectangle{
                visible: (scroll_vertical.size<1.0)
                implicitWidth: control.scrollBarWidth
                color: control.scrollBarColor
            }
        }

        ScrollBar.horizontal: ScrollBar {
            id: scroll_horizontal
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 2
            //active: table_view.ScrollBar.vertical.active
            //policy: ScrollBar.AsNeeded
            contentItem: Rectangle{
                visible: (scroll_horizontal.size<1.0)
                implicitHeight: control.scrollBarWidth
                color: control.scrollBarColor
            }
        }
        model: table_model
        delegate: Rectangle{
            color: (model.row%2)?"orange":Qt.darker("orange")
            Text{
                anchors.fill: parent
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
                elide: Text.ElideRight
                text: model.value
            }
        }
    }

    //横项表头
    Item{
        id: header_horizontal
        anchors{
            left: parent.left
            right: parent.right
            leftMargin: control.verHeaderWidth
        }
        height: control.horHeaderHeight
        z: 2
        property int posXTemp: 0
        MouseArea{
            anchors.fill: parent
            onPressed: header_horizontal.posXTemp=mouseX;
            onPositionChanged: {
                if(table_view.contentX+(header_horizontal.posXTemp-mouseX)>0){
                    table_view.contentX+=(header_horizontal.posXTemp-mouseX);
                }else{
                    table_view.contentX=0;
                }
                header_horizontal.posXTemp=mouseX;
            }
        }
        Row {
            id: header_horizontal_row
            anchors.fill: parent
            leftPadding: -table_view.contentX
            clip: true
            spacing: 0

            Repeater {
                model: table_view.columns > 0 ? table_view.columns : 0

                Rectangle {
                    id: header_horizontal_item
                    width: table_view.columnWidthProvider(index)+table_view.columnSpacing
                    height: control.horHeaderHeight
                    color: "purple"

                    Text {
                        anchors.centerIn: parent
                        text: table_model.headerData(index, Qt.Horizontal)
                    }
                    Rectangle{
                        width: 1
                        height: parent.height
                        anchors.right: parent.right
                        color: "black"
                        opacity: 0.5
                    }
                    MouseArea{
                        width: 3
                        height: parent.height
                        anchors.right: parent.right
                        cursorShape: Qt.SplitHCursor
                        onPressed: header_horizontal.posXTemp=mouseX;
                        onPositionChanged: {
                            if((header_horizontal_item.width-(header_horizontal.posXTemp-mouseX))>10){
                                header_horizontal_item.width-=(header_horizontal.posXTemp-mouseX);
                            }else{
                                header_horizontal_item.width=10;
                            }
                            header_horizontal.posXTemp=mouseX;
                            control.columnWidthArr[index]=(header_horizontal_item.width-table_view.columnSpacing);
                            //刷新布局,这样宽度才会改变
                            table_view.forceLayout();
                        }
                    }
                }
            }
        }
    }

    //竖向表头
    Column {
        id: header_verical
        anchors{
            top: parent.top
            bottom: parent.bottom
            topMargin: control.horHeaderHeight
        }
        topPadding: -table_view.contentY
        z: 2
        clip: true
        spacing: 1
        Repeater {
            model: table_view.rows > 0 ? table_view.rows : 0
            Rectangle {
                width: control.verHeaderWidth
                height: table_view.rowHeightProvider(index)
                color: "green"
                Text {
                    anchors.centerIn: parent
                    text: table_model.headerData(index, Qt.Vertical)
                }
            }
        }
    }

}
//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("QQ群:647637553")

    Rectangle{
        anchors.fill: parent
        anchors.margins: 20
        color: "gray"

        TableWidget{
            anchors.fill: parent
        }
    }
}

(接下来是C++定义的TableModel)

//EasyTableModel.h
#ifndef EASYTABLEMODEL_H
#define EASYTABLEMODEL_H

#include <QAbstractTableModel>
#include <QQmlParserStatus>
#include <QHash>
#include <QList>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>

class EasyTableModel : public QAbstractTableModel, public QQmlParserStatus
{
    Q_OBJECT
    Q_PROPERTY(QStringList horHeaders READ getHorHeaders WRITE setHorHeaders NOTIFY horHeadersChanged)
    Q_PROPERTY(QJsonArray datas WRITE setDatas)

public:
    explicit EasyTableModel(QObject *parent = nullptr);

    QStringList getHorHeaders() const;
    void setHorHeaders(const QStringList &headers);

    void setDatas(const QJsonArray &jsonArr);

    //QQmlParserStatus:构造前
    void classBegin() override;
    //QQmlParserStatus:构造后
    void componentComplete() override;
    //自定义role
    QHash<int,QByteArray> roleNames() const override;

    // 表头
    QVariant headerData(int section, Qt::Orientation orientation, 
int role = Qt::DisplayRole) const override;
    bool setHeaderData(int section, Qt::Orientation orientation, 
const QVariant &value, int role = Qt::EditRole) 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;
    Qt::ItemFlags flags(const QModelIndex& index) const override;

    // Add data:
    //bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
    //bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override;

    // Remove data:
    //bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
    //bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override;

signals:
    void horHeadersChanged();

private:
    //横项表头
    QList<QString> _horHeaderList;
    //数据,我一般纯展示,用vector就行了
    QVector<QVector<QVariant>> _modelData;
};

#endif // EASYTABLEMODEL_H
//EasyTableModel.cpp
#include "EasyTableModel.h"

#include <QDebug>

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

QStringList EasyTableModel::getHorHeaders() const
{
    return _horHeaderList;
}

void EasyTableModel::setHorHeaders(const QStringList &headers)
{
    _horHeaderList=headers;
    emit horHeadersChanged();
}

void EasyTableModel::setDatas(const QJsonArray &jsonArr)
{
    //如果要区分类型的话,可以用role,
    //这样ui中就能使用model.role来获取对应index的参数
    QVector<QVector<QVariant>> newData;
    QJsonArray::const_iterator iter;
    for(iter=jsonArr.begin();iter!=jsonArr.end();++iter){
        QVector<QVariant> newRow;
        const QJsonObject itemRow=(*iter).toObject();
        newRow.append(itemRow.value("id"));
        newRow.append(itemRow.value("name"));
        newRow.append(itemRow.value("age"));
        newRow.append(itemRow.value("note"));
        newData.append(newRow);
    }

    beginResetModel();
    _modelData=newData;
    endResetModel();
}

void EasyTableModel::classBegin()
{
    qDebug()<<"EasyTableModel::classBegin()";
}

void EasyTableModel::componentComplete()
{
    qDebug()<<"EasyTableModel::componentComplete()";
}

QHash<int, QByteArray> EasyTableModel::roleNames() const
{
    return QHash<int,QByteArray>{
        { Qt::UserRole+1,"value" },
        { Qt::UserRole+2,"type" }
    };
}

QVariant EasyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    //返回表头数据,无效的返回None
    if(role==Qt::DisplayRole||role==Qt::EditRole){
        if(orientation==Qt::Horizontal){
            return _horHeaderList.value(section,QString::number(section));
        }else if(orientation==Qt::Vertical){
            return QString::number(section);
        }
    }
    return QVariant();
}

bool EasyTableModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
    if (value != headerData(section, orientation, role)) {
        if(orientation==Qt::Horizontal&&role==Qt::EditRole){
            _horHeaderList[section]=value.toString();
            emit headerDataChanged(orientation, section, section);
            return true;
        }
    }
    return false;
}


int EasyTableModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return _modelData.count();
}

int EasyTableModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return _horHeaderList.count();
}

QVariant EasyTableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();
    switch (role) {
    case Qt::DisplayRole:
    case Qt::EditRole:
    case Qt::UserRole+1:
        return _modelData.at(index.row()).at(index.column());
    default:
        break;
    }
    return QVariant();
}

bool EasyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    //if (data(index, role) != value) {
    if (index.isValid()&&role==Qt::EditRole) {
        _modelData[index.row()][index.column()]=value;
        emit dataChanged(index, index, QVector<int>() << role);
        return true;
    }
    return false;
}

Qt::ItemFlags EasyTableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::NoItemFlags;
    return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable;
}

/*
bool EasyTableModel::insertRows(int row, int count, const QModelIndex &parent)
{
    beginInsertRows(parent, row, row + count - 1);
    // FIXME: Implement me!
    endInsertRows();
}

bool EasyTableModel::insertColumns(int column, int count, const QModelIndex &parent)
{
    beginInsertColumns(parent, column, column + count - 1);
    // FIXME: Implement me!
    endInsertColumns();
}

bool EasyTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    beginRemoveRows(parent, row, row + count - 1);
    // FIXME: Implement me!
    endRemoveRows();
}

bool EasyTableModel::removeColumns(int column, int count, const QModelIndex &parent)
{
    beginRemoveColumns(parent, column, column + count - 1);
    // FIXME: Implement me!
    endRemoveColumns();
}*/
//main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "EasyTableModel.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    qmlRegisterType<EasyTableModel>("EasyModel",1,0,"EasyTableModel");

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
发布了95 篇原创文章 · 获赞 26 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/gongjianbo1992/article/details/103555628