QML QtQuick 2 TableView的使用:展示表格數據

(更新日期:2020-2-7)

1.寫在前面

在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的基本操作,即數據的展示和編輯。下面是效果圖(樣式隨意寫的,不同的顏色便於調試時區分):

代碼主要由兩部分,一是自定義 TableView(包括 item 的 delegate ,滾動條、列寬拖動等),二是使用C++定義一個TableModel(數據通過 json 格式轉換)。這裏面比較有意思的就是 TableView 用 xxxWidthProvider 使用JS函數來做回調,用於設置行列寬高。

2.實現代碼

話不多說,直接上代碼:

(git鏈接:https://github.com/gongjianbo/QmlModelView.git

(先是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
        horHeader: ["Id","Name","Age","Note"]
        initData: [
            {"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是在C++中定義的,和QtC++是類似的
        model: table_model
        delegate: Rectangle{
            color: (model.row%2)?"orange":Qt.darker("orange")
            TextInput{
                anchors.fill: parent
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
                //elide: Text.ElideRight
                selectByMouse: true
                selectedTextColor: "black"
                selectionColor: "white"

                //獲取單元格對應的值
                text: model.value
                onEditingFinished: {
                    model.edit=text;
                    console.log("edit",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>

//tableview的簡易model
class EasyTableModel : public QAbstractTableModel, public QQmlParserStatus
{
    Q_OBJECT
    Q_INTERFACES(QQmlParserStatus)
    Q_PROPERTY(QStringList horHeader READ getHorHeader WRITE setHorHeader NOTIFY horHeaderChanged)
    Q_PROPERTY(QJsonArray initData READ getInitData WRITE setInitData NOTIFY initDataChanged)

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

    QStringList getHorHeader() const;
    void setHorHeader(const QStringList &header);

    QJsonArray getInitData() const;
    void setInitData(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;


private:
    void loadData(const QJsonArray &data);

signals:
    void horHeaderChanged();
    void initDataChanged();

private:
    // 組件是否初始化完成
    bool _completed=false;
    // 加載的數據
    QJsonArray _initData;
    // 數據,我一般純展示,用vector就行了
    QVector<QVector<QVariant>> _modelData;
    // 橫項表頭
    QList<QString> _horHeaderList;
};

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

#include <QDebug>

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

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

void EasyTableModel::setHorHeader(const QStringList &header)
{
    _horHeaderList=header;
    emit horHeaderChanged();
}

QJsonArray EasyTableModel::getInitData() const
{
    return _initData;
}

void EasyTableModel::setInitData(const QJsonArray &jsonArr)
{
    _initData=jsonArr;
    if(_completed){
        loadData(_initData);
    }
    emit initDataChanged();
}

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

void EasyTableModel::componentComplete()
{
    qDebug()<<"EasyTableModel::componentComplete()";
    _completed=true;
    if(!_initData.isEmpty()){
        loadData(_initData);
    }
}

QHash<int, QByteArray> EasyTableModel::roleNames() const
{
    //value表示取值,edit表示編輯
    return QHash<int,QByteArray>{
        { Qt::DisplayRole,"value" },
        { Qt::EditRole,"edit" }
    };
}

QVariant EasyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    //返回表頭數據,無效的返回None
    if(role==Qt::DisplayRole){
        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:
        return _modelData.at(index.row()).at(index.column());
    default:
        break;
    }
    return QVariant();
}

bool EasyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (value.isValid()&&index.isValid()&&(data(index, role) != value)) {
        if(Qt::EditRole==role){
            _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;
}

void EasyTableModel::loadData(const QJsonArray &data)
{
    //如果要區分類型的話,可以用role,
    //這樣ui中就能使用model.role來獲取對應index的參數
    QVector<QVector<QVariant>> newData;
    QJsonArray::const_iterator iter;
    for(iter=data.begin();iter!=data.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);
    }

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

 

//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();
}

 

發佈了106 篇原創文章 · 獲贊 31 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章