通过 C++ 单击按钮后更新 QML TableView

Posted

技术标签:

【中文标题】通过 C++ 单击按钮后更新 QML TableView【英文标题】:Update QML TableView after Button click via C++ 【发布时间】:2019-04-21 22:01:33 【问题描述】:

我想通过按一个按钮向 QML 表视图添加一定数量的行。用户界面如下所示:

按下“更新列表模型”后,TableView 中应该会出现一个新行。

我的代码如下所示。我猜addPerson 方法必须发出 dataChanged 事件才能使其正常工作。我怎样才能做到这一点?或者有没有更好的解决方案将 QML 表视图与 C++ 模型同步?

ma​​in.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "MainWindow.h"
int main(int argc, char *argv[]) 
    QGuiApplication app(argc, argv);

    qmlRegisterType<TableModel>("TableModel", 0, 1, "TableModel");

    QQmlApplicationEngine engine;
    MainWindow mainWindow;

    return app.exec();

MainWindow.h

#pragma once

#include <QQmlApplicationEngine>
#include <QtQuick>

#include "TableModel.h"

class MainWindow : public QObject 
    Q_OBJECT;

public:
    explicit MainWindow() 
        engine_.load(QUrl(QStringLiteral("qrc:/main.qml")));

        QObject *rootObject = engine_.rootObjects().first();
        QObject::connect(rootObject, SIGNAL(on_ButtonUpdateListModel_click()), this, SLOT(on_ButtonUpdateListModel_click()));
    

public slots:

    void on_ButtonUpdateListModel_click()          
        QQuickView view;
        QQmlContext *ctxt = view.rootContext();
        model_.addPerson();
        ctxt->setContextProperty("myModel", &model_);
    

private:
    TableModel model_;
    QQmlApplicationEngine engine_;
;

TableModel.h

#pragma once

#include <QAbstractTableModel>
#include <QObject>

class TableModel : public QAbstractTableModel 
    Q_OBJECT;
    enum TableRoles  TableDataRole = Qt::UserRole + 1, HeadingRole ;

public:
    explicit TableModel(QObject *parent = nullptr) : QAbstractTableModel(parent) 
        table.append(
          "First Name",
          "Last Name",
          "Age",
        );
    

    int rowCount(const QModelIndex & = QModelIndex()) const override 
        return table.size();
    

    int columnCount(const QModelIndex & = QModelIndex()) const override 
        return table.at(0).size();
    

    QVariant data(const QModelIndex &index, int role) const override 
        switch (role) 
        case TableDataRole: 
            return table.at(index.row()).at(index.column());
        
        case HeadingRole: 
            if (index.row() == 0) 
                return true;
             else 
                return false;
            
        
        default: break;
        

        return QVariant();
    

    QHash<int, QByteArray> roleNames() const override 
        QHash<int, QByteArray> roles;
        roles[TableDataRole] = "tabledata";
        roles[HeadingRole] = "heading";
        return roles;
    

    void addPerson() 
        table.append(
          "Marc",
          "Fonz",
          "25",
        );
        int idx = table.size() - 1;
        emit dataChanged(index(idx), index(idx));
    

private:
    QVector<QVector<QString>> table;
; 

ma​​in.qml

import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

import Qt.labs.settings 1.0
import Qt.labs.platform 1.0 as Platform

import QtQuick.Controls.Material 2.12
import TableModel 0.1

ApplicationWindow 
    id: window
    visible: true
    width: 1040
    height: 480

    signal on_ButtonUpdateListModel_click()

    ColumnLayout
        spacing: 2
        anchors.fill: parent
        Button 
            text: qsTr("Update List Model")
            onClicked: on_ButtonUpdateListModel_click()
        

        TableModel 
            id: myModel
        
        TableView 
            width: 400
            height: 200
            columnSpacing: 1
            rowSpacing: 1
            clip: true

            ScrollIndicator.horizontal: ScrollIndicator  
            ScrollIndicator.vertical: ScrollIndicator  

            model: myModel


            delegate: Rectangle 
                implicitWidth: 100
                implicitHeight: 20
                border.color: "black"
                border.width: 2
                color: (heading==true) ? 'teal':"green"

                TableView.onPooled: console.log(tabledata + " pooled")
                TableView.onReused: console.log(tabledata + " resused")

                Text 
                    text: tabledata
                    font.pointSize: 10
                    anchors.centerIn: parent
                
            
        

    

