10

我是 Qt 新手,所以请多多包涵。

我已经成功地从 StringList 和 Object* 的 QList 填充 ListView

我现在正在努力的是使用 C++ 中定义的派生 QAbstractListModel 的类在 QML 中填充 ListView。

这是我的 CPP 类的原型:

class MessageListEntryModel : public QAbstractListModel
{

Q_OBJECT
public:

enum eMLERoleTypes
{
    MLERT_MSG = Qt::UserRole+1,
    MLERT_COLOR
};

                                MessageListEntryModel(QObject* parent=0);
        virtual                 ~MessageListEntryModel();

        void                    AddEntry(QString aMessage, QColor aColor);

        // pure virtuals implementations
        QVariant                data(const QModelIndex &index, int role = Qt::DisplayRole) const;
        int                     rowCount(const QModelIndex &parent = QModelIndex()) const ;
        int                     columnCount(const QModelIndex &parent = QModelIndex()) const ;
        QModelIndex             index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
        QModelIndex             parent(const QModelIndex &child) const ;

        QHash<int,QByteArray>   roleNames();
private:
 QList<MessageEntry*> m_vpMessages;

MessageEntry 是一个简单的类,它包含 2 个成员、一个 QColor 和一个 QString(该类不扩展 QObject)。

我必须实现所有上述功能,因为它们在底层类中是纯虚拟的(这正常吗?到目前为止,在教程/示例中人们只提到了角色名称和数据)。

roleNames 和 data 的实现如下:

QHash<int,QByteArray>  MessageListEntryModel::roleNames()
{
    QHash<int,QByteArray> rez;
    rez[MLERT_MSG]="message";
    rez[MLERT_COLOR]="messagecolor";
    return rez;
}

QVariant MessageListEntryModel::data(const QModelIndex &index, int role) const
{
    qDebug()<<" Data asked for "<<index.row()<<" and role "<<role;
    if (index.row()<0 || index.row()>=m_vpMessages.size())
    {
        return QVariant();
    }
    MessageEntry* entry = m_vpMessages[index.row()];
    if (role == MLERT_MSG)
    {
        return QVariant::fromValue(entry->message);
    } else if (role == MLERT_COLOR)
    {
        return QVariant::fromValue(entry->messageColor);
    }
    // should be unreachable code
    return QVariant();
}

列表视图的 QML 部分是这样的:

 ListView {
    id: quickMessageListdata
    model: quickListModel
    delegate: Rectangle {

        width: 400
        height: 25
        color:"#000000"
        Text{
            text: model.message
            color: model.messagecolor
        }
    }

到目前为止,这是我对如何在 CPP 和 QML 中实现事物的理解。为了链接这两者,我使用以下代码:

MessageListEntryModel* model =new MessageListEntryModel();
// Add various entries
...
// assign model in QML
m_pViewRef->rootContext()->setContextProperty("quickListModel",model);

使用上面的代码,运行时 ListView 中不显示任何内容,并且出现以下错误:

Unable to assign [undefined] to QString

Unable to assign [undefined] to QColor

我还在注册要导出到 QML 的模型类(不知道这是否有必要):

qmlRegisterType<MessageListEntryModel> ("dlti.exported",1,0,"MessageListEntryModel");

所以很明显,要么我误解了正确使用 QAbstractListItem 派生类,要么我错过了一个简单的重要关键信息。

我将不胜感激一些指向一些相关示例/教程的指针(其中还向您展示了如何在 QML 中正确访问模型中的数据,因为我注意到在 CPP 中它永远不会通过数据函数)。

另请注意,我使用的是 qt5 ,因此 qt4.8 样本无法解决问题。

编辑

经过长时间的挫折,我终于解决了这该死的东西出了什么问题:

我的 roleNames 函数签名错误!重载的正确签名是:

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

请注意 protected 和 const 修饰符。

以正确的方式声明函数后,一切正常。

如需进一步通知,实现 data 和 rowCount 就足够了:)。

谢谢您的帮助。我会接受 BaCaRoZzo 的回答,因为我只是在查看示例中的代码后才弄清楚这一点。

附带说明一下,它适用于 message 和 model.message。

4

3 回答 3

9

你如何实现加法?您应该使用我评论中提供的示例中的方法。

文档

insertRows() 实现必须在将新行插入数据结构之前调用 beginInsertRows(),并且必须在之后立即调用 endInsertRows()

你应该有类似的东西:

void MessageListEntryModel::add(params...)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());   // kindly provided by superclass
    // object creation based on params...
    m_vpMessages << objectCreated;
    endInsertRows();                                          // kindly provided by superclass
} 

int MessageListEntryModel::rowCount(const QModelIndex & parent) const {
    Q_UNUSED(parent);
    return m_vpMessages.count();
}

此外,@Jonathan Mee 评论是正确的:仅使用您在模型中定义的委托中的角色名称。如果其他一切都正确,那就是访问数据的方式。


了解 C++ 模型实现和使用的最佳文档之一是模型子类化参考。在本文档中清楚地描述了哪些是最重要的子类方法以及用于什么目的。

至于实现的方法,这真的取决于需要。根据模型上可能的操作,应实施不同的方法(有关完整详细信息,请参阅上面的链接)。可以在其中添加/删除项目的模型ListView可以继承QAbstractListModel并仅依赖于大多数功能的默认实现。您只需要data(),roleNames()rowCount(),正如您在大多数示例中已经看到的那样。

相反,如果您还需要编辑数据而不仅仅是添加/删除它,那么您还需要其他功能,尤其是setData(). 还有责任通过setData()信号通知附加视图以及模型发生的任何修改dataChanged()。再次参考上面提供的子类化参考。

还要注意,如果add方法是用Q_INVOKABLE修饰符修改的,即它被声明为

Q_INVOKABLE void add(params...);

在模型头中,您还可以从 QML 调用它(因为模型被设置为上下文属性),您可以编写,例如:

ListView {
    id: quickMessageListdata
    model: quickListModel
    delegate: Rectangle {

        width: 400
        height: 25
        color:"#000000"
        Text{
            text: model.message
            color: model.messagecolor
        }
    }

    Component.onCompleted: {
        quickListModel.add(params)
        quickListModel.add(params)      
    }
}

创建视图后立即在视图中插入项目。显然,相同的方法可以应用于其他 QML 信号,以便您可以对 QML 事件做出反应并触发添加/删除行为。

最后,您不需要使用qmlRegisterType. 对于您当前的要求,这是多余的。

于 2014-12-11T10:44:10.817 回答
1

嗯...我对 QML 不是很熟悉,但我相信这是你的问题:http: //qt-project.org/doc/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel

根据链接,您似乎需要将Text块更改为:

Text{
    text: message
    color: messagecolor
}

进一步阅读:http: //qt-project.org/forums/viewthread/5491

于 2014-12-10T12:11:06.660 回答
0

在使用 Qt C++ 创建自己的列表模型之前,我也经历过困难时期。为了避免 C++ 模型的开发开销,我开始使用QSyncable(Ben Lau 的现有 QAbstractListModel 实现)。你可以在GitHub 上找到它。

该项目最好的部分是 JsonListModel QML 类型。它可以将您在 QML 中创建或获取的任何变体 JSON 列表转换为功能齐全的 QML ListModel。这为使用 JSON 或 REST 服务的应用程序节省了大量时间和精力。您可以在此处找到其工作原理的详细指南。

于 2019-02-04T13:02:35.370 回答