Модель данных Qt QML, похоже, не работает с C ++

Я работал с примерами на http://doc.qt.digia.com/4.7/qdeclarativemodels.html, которая является страницей Qt, посвященной декларативным моделям данных QML. В частности, я работаю с примером objectlistmodel, который поставляется с Qt SDK (в examples / declarative / modelviews / objectlistmodel). Кажется, все работает достаточно хорошо, пока я не попытаюсь объединить это с примером QMLPageControl на http://www.developer.nokia.com/Community/Wiki/How_to_create_a_Page_Control_component_in_QML.

Когда я пытаюсь отобразить ListModel на основе QML (заполненный QML ListElements) с помощью QML ListView следующим образом:

import QtQuick 1.0

Rectangle {
   width: 200; height: 200

   ListModel {
       id: qmlModel
       ListElement { name: "qml entry1 (red)"; colour: "red" }
       ListElement { name: "qml entry2 (orange)"; colour: "orange" }
       ListElement { name: "qml entry3 (yellow)"; colour: "yellow" }
       ListElement { name: "qml entry4 (green)"; colour: "green" }
       ListElement { name: "qml entry5 (blue)"; colour: "blue" }
       ListElement { name: "qml entry6 (purple)"; colour: "purple" }
   }


   ListView {

       id: list_view

       anchors.fill: parent
       model: qmlModel
       delegate: Rectangle {
           height: 20
           width: 200
           color: colour
           Text { text: name }

       }
    }
}

... все работает неплохо. Это работает полностью так, как ожидалось - всплывает окно с текстом на цветном фоне в полосах.

ListView, отображающий модель списка QML

Затем я могу сделать что-то более сложное, например, использовать PathView:

import QtQuick 1.0

Rectangle {
    width: 200; height: 200

    ListModel {
        id: qmlModel
        ListElement { name: "qml entry1 (red)"; colour: "red" }
        ListElement { name: "qml entry2 (orange)"; colour: "orange" }
        ListElement { name: "qml entry3 (yellow)"; colour: "yellow" }
        ListElement { name: "qml entry4 (green)"; colour: "green" }
        ListElement { name: "qml entry5 (blue)"; colour: "blue" }
        ListElement { name: "qml entry6 (purple)"; colour: "purple" }
    }


    //       ListView {
    //           id: list_view
    //           anchors.fill: parent
    //           model: qmlModel
    //           delegate: Rectangle {
    //               height: 20
    //               width: 200
    //               color: colour
    //               Text { text: name }
    //           }
    //       }

    PathView {
        id: my_path_view

        anchors.fill: parent

        Keys.onRightPressed: if (!moving && interactive) incrementCurrentIndex()
        Keys.onLeftPressed: if (!moving && interactive) decrementCurrentIndex()

        flickDeceleration: 500

        preferredHighlightBegin: 0.5
        preferredHighlightEnd: 0.5
        focus: true
        interactive: true
        model: qmlModel

        delegate: Rectangle {
            width: 100
            height: 100
            color: colour
            Text {
                anchors.centerIn: parent
                text: name
            }
        }


        path: Path {
            startX: - my_path_view.width * my_path_view.model.count / 2 + my_path_view.width / 2
            startY: my_path_view.height / 2
            PathLine {
                x: my_path_view.width * my_path_view.model.count / 2 + my_path_view.width / 2
                y: my_path_view.height / 2
            }
        }
    }
}

Опять же, все работает так, как ожидалось - всплывает окно с перетаскиваемым списком цветных полей.

PathView, отображающий модель списка QML

PathView, отображающий модель списка QML в движении

После резервного копирования я могу определить объект данных на C ++ следующим образом:

dataobject.h

#ifndef DATAOBJECT_H
#define DATAOBJECT_H

#include <QObject>

class DataObject : public QObject
{
    Q_OBJECT

    Q_PROPERTY( QString name READ name WRITE setName NOTIFY nameChanged )
    Q_PROPERTY( QString colour READ colour WRITE setColour NOTIFY colourChanged )


public:
    DataObject( QObject * parent = 0 );
    DataObject( const QString &_name, const QString &_color, QObject * parent=0 );

    QString name() const;
    void setName(const QString &);

