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.