在ListView中使用QAbstractListModel

时间:2014-12-10 11:35:33

标签: qt listview qt5 qtquick2 qabstractlistmodel

我是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和数据的实现如下:

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修饰符。

在以正确的方式声明功能后,一切正常。

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

感谢您的帮助。 我将接受BaCaRoZzo的答案,因为我只是在查看示例中的代码后才想到这一点。

作为旁注,它适用于message和model.message。

3 个答案:

答案 0 :(得分:8)

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

来自docs

  

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 ++模型实现和使用的最佳文档之一是Model subclassing reference。在本文档中清楚地描述了子类化和用于何种目的的最重要方法。

至于实施方法,实际上取决于需求。根据模型上可能的操作,应实施不同的方法(有关完整详细信息,请参阅上面的链接)。可以添加/删除项目的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注册模型。对于您目前的要求,它是多余的。

答案 1 :(得分: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

答案 2 :(得分:0)

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

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