如何从 C++ 更改 qml 中 TableView 的模型 属性

How does one change the model property of a TableView in qml from c++

我是 QT 的新手,所以非常感谢任何帮助!

我正在开发 Qt Quick 应用程序,为 UI 使用 QQmlApplicationEngine。我制作了 QAbstractTableModel 的子类并实现了必要的功能并成功地创建并在 Window 上显示了一个(单数)table。

目前,我在 QML 文件中 link 编辑模型的方式是设置 root context 属性 属性 QQmlApplicationEngine。

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSharedPointer>
#include <QQmlContext>

#include "tablecontroller.h"

int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);

  QSharedPointer<QQmlApplicationEngine> engine = 
                     QSharedPointer<QQmlApplicationEngine>::create();

  TableController theController(engine.toWeakRef());

  engine.data()->rootContext()->setContextProperty("TableController", &theController);

  engine.data()->load(QUrl(QStringLiteral("qrc:/main.qml")));
  return app.exec();
}

tabcontroller.h

#ifndef TABLECONTROLLER_H
#define TABLECONTROLLER_H

#include <QObject>
#include <QQmlApplicationEngine>
#include <QWeakPointer>
#include <QHash>
#include <QList>

#include "tablemodel.h"

class TableController : public QObject
{
  Q_OBJECT
public:
    explicit TableController(QWeakPointer<QQmlApplicationEngine> Engine, QObject *parent = 0);

    Q_INVOKABLE void AddEntry();

signals:

public slots:

private:
  TE::TDT::TableModel m_TableModel;
  QList<QString> m_Headings;
};

#endif // TABLECONTROLLER_H

tabcontroller.cpp

#include "tablecontroller.h"
#include <QQmlContext>

TableController::TableController(QWeakPointer<QQmlApplicationEngine> Engine, QObject *parent) : QObject(parent)
{
  m_Headings << "Heading1" << "Heading2" << "Heading3" << "Heading4";
  m_TableModel.setColumnHeadings(m_Headings);

  Engine.data()->rootContext()->setContextProperty("myModel", &m_TableModel);
}

void TableController::AddEntry()
{
  QHash<QString, QVariant> tempHash;
  int counter = 1;
  for (auto x : m_Headings)
  {
    tempHash.insert(x, QString::number(counter));
    counter++;
  }
  m_TableModel.addElement(tempHash);
}

tablemodel.h

#ifndef TABLEMODEL_H
#define TABLEMODEL_H

#include <QObject>
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QList>
#include <QString>
#include <QHash>

namespace TE {

namespace TDT {

class TableModel : public QAbstractTableModel
{
  Q_OBJECT
  Q_PROPERTY(QStringList userRoleNames READ userRoleNames CONSTANT)
public:
    explicit TableModel(QObject *parent = 0);

    enum MyModelRoles {
        UserRole1 = Qt::UserRole + 1,
        UserRole2,
    };

  void setFirstColumn(const QList<QString> &FirstColumn);

  void setColumnHeadings(const QList<QString> &ColumnHeadings);

  void addElement(const QHash<QString, QVariant> Entry);

  QStringList userRoleNames();

signals:

public slots:

// QAbstractTableModel interface
public:
  int rowCount(const QModelIndex &parent) const override;
  int columnCount(const QModelIndex &parent) const override;
  QVariant data(const QModelIndex &index, int role) const override;
  QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
  QHash<int, QByteArray> roleNames() const override;

private:
  QList<QHash<QString, QVariant>> m_TableData;
  QList<QString> m_ColumnHeadings;
  QMap<int, QString> m_roleNames;

};

}// TDT

}// TE

#endif // TABLEMODEL_H

tablemodel.cpp

#include "tablemodel.h"
#include <QDebug>
#include <QAbstractListModel>

TE::TDT::TableModel::TableModel(QObject *parent) : QAbstractTableModel(parent)
{

}

int TE::TDT::TableModel::rowCount(const QModelIndex &parent) const
{
  Q_UNUSED(parent);
  return m_TableData.count();
}

int TE::TDT::TableModel::columnCount(const QModelIndex &parent) const
{
  Q_UNUSED(parent);
  return m_ColumnHeadings.count();
}

QVariant TE::TDT::TableModel::data(const QModelIndex &index, int role) const
{
  QVariant retVal;
  try {
     if(!index.isValid())
     {
         throw QString("Invalid index for inherited data function");
     }
     // Check row index
     if(index.row() >= m_TableData.count() || index.row() < 0)
     {
         throw QString("Index (row) out of bounds for data function");
     }
     //Check column index
     if(index.column() >= m_ColumnHeadings.count() || index.column() < 0)
     {
         throw QString("Index (column) out of bounds for data function");
     }

     QList<int> keys = m_roleNames.keys();

     if(role == Qt::DisplayRole || role == Qt::EditRole)
     {
         QString colKey = m_ColumnHeadings.at(index.column());
         if (m_TableData.at(index.row()).value(colKey).isNull())
         {
             retVal = QVariant();
         } else {
             retVal = m_TableData.at(index.row()).value(colKey);
         }
     } else if (m_roleNames.keys().contains(role)) {
         QHash<QString, QVariant> temp1 = m_TableData.at(index.row());
         retVal = m_TableData.at(index.row()).value(m_roleNames.value(role));
     }
     return retVal;

  } catch (QString &e) {
     qDebug() << e;
     return QVariant();
  }
}

