Qt QML 数据模型似乎不适用于 C++
我一直在使用 http://doc.qt 中的示例.digia.com/4.7/qdeclarativemodels.html 这是关于 QML 声明性数据模型的 Qt 页面.特别是,我正在使用 Qt SDK 附带的 objectlistmodel
示例(在 examples/declarative/modelviews/objectlistmodel 中).在我尝试将它与 http 上的 QMLPageControl 示例结合起来之前,这一切似乎都运行良好://www.developer.nokia.com/Community/Wiki/How_to_create_a_Page_Control_component_in_QML.
I've been working with the examples in http://doc.qt.digia.com/4.7/qdeclarativemodels.html which is the Qt page on QML declarative data models. In particular, I'm working with the objectlistmodel
example that comes with the Qt SDK (in examples/declarative/modelviews/objectlistmodel). It all seems to work reasonably well, until I try to combine it with the QMLPageControl example at http://www.developer.nokia.com/Community/Wiki/How_to_create_a_Page_Control_component_in_QML.
当我尝试使用这样的 QML ListView 显示基于 QML 的 ListModel(填充有 QML ListElements)时:
When I try to display a QML-based ListModel (populated with QML ListElements) with a QML ListView like this:
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 }
}
}
}
...一切都很好.这完全符合预期 - 弹出一个窗口,其中包含带状彩色背景的一些文本.
...everything works quite nicely. This works entirely as expected - a window pops up with some text across colored backgrounds in bands.
然后,我可以做一些更复杂的事情,比如使用 PathView:
Then, I can do something a bit more complicated, like use a 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
}
}
}
}
同样,这一切都按预期工作 - 弹出一个窗口,其中包含可轻弹、可拖动的彩色框列表.
Again, this all works as expected - a window pops up with a flickable, dragable list of colored boxes.
备份,然后我可以像这样在 C++ 中定义一个数据对象:
Backing up, I can then define a data object in C++ like this:
数据对象.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
数据对象.cpp
#include "dataobject.h"
#include <QDebug>
DataObject::DataObject( QObject * parent )
: QObject( parent )
{
qDebug() << "DataObject::DataObject() has been called.
";
}
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.
";
}
QString DataObject::name() const {
qDebug() << "name() has been called.
";
return m_name;
}
void DataObject::setName(const QString &name) {
qDebug() << "setName has been called.
";
if ( name != m_name ) {
m_name = name;
emit nameChanged();
}
}
QString DataObject::colour() const {
qDebug() << "colour() has been called.
";
return m_colour;
}
void DataObject::setColour(const QString &colour) {
qDebug() << "setColour has been called.
";
if ( colour != m_colour ) {
m_colour = colour;
emit colourChanged();
}
}
然后我将它添加到 QML 上下文中:
And then I add it to the QML context:
#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:
And finally, in the QML, I add this C++ model to the 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 }
}
}
}
再一次,这很好用 - 出现一个对话框,其中包含以带状排列的彩色背景的文本.显示由 C++ 模型支持的 ListView 似乎每一点都有效,以及显示由 QML ListModel 支持的 ListView.
Once again, this works just fine - a dialog with text against colored backgrounds arranged in bands appears. Displaying a ListView backed by a C++ model seems to work every bit as well as displaying a ListView backed by a QML ListModel.
我希望得到一个支持 PathView 的 C++ 模型,如下所示:
What I'd like to get working is a C++ model backing a PathView like this:
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 对话框中居中.
This DOESN'T work. What I see is the colored rectangles, but they can't be interacted with with the mouse and they aren't centered in the qmlviewer dialog.
在调试控制台上我看到了这个:
And on the debug console I see this:
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 显示.
It seems like a QList has a basic shape that's close enough to a QML ListModel/ListItem collection for a ListView to display, but not close enough for a PathView to display.
有人知道可能出了什么问题吗?不幸的是,QML 类文档并没有真正与编写符合 C++ 替代的目标放在一起.例如,http://qt-project 的 PathView 对象文档.org/doc/qt-4.8/qml-pathview.html 没有说明它的模型需要支持哪些属性.此外,ListModel 文档不是确定性的――它没有准确说明 ListModel 支持哪些属性,也没有明确的文档说明 QList 如何精确地满足这些要求以及如何不满足.
Does anyone have any idea what might be going wrong? Unfortunately the QML class documentation isn't really put together with the goal of writing conformant C++ stand-ins. For example, the PathView object documentation at http://qt-project.org/doc/qt-4.8/qml-pathview.html doesn't say what properties its model needs to support. Moreover, the ListModel documentation isn't definitive - it doesn't state exactly what properties the ListModel supports and there's no clear documentation on how precisely a QList satisfies those requirements and how it doesn't.
更新:我已经在 Windows 上使用 Qt 5 进行了尝试,但我仍然遇到同样的问题.
UPDATE: I've tried this with Qt 5 on Windows, and I'm still having the same problem.
推荐答案
原来cppModel
的count
属性不存在的原因很简单可用 - 这是因为 QAbstractListModel
和 QList<>
都没有 count
属性!
It turns out that there's a very simple reason that the count
property of the cppModel
isn't available - it's because neither QAbstractListModel
nor QList<>
have a count
property!
我曾假设 ListModel
可以替换为基于 C++ 的对象(如 QList<>),这意味着它们是多态的,并且 ListView 或 PathView 将使用 count
属性以正确处理它们.
I had assumed that the fact that a ListModel
could be substituted with a C++-based object like a QList<> meant that they were polymorphic and that a ListView or PathView would use a count
property to correctly handle them.
首先,QAbstractListModel
和 QList<>
都不具有 ListModel
的多态性.事实证明,它们都只是特殊情况 - ListView
知道它是否具有 ListModel
或 QList<>
或QAbstractListModel
并有单独的代码路径供使用.ListView
不需要不存在的 count
属性来管理 QList<>
或 QAbstractListModel
.事实上,我不清楚 ListView
和 PathView
甚至使用 ListModel
的 count
属性.count
属性似乎主要是为了 QML 程序员的利益.在我的示例中,我使用 count
属性在 PathView
中构建了一个 Path
对象.如果我使用 length
属性代替,我的示例将完美运行,因为 QList<>
确实具有 length
属性.
First, neither QAbstractListModel
nor QList<>
are polymorphic with a ListModel
. It turns out that they're all just special-cased - a ListView
knows whether it has a ListModel
or a QList<>
or a QAbstractListModel
and has separate code paths for using each. The ListView
doesn't need the nonexistent count
property to manage a QList<>
or a QAbstractListModel
. In fact, it isn't clear to me that ListView
and PathView
even use ListModel
's count
property. The count
property seems to be mostly for the QML programmer's benefit. In my example, I was using the count
property to build a Path
object in the PathView
. My example works perfectly if I use a length
property instead because QList<>
DOES have a length
property.
感谢 #qt-qml 上的 blam 和 torgeirl 帮助我解决这个问题(他们都不想通过发布这个答案来收集 stackoverflow 积分,所以我发布它是为了社区的利益).
Thanks to blam and torgeirl on #qt-qml for helping me with this (neither wanted to collect the stackoverflow points by posting this answer, so I'm posting it for the benefit of the community).
相关文章