objectlistmodel c++ qt model-view-controller qml datamodel

objectlistmodel - El modelo de datos Qt QML no parece funcionar con C++



objectlistmodel qml (1)

Resulta que hay una razón muy simple por la que la propiedad count del cppModel no está disponible, ¡es porque ni QAbstractListModel ni QList<> tienen una propiedad count !

Había asumido que el hecho de que un ListModel podría ser sustituido con un objeto basado en C ++ como un QList <> significaba que eran polimórficos y que un ListView o PathView usaría una propiedad de count para manejarlos correctamente.

Primero, ni QAbstractListModel ni QList<> son polimórficos con un ListModel . Resulta que todos tienen una QList<> especial: un ListView sabe si tiene un ListModel o un QList<> o un QAbstractListModel y tiene rutas de código separadas para usar cada uno. ListView no necesita la propiedad de count inexistente para administrar un QList<> o un QAbstractListModel . De hecho, no está claro para mí que ListView y PathView incluso utilicen la propiedad count PathView . La propiedad de count parece ser principalmente para el beneficio del programador QML. En mi ejemplo, estaba usando la propiedad count para construir un objeto Path en PathView . Mi ejemplo funciona perfectamente si uso una propiedad de length porque QList<> DOES tiene una propiedad de length .

Gracias a blam y torgeirl en # qt-qml por ayudarme con esto (ni quería recopilar los puntos de stackoverflow publicando esta respuesta, así que lo estoy publicando para el beneficio de la comunidad).

He estado trabajando con los ejemplos en http://doc.qt.digia.com/4.7/qdeclarativemodels.html que es la página de Qt en modelos de datos declarativos QML. En particular, estoy trabajando con el ejemplo objectlistmodel que viene con el Qt SDK (en examples / declarative / modelviews / objectlistmodel). Todo parece funcionar razonablemente bien, hasta que intento combinarlo con el ejemplo de QMLPageControl en http://www.developer.nokia.com/Community/Wiki/How_to_create_a_Page_Control_component_in_QML .

Cuando intento mostrar un ListModel basado en QML (rellenado con elementos de lista QML) con un ListView de QML como este:

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 } } } }

... todo funciona bastante bien. Esto funciona del todo como se esperaba: aparece una ventana con texto sobre fondos de colores en bandas.

Entonces, puedo hacer algo un poco más complicado, como usar un 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 } } } }

Una vez más, todo esto funciona como se esperaba: aparece una ventana con una lista deslizable y dragable de cuadros de colores.

Copia de seguridad, puedo definir un objeto de datos en C ++ de esta manera:

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(); } }

Y luego lo agrego al contexto 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(); }

Y finalmente, en QML, agrego este modelo de C ++ a 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 } } } }

Una vez más, esto funciona bien, aparece un diálogo con texto sobre fondos de colores dispuestos en bandas. Mostrar un ListView respaldado por un modelo de C ++ parece funcionar bien y muestra un ListView respaldado por un QML ListModel.

Lo que me gustaría que funcione es un modelo C ++ que respalda un PathView como este:

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 } } } }

Esto NO funciona Lo que veo son los rectángulos coloreados, pero no se pueden interactuar con el mouse y no están centrados en el cuadro de diálogo qmlviewer.

Y en la consola de depuración veo esto:

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

Parece que una QList tiene una forma básica que es lo suficientemente cercana a una colección QML ListModel / ListItem para que se muestre ListView, pero no lo suficientemente cerca como para que se muestre PathView.

¿Alguien tiene alguna idea de lo que podría estar yendo mal? Lamentablemente, la documentación de la clase QML no se ha elaborado realmente con el objetivo de escribir complementos compatibles de C ++. Por ejemplo, la documentación del objeto PathView en http://qt-project.org/doc/qt-4.8/qml-pathview.html no dice qué propiedades debe admitir su modelo. Además, la documentación de ListModel no es definitiva: no indica exactamente qué propiedades admite ListModel y no hay documentación clara sobre la precisión con que una QList satisface esos requisitos y cómo no.

ACTUALIZACIÓN: He intentado esto con Qt 5 en Windows, y todavía tengo el mismo problema.