【问题讨论】:

显示您的 TableModel。 @eyllanesc 复制了 MainWindow.h 而不是 TableModel.h - 现在已修复 【参考方案1】:

您有以下错误:

在 addPerson 中,您要添加一行,因此不应使用 dataChanged,因为该信号表明已存在的内容已被修改,而应使用 beginInsertRows() 和 endInsertRows()。

在 MainWindow 中创建的 TableModel 与在 QML 中创建的 TableModel 不同。

您无需在 main.cpp 中创建 QQmlApplicationEngine,因为您只需在 MainWindow 中使用。

您必须将业务逻辑与视图分开,因此将对象从 QML 导出到 C++ 被认为是不好的做法。

综合以上情况,解决办法是:

将 addPerson 方法设为 Q_INVOKABLE,以便在 QML 中可访问。

使用 setContextProperty 将启动时的 TableModel 导出到 QML,因此无需注册 TableModel。

ma​​in.cpp

#include "MainWindow.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])

    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    MainWindow mainwindow;
    return app.exec();

TableModel.h

#pragma once

#include <QAbstractTableModel>
#include <QObject>

class TableModel : public QAbstractTableModel 
    Q_OBJECT
    enum TableRoles  TableDataRole = Qt::UserRole + 1, HeadingRole ;
public:
    explicit TableModel(QObject *parent = nullptr) : QAbstractTableModel(parent) 
        table.append(
                         "First Name",
                         "Last Name",
                         "Age",
                     );
    
    int rowCount(const QModelIndex & = QModelIndex()) const override 
        return table.size();
    

    int columnCount(const QModelIndex & = QModelIndex()) const override 
        return table.at(0).size();
    

    QVariant data(const QModelIndex &index, int role) const override 
        switch (role) 
        case TableDataRole: 
            return table.at(index.row()).at(index.column());
        
        case HeadingRole: 
            return index.row() == 0;
        
        default: break;
        
        return QVariant();
    
    QHash<int, QByteArray> roleNames() const override 
        QHash<int, QByteArray> roles;
        roles[TableDataRole] = "tabledata";
        roles[HeadingRole] = "heading";
        return roles;
    

    Q_INVOKABLE void addPerson() 
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        table.append(
                         "Marc",
                         "Fonz",
                         "25",
                     );
        endInsertRows();
    
private:
    QVector<QVector<QString>> table;
;

MainWindow.h

#pragma once

#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "TableModel.h"

class MainWindow : public QObject 
    Q_OBJECT
public:
    explicit MainWindow() 
        engine_.rootContext()->setContextProperty("myModel", &model_);
        engine_.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
private:
    TableModel model_;
    QQmlApplicationEngine engine_;
;

ma​​in.qml

import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

import Qt.labs.settings 1.0
import Qt.labs.platform 1.0 as Platform

import QtQuick.Controls.Material 2.12

ApplicationWindow 
    id: window
    visible: true
    width: 1040
    height: 480
    ColumnLayout
        spacing: 2
        anchors.fill: parent
        Button 
            text: qsTr("Update List Model")
            onClicked: myModel.addPerson()
        
        TableView 
            width: 400
            height: 200
            columnSpacing: 1
            rowSpacing: 1
            clip: true
            ScrollIndicator.horizontal: ScrollIndicator  
            ScrollIndicator.vertical: ScrollIndicator  
            model: myModel
            delegate: Rectangle 
                implicitWidth: 100
                implicitHeight: 20
                border.color: "black"
                border.width: 2
                color: heading ? 'teal':"green"
                TableView.onPooled: console.log(tabledata + " pooled")
                TableView.onReused: console.log(tabledata + " resused")

                Text 
                    text: tabledata
                    font.pointSize: 10
                    anchors.centerIn: parent
                
            
        
    

输出:


完整的解决方案可以在here

【讨论】:

以上是关于通过 C++ 单击按钮后更新 QML TableView的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 更新 QML Window 属性标志

单击按钮后如何通过服务执行小部件更新?

如何检索 QML 相机提要并发送到 C++ 后端

QML 从无到有 3 (自动更新)

如何在 2 个 qml 窗口之间进行通信

TableView在按钮上滚动单击Qml