    QString colour() const;
    void setColour(const QString &);

signals:
    void nameChanged();
    void colourChanged();


private:
    QString m_name;
    QString m_colour;
};


#endif // DATAOBJECT_H

dataobject.cpp

#include "dataobject.h"
#include <QDebug>

DataObject::DataObject( QObject * parent )
    : QObject( parent )
{
    qDebug() << "DataObject::DataObject() has been called.\n";

}

DataObject::DataObject( const QString &_name, const QString &_colour, QObject * parent )
    : QObject( parent )
    , m_name( _name )
    , m_colour( _colour )
{
    qDebug() << "DataObject::DataObject(name, color) has been called.\n";

}


QString DataObject::name() const {
    qDebug() << "name() has been called.\n";
    return m_name;
}

void DataObject::setName(const QString &name) {
    qDebug() << "setName has been called.\n";
    if ( name != m_name ) {
        m_name = name;
        emit nameChanged();
    }
}

QString DataObject::colour() const {
    qDebug() << "colour() has been called.\n";
    return m_colour;
}

void DataObject::setColour(const QString &colour) {
    qDebug() << "setColour has been called.\n";
    if ( colour != m_colour ) {
        m_colour = colour;
        emit colourChanged();
    }
}

А потом добавляю в контекст QML:

#include <QApplication>
#include <QDialog>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QLayout>
#include <QDir>
#include "qmlapplicationviewer.h"
#include "dataobject.h"

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

    QList<QObject*> dataList;
    dataList.append( new DataObject( "c++ entry1 (red)", "red" ) );
    dataList.append( new DataObject( "c++ entry2 (orange)", "orange" ) );
    dataList.append( new DataObject( "c++ entry3 (yellow)", "yellow" ) );
    dataList.append( new DataObject( "c++ entry4 (green)", "green" ) );
    dataList.append( new DataObject( "c++ entry5 (blue)", "blue" ) );
    dataList.append( new DataObject( "c++ entry6 (purple)", "purple" ) );

    QmlApplicationViewer viewer;
    viewer.rootContext()->setContextProperty( "cppModel", QVariant::fromValue(dataList) );
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
#if defined( Q_OS_MAC )
    viewer.setMainQmlFile("../Resources/qml/main.qml");
#elif defined( Q_OS_WIN32 )
    viewer.setMainQmlFile("qml/main.qml");
#else
#error - unknown platform
#endif
    viewer.showExpanded();

    return app.exec();
}

И, наконец, в QML я добавляю эту модель C ++ в ListView:

import QtQuick 1.0

Rectangle {
    width: 200; height: 200

    ListModel {
        id: qmlModel
        ListElement { name: "qml entry1 (red)"; colour: "red" }
        ListElement { name: "qml entry2 (orange)"; colour: "orange" }
        ListElement { name: "qml entry3 (yellow)"; colour: "yellow" }
        ListElement { name: "qml entry4 (green)"; colour: "green" }
        ListElement { name: "qml entry5 (blue)"; colour: "blue" }
        ListElement { name: "qml entry6 (purple)"; colour: "purple" }
    }


           ListView {

               id: list_view

               anchors.fill: parent
               //model: qmlModel
               model: cppModel
               delegate: Rectangle {
                   height: 20
                   width: 200
                   color: colour
                   Text { text: name }

               }
           }

}

Опять же, это прекрасно работает - появляется диалоговое окно с текстом на цветном фоне, упорядоченным по полосам. Отображение ListView, поддерживаемого моделью C ++, кажется, работает каждый бит, а также отображение ListView, поддерживаемого QML ListModel.

ListView, отображающий QList на C ++

Я бы хотел работать с моделью C ++, поддерживающей PathView следующим образом:

import QtQuick 1.0

