将 QAbstractTableModel 与 Qml TableView 一起使用仅显示第一列

2022-01-02 00:00:00 model qt qml c++

我正在尝试将(派生自)QAbstractTableModel 与 Qml TableView 一起使用;

I'm trying to use a (class derived from) QAbstractTableModel with a Qml TableView;

但是,只显示第一列.

原因是不为非零列调用 QVariant MyModel::data(const QModelIndex &index, int role),但我不明白为什么.

The reason is QVariant MyModel::data(const QModelIndex &index, int role) isn't called for non-zero columns, but I don't understand why.

一个 QTableView 工作正常.

A QTableView works fine however.

我制作了一个单独的简单项目来重现我的问题:

I've made a separate, simple project that reproduce my issue:

MyModel.h :

MyModel.h :

#ifndef MYMODEL_H
#define MYMODEL_H

#include <QObject>
#include <QAbstractTableModel>
#include <QList>
#include <QString>
#include <QDebug>

struct SimpleData
{
    QString m_one;
    qint32 m_two;
    qreal m_three;
};

class MyModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit MyModel();//MyData *the_data);
    int rowCount(const QModelIndex & parent = QModelIndex()) const Q_DECL_OVERRIDE;
    int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

    QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
signals:

public slots:
    void theDataChanged();

private:
   QList<SimpleData> m_the_data;

};

#endif // MYMODEL_H

我的模型.cpp:#include "mymodel.h"

mymodel.cpp: #include "mymodel.h"

MyModel::MyModel() : QAbstractTableModel(0)
{
    m_the_data << SimpleData{"Alpha", 10, 100.0}
               << SimpleData{"Beta", 20, 200.0}
               << SimpleData{"Gamma", 30, 300.0}
               << SimpleData{"Delta", 40, 400.0};
}

int MyModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return m_the_data.size();
}

int MyModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return 3;
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    // Check DisplayRole
    if(role != Qt::DisplayRole)
    {
        return QVariant();
    }

    // Check boudaries
    if(index.column() < 0 ||
            columnCount() <= index.column() ||
            index.row() < 0 ||
            rowCount() <= index.row())
    {
        qDebug() << "Warning: " << index.row() << ", " << index.column();
        return QVariant();
    }

    // Nominal case
     qDebug() << "MyModel::data: " << index.column() << "; " << index.row();
    switch(index.column())
    {
        case 0:
            return m_the_data[index.row()].m_one;
        case 1:
            return  m_the_data[index.row()].m_two;
        case 2:
            return  m_the_data[index.row()].m_three;
        default:
            qDebug() << "Not supposed to happen";
            return QVariant();
    }
}

QHash<int, QByteArray> MyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[0] = "one";
    roles[1] = "two";
    roles[2] = "three";
    return roles;

}

void MyModel::theDataChanged()
{
    //TODO
}

main.qml:

import QtQuick 2.1
import QtQuick.Controls 1.0

ApplicationWindow {
    title: qsTr("Hello World")
    width: 640
    height: 480

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
    }
TableView {

    anchors.fill: parent

    TableViewColumn {title: "1"; role: "one"; width: 70 }
    TableViewColumn {title: "2"; role: "two"; width: 70   }
    TableViewColumn {title: "3"; role: "three"; width: 70 }

    model: theModel

}

}

main.cpp:

#include <QtQml>
#include <QQmlApplicationEngine>
#include <QApplication>
#include <QQuickWindow>
#include <QTableView>

#include "mymodel.h"

int main(int argc, char *argv[])
{
    // Application :
    QApplication app(argc, argv);

    // Data stuff :
    //MyData data(&app);
    MyModel model;



 // UI :
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("theModel", &model);

    engine.load(QUrl("qrc:/qml/main.qml"));
    QList<QObject*> temp = engine.rootObjects();
    QObject *topLevel = temp.value(0);
    QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
    if ( !window ) {
        qWarning("Error: Your root item has to be a Window.");
        return -1;


 }

    // Display the main.qml, which show the model:
    window->show();

    // Same, using a QTableView:
    QTableView view;;
    view.setModel(&model);
    view.show();

    return app.exec();
}

关于 TableView 的 qDebug 输出(行,然后列):

qDebug output about the TableView (row, then column):

MyModel::data:  0 ;  0 
MyModel::data:  0 ;  0 
MyModel::data:  0 ;  1 
MyModel::data:  0 ;  1 
MyModel::data:  0 ;  2 
MyModel::data:  0 ;  2 
MyModel::data:  0 ;  3 
MyModel::data:  0 ;  3 

关于 QTableVierw 的 qDebug 输出:

qDebug output about the QTableVierw:

