【问题标题】:How does one change the model property of a TableView in qml from c++如何从 c++ 更改 qml 中 TableView 的模型属性
【发布时间】:2017-03-14 17:49:15
【问题描述】:

我对 QT 比较陌生,因此非常感谢任何帮助!

我正在开发一个 Qt 快速应用程序,将 QQmlApplicationEngine 用于 UI。我创建了一个 QAbstractTableModel 的子类并实现了必要的功能,并成功地在窗口上创建并显示了一个(单一)表。

目前,我如何在QML文件中链接模型是通过设置QQmlApplicationEngine的root属性context属性

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 中的模型链接到的“变量”是静态的(在启动时定义),在本例中为“myModel”。

创建此 TableView 的另一个实例后,如何更改模型?我每次都必须链接另一个“变量”吗?

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

抱歉,帖子太长了,希望能提供尽可能多的信息。

【问题讨论】:

  • 您可以使用 qmlRegisterType 将您的模型公开给 QML,从而创建多个模型项

标签: qt qml tableview qqmlapplicationengine


【解决方案1】:

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

然后您可以向TableController 添加一个方法来“注册”这些实例,以防控制器需要了解它们。

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

【讨论】:

    【解决方案2】:

    如果我正确理解您的问题,您需要将您的自定义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()
                }
            }
        }
    }
    

    【讨论】:

    • 感谢您的回答,但我仍然不明白这如何解决我的问题。我想创建 TableView 的多个实例,它们都有自己的 unique 模型,由 c++ 代码中定义的 QAbstractTableModel 子类定义。如何为每个实例设置模型?假设我想在一个“选项卡”中创建 2 个(或任意数量的)表,这意味着实例化 TableView 两次(或更多),那么我将如何设置模型?
    • 喜欢吗? DynamicTableView { id: firstTableview; model: myModel1; ... } DynamicTableView { id: secondTableview; model: myModel2; ... }
    • 但这需要我对模型进行硬编码......正如问题的标题所说,它是如何形成 c++ 的?我正在编写的应用程序允许用户插入一个表(或多个表),用户将能够选择他希望在表中包含哪些参数(因此是模型的动态设置)。抱歉,如果我没有在问题中正确解释这一点。编辑:我也在尝试维护 MVC 架构,我不想从 c++ 做太多的 QML 修改,在这种情况下,我想最小化“插入”代码到 QML 文件中,我只想“设置” " 模型。
    • 最简单的方法是在 QML 中实例化模型,就像 @folibis 和我本人所建议的那样。或者,也如前所述,您有一个模型列表并为每个列表条目创建一个视图
    • @GrecKo 代替resources 的脚本代码,您也可以使用Instantiator。理论上甚至应该允许在运行时动态更改列
    【解决方案3】:

    https://stackoverflow.com/a/35755172/7094339

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 2021-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多