qml tableview

一、C++ model

CustomTableModel.h

#pragma once
#include <QAbstractTableModel>
#include <set>


class CustomTableModel : public QAbstractTableModel
{
    Q_OBJECT
    const static int kColumnCnt = 5;
    const static int kInvalidRow = -1;
    enum {
        NodeSelectStateRole = Qt::UserRole + 1,
        NodeFileName,
        NodeSize,
        NodeTime
    };

    enum TableColumn {
        TableColumnCheckbox,
        TableColumnFilefilename,
        TableColumnSize,
        TableColumnTime
    };

    struct FileInfo
    {
        QString qstrFilename;
        QString qstrSize;
        QString qstrTime;
    };

public:
    CustomTableModel(QObject* pParent = nullptr);
    int rowCount(const QModelIndex & = QModelIndex()) const override;
    int columnCount(const QModelIndex & = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    QHash<int, QByteArray> roleNames() const override;
    Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    Q_INVOKABLE void initModel();
    Q_INVOKABLE bool isItemSelected(int nRow) const;
    Q_INVOKABLE QVariant getData(int nRow, int nCol);

private slots:

private:
    QVariant get_data(int row, int role) const;

private:
    std::vector<FileInfo> m_listFile;
    std::set<int> m_setSelect;
    std::vector<QString> vecHeader;

};


CustomTableModel.cpp

#include "CustomTableModel.h"
#include <QDebug>
#include <sstream>




CustomTableModel::CustomTableModel(QObject* pParent/* = nullptr*/)
    : QAbstractTableModel(pParent)
{
    vecHeader.emplace_back(QString(""));
    vecHeader.emplace_back(QString("filenmae"));
    vecHeader.emplace_back(QString("size"));
    vecHeader.emplace_back(QString("time"));
}

int CustomTableModel::rowCount(const QModelIndex &/* = QModelIndex()*/) const
{
    return m_listFile.size();
}

int CustomTableModel::columnCount(const QModelIndex &/* = QModelIndex()*/) const
{
    return kColumnCnt;
}

QVariant CustomTableModel::data(const QModelIndex &index, int role) const
{ 
    return get_data(index.row(), role);
}

QHash<int, QByteArray> CustomTableModel::roleNames() const
{
    return {
        {NodeFileName, "fileName"},
        {NodeSize, "size"},
        {NodeTime,"time"}
    };
}

QVariant CustomTableModel::headerData(int section, Qt::Orientation orientation, int role/* = Qt::DisplayRole*/) const
{
    if(role == Qt::DisplayRole) {
        // horizontal header
        if(orientation == Qt::Horizontal) {
            if(section >= vecHeader.size()) return QVariant();
            return vecHeader[section];
        }
    }
    return QVariant();
}

bool CustomTableModel::isItemSelected(int nRow) const
{
    return m_setSelect.find(nRow) != m_setSelect.end();
}

void CustomTableModel::initModel()
{
    beginResetModel();
    m_listFile.clear();
    m_setSelect.clear();
    for(int i = 0; i < 15; ++i) {
        FileInfo infoFile;
        infoFile.qstrFilename = QString("file") + QString::number(i);
        infoFile.qstrSize = "66 Mb";
        infoFile.qstrTime = "2022-08-02 21:00:00";
        if(i % 2)
            m_setSelect.insert(i);
        m_listFile.emplace_back(infoFile);
    }

    endResetModel();
}

QVariant CustomTableModel::getData(int nRow, int nCol)
{
    return get_data(nRow, nCol + NodeSelectStateRole);
}


QVariant CustomTableModel::get_data(int row, int role) const
{
    switch (role) {
        case NodeSelectStateRole:
            return isItemSelected(row);
        case NodeFileName:
            return m_listFile[row].qstrFilename;
        case NodeSize: {
            return m_listFile[row].qstrSize;
        }
        case NodeTime:
            return m_listFile[row].qstrTime;
        default:
            return QVariant();
        }
    return QString("hello");
}






qml文件

CustomTableview.qml

import QtQuick 2.15
import QtQuick.Controls 2.15



TableView
{
    id: root


    /************************************************************************************************
    *                                       properties                                              *
    *************************************************************************************************/
    // state for load more
    property bool bContentYChanged: false
    // flick stop at bounds
    boundsBehavior: Flickable.StopAtBounds
    clip: true


    // scroll bar for vertical
    ScrollBar.vertical: ScrollBar {
         parent: flickable.parent
         anchors.top: flickable.top
         anchors.left: flickable.right
         anchors.bottom: flickable.bottom
     }


    /************************************************************************************************
    *                                       signals                                                 *
    *************************************************************************************************/
    // triggered on load more
    signal signalLoadMore();




    // width for cell
    //columnWidthProvider: function (column) { return 40; }
    // height for each row
    //rowHeightProvider: function (column) { return 40; }



    /************************************************************************************************
    *                                       connection                                               *
    *************************************************************************************************/
    Connections {
        target: root
        // suitable for width
        function onWidthChanged() {
            // force strench width for tableview
            viewContent.forceLayout();
            //console.log("width changed")
        }

        // check load more(down)
        function onAtYEndChanged() {
            if(bContentYChanged) {
                bContentYChanged = false;
                // load more with last record
                //console.log("need load more triggered, state: " + root.atYEnd)
                if(root.atYEnd)
                    signalLoadMore();
            }
        }

        // triggered on content Y changed
        function onContentYChanged() {
            bContentYChanged = true;
            //console.log("Content Y changed: " + root.contentY)
        }
    }
}

CustomCheckbox.qml

import QtQuick 2.0
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15


Rectangle {
    // border color
    //border.color: "#222222"
    // border width
    //border.width: 0
    color:"#00000000"
    property string imgSource
    property bool clickable:true
    // custom signal
    signal mouseClicked
    signal mouseClickpos(int x, int y)
    signal hovered
    property int radiusBtn : 2
    property bool checked: false
    property string imgChecked;
    property string imgUnchecked;
    // image for button

    Image {
        id: imgButton
        anchors.fill: parent
        anchors.centerIn: parent
        source: parent.checked ? parent.imgChecked: parent.imgUnchecked
        fillMode: Image.PreserveAspectFit
        visible: false
    }

    Rectangle{
        id: mask
        anchors.fill: parent
        radius: radiusBtn
        visible: false
    }
    OpacityMask {
        anchors.fill: parent
        source: imgButton
        maskSource: mask
    }



    // mouse area
    MouseArea
    {
        id: btnMouse
        hoverEnabled: true
        anchors.fill: parent
        // handle style on image button
        //cursorShape: clickable ? Qt.PointingHandCursor : Qt.ArrowCursor
        onClicked: {
            if(!clickable)return
            checked = !checked;
            mouseClicked()
            mouseClickpos(mouse.x,mouse.y)
            updateCheckState(checked);
        }
        onEntered: hovered()
    }
    // set button type
    function setButtonType(bCycle) {
        if(bCycle)
            radius = width /2
    }

    function setMouseShape(bEnable) {
        if(bEnable)
            btnMouse.cursorShape = Qt.PointingHandCursor
        else
            btnMouse.cursorShape = Qt.ArrowCursor
    }

    function setButtonIcon(qstrIcon) {
        imgButton.source = qstrIcon
    }

    function setClickable(enable) {
        clickable = enable;
    }

    function updateCheckState(checked) {
        imgButton.source = checked ? imgChecked: imgUnchecked
        checked = bchecked
        //console.log("update checkbox img: " + checked)
    }
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    FileView {
        anchors.fill: parent
    }


}


FileView.qml

import QtQuick.Controls 2.15
import QtQuick 2.15
import Qt.labs.qmlmodels 1.0
import CustomTableModel 1.0


Rectangle {
    id: root
    enum TableColumn {
        TableColumnCheckbox,
        TableColumnFilefilename,
        TableColumnSize,
        TableColumnTime
    }
    property int widthCheckbox : 40
    property int widthFilename : 180
    property int widthTime : 180
    property int widthSize : 150
    property bool checkedAll: false

    signal selectAll(bool bSelectAll);
    // tableview
    CustomTableview {
        id:viewContent
        anchors.fill: parent
        columnWidthProvider: function (column) {
            var nWidth = widthCheckbox;
            switch(column){
                case FileView.TableColumnCheckbox: {
                    nWidth = widthCheckbox;
                    break;
                }
                case FileView.TableColumnFilefilename:{
                    nWidth = viewContent.width - widthTime - widthCheckbox - widthSize - 150;
                    if(nWidth < 0)
                        nWidth = widthFilename
                    break;
                }
                case FileView.TableColumnSize:{
                    nWidth = widthSize;
                    break;
                }
                case FileView.TableColumnTime:{
                    nWidth = widthTime;
                    break;
                }
                case 4:{
                    nWidth = 150;
                    break;
                }
            }
            //console.log("get column width, col: " + column+ ", width: " + nWidth)
            return nWidth;
        }
        rowHeightProvider: function (column) { return 40; }

        model:modelContent
        // item deletegate
        delegate:DelegateChooser{
            DelegateChoice{
                column: FileView.TableColumnCheckbox
                delegate: Rectangle{
                    width: widthCheckbox //viewContent.columnWidthProvider(FileView.TableColumnCheckbox);
                    //implicitHeight: 32
                    CustomCheckbox {
                        id: checkboxItem
                        width: 20
                        height: 20
                        imgChecked: "qrc:/box_checked@2x.png"
                        imgUnchecked: "qrc:/box_uncheck@2x.png"
                        anchors{
                            centerIn: parent
                            verticalCenter: parent.verticalCenter
                        }
                        checked: modelContent.getData(row, FileView.TableColumnCheckbox)
                        onMouseClicked: {
                            console.log("clicked at index:" + index)
                        }
                        // select all/none
                        Connections {
                            target: root
                            function onSelectAll(bSelectAll) {
                                checkboxItem.updateCheckState(bSelectAll);
                            }
                        }
                    }
                }
            }
            DelegateChoice{
                column: FileView.TableColumnFilefilename
                delegate: Rectangle{
                    id: rectFilename
                    //color: "#666666"
                    width: viewContent.columnWidthProvider(FileView.TableColumnFilefilename);
                    //implicitHeight: 32
                    //border.width: 1
                    //border.color: "#848484"
                    TextMetrics {
                          id: textMetrics
                          text: modelContent.getData(row, FileView.TableColumnFilefilename)
                    }
                    Text {
                        id: textFilename
                        text: modelContent.getData(row, FileView.TableColumnFilefilename)
                        width: viewContent.columnWidthProvider(FileView.TableColumnFilefilename)
                        anchors {
                            left: parent.left
                            right: parent.right
                            top: parent.top
                            bottom: parent.bottom
                        }
                        font.pointSize: 12
                        horizontalAlignment: Text.AlignLeft
                        verticalAlignment: Text.AlignVCenter
                    }
                    MouseArea{
                        id:areaMouse
                        hoverEnabled: true
                        anchors.fill: parent
                    }
                    ToolTip
                    {
                        height: 26
                        visible: areaMouse.containsMouse && textFilename.text !== "" && textMetrics.width > (rectFilename.width-6)
                        contentItem: Text {
                            text: textFilename.text
                            color: "#D6D6D6"
                        }
                        background: Rectangle {
                            color: "#222222"
                        }
                    }
                }
            }
            DelegateChoice{
                column: FileView.TableColumnSize
                delegate: Rectangle{
                    width: viewContent.columnWidthProvider(FileView.TableColumnSize);
                    //implicitHeight: 32

                    Text {
                        text: modelContent.getData(row, FileView.TableColumnSize)
                        anchors.fill: parent
                        font.pointSize: 12
                        //color: "white"
                        horizontalAlignment: Text.AlignLeft
                        verticalAlignment: Text.AlignVCenter
                    }
                }
            }
            DelegateChoice{
                 column: FileView.TableColumnTime
                 delegate: Rectangle{
                     id:rect
                     width: viewContent.columnWidthProvider(FileView.TableColumnTime);
                     //implicitHeight: 32
                     clip: true
                     Text {
                         id: textTime
                         text: modelContent.getData(row, FileView.TableColumnTime)
                         anchors.centerIn: parent
                         font.pointSize: 12
                         width: parent.width
                         elide: Text.ElideRight
                         leftPadding: 3
                         rightPadding: 3
                     }
                 }
             }
        }
    }

    CustomTableModel {
        id: modelContent
    }
    // header view
    HorizontalHeaderView {
        id: headerContent
        visible: true
        // disbale drag on table
        interactive: false
        // sync with tableview
        syncView: viewContent
        //model: ["123", "filename", "size", "time"]
        model:modelContent
        delegate:Rectangle{
            CustomCheckbox {
                id: checkboxHeader
                width: 20
                height: 20
                imgChecked: "qrc:/box_checked@2x.png"
                imgUnchecked: "qrc:/box_uncheck@2x.png"
                anchors{
                    centerIn: parent
                    verticalCenter: parent.verticalCenter
                }
                visible: (0 == index) ? true: false;
                checked: checkedAll
                onMouseClicked: {
                    console.log("clicked at index:" + index + ", check state: " + checkboxHeader.checked)
                    headerSelectAll(checkboxHeader.checked);
                }
            }
            Text {
                // model declared in qml, modelData = header-data
                text: modelContent.headerData(column, Qt.Horizontal)
                //text: modelData
                font.pointSize: 12
                verticalAlignment: Text.AlignVCenter
                anchors {
                    left: parent.left;
                    verticalCenter: parent.verticalCenter
                }
                visible: !checkboxHeader.visible
            }
        }
    }

    Component.onCompleted: {
        modelContent.initModel();
    }

    function headerSelectAll(bSelectAll) {
        checkedAll = bSelectAll;
        selectAll(checkedAll);
    }
}



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值