QVariant TE::TDT::TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  Q_UNUSED(orientation);
  QVariant retVal;
  if (role == Qt::DisplayRole)
  {
     retVal = m_ColumnHeadings.at(section);
  }
  return retVal;
}

QHash<int, QByteArray> TE::TDT::TableModel::roleNames() const {
  // Populate the roles - basically the column headings
  QHash<int, QByteArray> roles = QAbstractTableModel::roleNames();

  // Should not overwrite existing roles
  int LastIndexOfUserRole = Qt::UserRole;
  for (int x = 1; x <= m_ColumnHeadings.count(); x++)
  {
     roles[LastIndexOfUserRole + x] = m_ColumnHeadings.at(x-1).toUtf8();
  }
  return roles;
}

QStringList TE::TDT::TableModel::userRoleNames() // Return ordered List of user-defined roles
{
  QHashIterator<int, QByteArray> i(roleNames());
  while (i.hasNext())
  {
     i.next();
     if(i.key() > Qt::UserRole)
     {
         m_roleNames[i.key()] = i.value();
     }
  }
  return m_roleNames.values();
}

void TE::TDT::TableModel::setColumnHeadings(const QList<QString> &ColumnHeadings)
{
  m_ColumnHeadings = ColumnHeadings;
}

void TE::TDT::TableModel::addElement(const QHash<QString, QVariant> Entry)
{
   beginInsertRows(QModelIndex(), this->rowCount(QModelIndex()), this->rowCount(QModelIndex()));
   m_TableData.append(Entry);
   endInsertRows();
}

main.qml 导入 QtQuick 2.5 导入 QtQuick.Window 2.2 导入 QtQuick.Controls 1.2

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

    // template component for the column headings
    Component
    {
        id: columnComponent
        TableViewColumn{width: 100 }
    }

    TableView {
        id: tableview
        height: mainwindow.height - 50
        width: mainwindow.width
        y: 5
        x: 0
        visible: true
        resources:
        {
            var roleList = myModel.userRoleNames
            var temp = []
            for(var i = 0; i < roleList.length; i++)
            {
                var role  = roleList[i]
                temp.push(columnComponent.createObject(tableview, { "role": role, "title": role}))
            }
            return temp
        }
        model: myModel
    }

    Rectangle {
        id: abutton
        anchors.top: tableview.bottom
        height: 40
        width: mainwindow.width

        Text {
            text: "Click to Add"
            anchors.fill: parent
        }

        MouseArea {
            anchors.fill: parent
            onClicked: {
                TableController.AddEntry()
            }
        }
    }
}

代码改编自: QML TableView with dynamic number of columns

现在,我的问题是,如果我想重新使用 main.qml 中定义的 TableView我想为它使用另一个 model。问题是(根据我有限的理解)QML link 中的模型 "variable" 是静态的(在启动时定义),在本例中为 "myModel"。

创建此 TableView 的另一个 实例 后如何更改模型?我每次都必须 link 另一个 "variable" 吗?

我试图将 TableView(在 QML 中)转换为 QQuickItem(在 C++ 中)并尝试在那里设置 属性,但无济于事(给出空值,但 QQuickItem 也没有"setModel" 函数)

很抱歉post,想提供尽可能多的信息。

正如@folibis 所说,一种选择是使您的模型可从 QML 实例化,即您可以在 QML 中创建模型的实例。

然后您可以将方法添加到 TableController 到 "register" 这些实例,以防控制器需要知道它们。

或者您仍然在 TableController 中创建模型并使它们可访问,例如通过每个模型一个 属性,一个列表 属性 或 Q_INVOKABLE 方法。

如果我对你的问题的理解正确,你需要将你的自定义 TableView 重新用于另一个模型,你真的不需要更改现有 TableViewmodel

为此,您可以在单独的文件中定义一个新组件并在其他地方使用它(这里有一些文档:http://doc.qt.io/qt-5/qtqml-documents-definetypes.html

在你的情况下它可能看起来像这样:

DynamicTableView.qml :

TableView {
    //it's better not to set positioning properties in a component definition file.
    Component {
        id: columnComponent
        TableViewColumn { width: 100 }
    }  
    resources:
    {
        var roleList = model.userRoleNames // here you expect all your models to be an instance of your TableModel
        var temp = []
        for(var i = 0; i < roleList.length; i++)
        {
            var role  = roleList[i]
            temp.push(columnComponent.createObject(tableview, { "role": role, "title": role}))
        }
        return temp
    }
}

然后您可以在 main.qml 中重用您的组件并定义其 model 属性 :

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

    DynamicTableView {
        id: tableview
        height: mainwindow.height - 50
        width: mainwindow.width
        y: 5
        x: 0
        model: myModel
    }    

    Rectangle {
        id: abutton
        anchors.top: tableview.bottom
        height: 40
        width: mainwindow.width    

        Text {
            text: "Click to Add"
            anchors.fill: parent
        }    

        MouseArea {
            anchors.fill: parent
            onClicked: {
                TableController.AddEntry()
            }
        }
    }
}

这个人救了我的命 :D 所以看来我需要使用 component.beginCreate() 函数。