MyModel::data:  0 ;  0 
MyModel::data:  0 ;  0 
MyModel::data:  0 ;  0 
MyModel::data:  1 ;  0 
MyModel::data:  2 ;  0 
MyModel::data:  0 ;  1 
MyModel::data:  1 ;  1 
MyModel::data:  2 ;  1 
MyModel::data:  0 ;  2 
MyModel::data:  1 ;  2 
MyModel::data:  2 ;  2 
MyModel::data:  0 ;  3 
MyModel::data:  1 ;  3 
MyModel::data:  2 ;  3 

我尝试过的笔记/东西:

Notes / stuff I tried:

  • 在我给出的代码中,我可以使用 QQmlListProperty;但是我的实际代码更复杂

  • In the code I gave, I could use a QQmlListProperty; however my actual code is more complex

  • 实际查询的数据,
  • 我没有 SimpleData 类
  • 我基本上使用 QAbstractTableModel 作为代理.因此,我无法切换到 QQmlListProperty(或等效的)

有一个项目来检查模型,ModelTest,但是它不适用于 Qt 5.2.

there was a project to check models, ModelTest, however it does not work with Qt 5.2.

数据类型(QString、qreal)不是问题:交换仍然只显示第一列.

the type of data (QString, qreal) isn't an issue: swapping still display only the 1st column.

相关链接:

http://qt-project.org/doc/qt-5/QAbstractTableModel.html

http://qt-project.org/doc/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel

提前致谢!

规格:Windows 7、Qt 5.2、Mingw 4.8、Qt Creator 3.0

Specs: Windows 7, Qt 5.2, Mingw 4.8, Qt Creator 3.0

推荐答案

TableViewColumn API 建议通过角色而不是列检索列中的数据,即一"、二"和三",而传递的列将始终为 0.对于除 Qt::DisplayRole 之外的所有内容,您都返回 QVariant().Qt::DisplayRole 为 0,转换为 int.在角色名称中,您将 0 的名称设置为一",这就是您碰巧看到一"(DisplayRole)的原因.

The TableViewColumn API suggests that the data from the column is retrieved via roles instead of columns, i.e. "one", "two", and "three", while the column passed will be always 0. You return QVariant() for everything but Qt::DisplayRole. Qt::DisplayRole is 0, converted to int. In rolenames, you set the name for 0 to "one", so that's why you happen to see something for "one" (DisplayRole).

所以要解决这个问题,你必须为一、二和三返回一些东西.我建议在标题中定义一个自定义角色枚举:

So to fix this, you must return something for one, two, and three. I'd suggest to define a custom roles enum in the header:

//In class MyModel:
enum Role {
    OneRole=Qt::UserRole,
    TwoRole,
    ThreeRole
};

像这样定义角色名称:

QHash<int, QByteArray> MyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[OneRole] = "one";
    roles[TwoRole] = "two";
    roles[ThreeRole] = "three";
    return roles;
}

请注意,我从 Qt::UserRole 而不是 0 开始.这避免了与预定义角色(例如 Qt::DisplayRole)的冲突.

Note that I started with Qt::UserRole instead of 0. That avoids collision with predefined roles such as Qt::DisplayRole.

然后在 data() 中返回一、二和三的内容:

Then return something for One, Two and Three, in data():

...
switch(role)
{
    case OneRole:
        return m_the_data[index.row()].m_one;
    case TwoRole:
        return m_the_data[index.row()].m_two;
    case ThreeRole:
        return m_the_data[index.row()].m_three;
}
...

现在您应该可以看到数据了.

Now you should see the data.

似乎来自 QtQuickControls 的 TableView/TableViewColumn 做了一个有点不幸和令人困惑的角色和列的混合:虽然命名让我们想到模型列(但它们实际上在这里指的是视图的列),但只能检索数据通过不同的角色,将列固定为 0.(对我来说,TableViewColumn 中应该有另一个可选属性列".)在 C++ QAbstractItemModel/QTableView 方式之间有点冲突,其中多列是很自然的事情,而 QtQuick 视图都只使用角色来引用数据,通常根本不支持多列(ListView,网格视图).

It seems that the TableView/TableViewColumn from QtQuickControls does a somewhat unfortunate and confusing mix of role and columns: While the naming let's one think of model columns (but they actually refer to the view's columns here), one can retrieve data only via different roles, with the column being fixed to 0. (To me there should be another optional property "column" in TableViewColumn.) It's a bit of a clash between the C++ QAbstractItemModel/QTableView way of things, where multiple columns are a natural thing to do, and QtQuick views, which all use only roles to refer to data, often not supporting multiple columns at all (ListView, GridView).

相关文章