Rectangle {
    width: 200; height: 200

    ListModel {
        id: qmlModel
        ListElement { name: "qml entry1 (red)"; colour: "red" }
        ListElement { name: "qml entry2 (orange)"; colour: "orange" }
        ListElement { name: "qml entry3 (yellow)"; colour: "yellow" }
        ListElement { name: "qml entry4 (green)"; colour: "green" }
        ListElement { name: "qml entry5 (blue)"; colour: "blue" }
        ListElement { name: "qml entry6 (purple)"; colour: "purple" }
    }


//    ListView {

//       id: list_view

//       anchors.fill: parent
//       model: qmlModel
//       //model: cppModel
//       delegate: Rectangle {
//           height: 20
//           width: 200
//           color: colour
//           Text { text: name }

//       }
//    }

    PathView {
        id: my_path_view

        anchors.fill: parent

        Keys.onRightPressed: if (!moving && interactive) incrementCurrentIndex()
        Keys.onLeftPressed: if (!moving && interactive) decrementCurrentIndex()

        flickDeceleration: 500

        preferredHighlightBegin: 0.5
        preferredHighlightEnd: 0.5
        focus: true
        interactive: true
        //model: qmlModel
        model: cppModel

        delegate: Rectangle {
            width: 100
            height: 100
            color: colour
            Text {
                anchors.centerIn: parent
                text: name
            }
        }


        path: Path {
            startX: - my_path_view.width * my_path_view.model.count / 2 + my_path_view.width / 2
            startY: my_path_view.height / 2
            PathLine {
                x: my_path_view.width * my_path_view.model.count / 2 + my_path_view.width / 2
                y: my_path_view.height / 2
            }
        }
    }
}

Это НЕ РАБОТАЕТ. Я вижу цветные прямоугольники, но с ними нельзя взаимодействовать с помощью мыши, и они не центрированы в диалоговом окне qmlviewer.

C ++ QList в QML PathView

И на консоли отладки я вижу это:

QDeclarativeDebugServer: Waiting for connection on port 3768...
QDeclarativeDebugServer: Connection established
QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call
QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call
colour() has been called.

name() has been called.

colour() has been called.

name() has been called.

colour() has been called.

name() has been called.

colour() has been called.

name() has been called.

colour() has been called.

name() has been called.

colour() has been called.

name() has been called.

QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call
QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call
QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call
QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call
QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call

Кажется, что QList имеет базовую форму, которая достаточно близка к коллекции QML ListModel / ListItem для отображения ListView, но недостаточно близка для отображения PathView.

Кто-нибудь знает, что может пойти не так? К сожалению, документация по классам QML на самом деле не предназначена для написания совместимых замен на C ++. Например, документация по объекту PathView по адресу http://qt-project.org/doc/qt-4.8/qml-pathview.html не сообщает, какие свойства должна поддерживать его модель. Более того, документация ListModel не является окончательной - в ней не указано, какие именно свойства поддерживает ListModel, и нет четкой документации о том, насколько точно QList удовлетворяет этим требованиям, а как - нет.

ОБНОВЛЕНИЕ: я пробовал это с Qt 5 в Windows, и у меня все еще та же проблема.


person Ted Middleton    schedule 01.04.2013    source источник


Ответы (1)


Оказывается, есть очень простая причина, по которой свойство count cppModel недоступно - это потому, что ни QAbstractListModel, ни QList<> не имеют свойства count!

Я предполагал, что тот факт, что ListModel может быть заменен объектом на основе C ++, таким как QList ‹>, означает, что они полиморфны и что ListView или PathView будут использовать свойство count для их правильной обработки.

Во-первых, ни QAbstractListModel, ни QList<> не полиморфны с ListModel. Оказывается, все они имеют особый корпус - ListView знает, есть ли у него ListModel, QList<> или QAbstractListModel, и имеет отдельные пути кода для каждого из них. ListView не требуется несуществующее свойство count для управления QList<> или QAbstractListModel. На самом деле, мне непонятно, используют ли ListView и PathView даже свойство ListModel count. Свойство count, по-видимому, предназначено главным образом для пользы программиста QML. В моем примере я использовал свойство count для создания объекта Path в PathView. Мой пример отлично работает, если я использую вместо этого свойство length, потому что QList<> ДЕЙСТВИТЕЛЬНО имеет свойство length.

Спасибо blam и torgeirl на # qt-qml за помощь мне в этом (никто не хотел собирать точки stackoverflow, публикуя этот ответ, поэтому я публикую его на благо сообщества).

person Ted Middleton    schedule 11.04.2013