I bind a custom FBO, but what I draw goes beyond it - qt

I've made a simple testcase. What I do, in short:
I have a QQuickFramebufferObject subclass called MyItem. It uses the CombinedDepthStencil Attachment type.
I create an instance of MyItem on window hover and destroy it on window unhover
In MyItemRenderer::render() I create a local FBO and draw a triangle to it
This local FBO uses a custom FBO wrapper class instead of QOpenGLFramebufferObject, to narrow down the problem. The problem occurs with QOpenGLFramebufferObject too, though.
The expected behavior is that nothing should ever be drawn to the window. But instead, on the second time you hover the window (after launching the app), the triangle is drawn to the window.
My code:
main.cpp:
#include <QGuiApplication>
#include <QQuickView>
#include <QQuickWindow>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include "fbo.h"
class MyItem : public QQuickFramebufferObject {
Q_OBJECT
public:
Renderer* createRenderer() const;
};
class MyItemRenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions {
public:
MyItemRenderer() {
initializeOpenGLFunctions();
m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader.vert.glsl");
m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader.frag.glsl");
m_program.link();
}
void render() {
glClearColor(0, 0, 0, 0);
glDisable(GL_DEPTH_TEST);
Fbo fbo(m_size);
m_program.bind();
fbo.bind();
glClear(GL_COLOR_BUFFER_BIT);
paintGeometry();
framebufferObject()->bind();
m_window->resetOpenGLState();
}
void paintGeometry() {
QVector<QVector2D> vertices = { QVector2D(0, 0), QVector2D(1, 0), QVector2D(1, 1) };
m_program.enableAttributeArray("aPos");
m_program.setAttributeArray("aPos", vertices.constData());
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
m_program.disableAttributeArray("aPos");
}
QOpenGLFramebufferObject* createFramebufferObject(const QSize &size) {
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
return new QOpenGLFramebufferObject(size, format);
}
private:
QOpenGLShaderProgram m_program;
QQuickWindow* m_window;
QSize m_size;
protected:
void synchronize(QQuickFramebufferObject* qqfbo) {
MyItem* parentItem = (MyItem*)qqfbo;
m_window = parentItem->window();
m_size = QSize(parentItem->width(), parentItem->height());
}
};
QQuickFramebufferObject::Renderer* MyItem::createRenderer() const {
return new MyItemRenderer();
}
int main(int argc, char **argv) {
QGuiApplication app(argc, argv);
qmlRegisterType<MyItem>("MyItem", 1, 0, "MyItem");
QQuickView view;
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.0
import MyItem 1.0
Item {
width: 400
height: 400
id: root
Loader {
active: mouseArea.containsMouse
sourceComponent: MyItem {
parent: root
anchors.fill: parent
}
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
}
}
shader.frag.glsl:
void main() {
gl_FragColor = vec4(0.40, 1.0, 0.0, 1.0);
}
shader.vert.glsl:
attribute highp vec2 aPos;
void main() {
gl_Position = vec4(aPos, 0.0, 1.0);
}
fbo.h:
#ifndef FBO_H
#define FBO_H
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
class Fbo : public QOpenGLFunctions {
public:
Fbo(const QSize &size)
: m_colorAttachment(QOpenGLTexture::Target2D)
{
initializeOpenGLFunctions();
glGenFramebuffers(1, &m_id);
m_colorAttachment.setSize(size.width(), size.height());
m_colorAttachment.setFormat(QOpenGLTexture::RGBA8_UNorm);
m_colorAttachment.allocateStorage();
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorAttachment.textureId(), 0);
}
~Fbo() {
glDeleteFramebuffers(1, &m_id);
}
void bind() {
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
}
private:
GLuint m_id;
QOpenGLTexture m_colorAttachment;
};
#endif
Any ideas?
Note that I've ran the testcase in the CodeXL OpenGL debugger and it has found no OpenGL errors. Same with Qt's QOpenGLDebugLogger mechanism.

Related

How can a QThread send a signal from its own thread with an enum as an argument for consumption in QML?

In the following code, if the signal errorHappened is emitted from the main thread it works without problem. However if it is emitted from the QThread thread it fails with the following error:
QObject::connect: Cannot queue arguments of type 'ErrorCode'
(Make sure 'ErrorCode' is registered using qRegisterMetaType().)
Is there a way that the signal can be successfully emitted from the QThread thread? If so, how?
Full code in this Gist
MyClass.h
#import <QThread>
#import <atomic>
class MyClass : public QThread
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = Q_NULLPTR);
virtual ~MyClass() override;
enum ErrorCode {
ErrorA,
ErrorB,
ErrorC
};
Q_ENUM(ErrorCode)
signals:
void errorHappened(ErrorCode errorCode);
public slots:
void mainThreadError();
void otherThreadError();
private:
std::atomic<bool> m_running;
std::atomic<bool> m_signalStop;
std::atomic<bool> m_signalError;
void run() override;
void stop();
};
MyClass.cpp
#include "MyClass.h"
MyClass::MyClass(QObject *parent)
: QThread(parent)
{
start();
}
MyClass::~MyClass()
{
stop();
}
void MyClass::mainThreadError()
{
emit errorHappened(ErrorCode::ErrorA);
}
void MyClass::otherThreadError()
{
m_signalError = true;
}
void MyClass::run()
{
m_running = true;
while (!m_signalStop) {
if (m_signalError) {
emit errorHappened(ErrorCode::ErrorA);
m_signalError = false;
}
msleep(1);
}
m_running = false;
m_signalStop = false;
}
void MyClass::stop()
{
if (m_running) {
m_signalStop = true;
wait();
}
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include "MyClass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView *view = new QQuickView();
qmlRegisterType<MyClass>("MyClass", 1, 0, "MyClass");
view->setSource((QUrl(QStringLiteral("qrc:/main.qml"))));
view->create();
view->show();
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Controls 2.5
import MyClass 1.0
Rectangle {
id: root
width: 800
height: 600
focus: true
MyClass {
id: tester
onErrorHappened: {
var s
switch (errorCode) {
case MyClass.ErrorA:
s = "Error A happened"
break
}
console.log(s)
}
}
Row {
spacing: 30
Button {
id: mainThreadButton
enabled: !tester.testRunning
text: "Test on main thread"
onClicked: tester.mainThreadError()
}
Button {
id: otherThreadButton
enabled: !tester.testRunning
text: "Test on other thread"
onClicked: tester.otherThreadError()
}
}
}
qmlRegisterType makes the QObject (MyClass) class accessible in QML (Q_PROPERTY, Q_ENUM, Q_SIGNAL, Q_SLOT, Q_INVOKABLE, etc.) but does not allow the transmission of data between threads, for this case it must be registered using qRegisterMetaType<MyClass::ErrorCode>("ErrorCode"):
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include "MyClass.h"
static void registerTypes(){
qRegisterMetaType<MyClass::ErrorCode>("ErrorCode");
qmlRegisterType<MyClass>("MyClass", 1, 0, "MyClass");
}
Q_COREAPP_STARTUP_FUNCTION(registerTypes)
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource((QUrl(QStringLiteral("qrc:/main.qml"))));
view.show();
return app.exec();
}
your type "ErrorCode" defined only in thread (not in main)
For user defined types you can use (read this https://doc.qt.io/qt-5/qmetatype.html)
Q_DECLARE_METATYPE(ErrorCode);
Use standard types for arguments (int,QMap,QString....)

Save QPainter after called update

I want to draw straight lines on qml , but every time I draw a line, the previous disappears, I wonder if it has any thing to do with the update method, any ways to solve this problem.
main.qml
MouseArea{
id:fidpoint
anchors.fill: parent
onPressed: {
switch(addstate.currentText){
case 'Track':
map.setTstart(mouseX,mouseY);
draw_line.setColor("black");
draw_line.setStart(Qt.point(mouseX,mouseY));
draw_line.setEnd(Qt.point(mouseX,mouseY));
draw_line.update();
break;
}
}
onReleased: {
switch(addstate.currentText){
case 'Track':
map.addTrack(mouseX,mouseY);
draw_line.setColor("black");
draw_line.setEnd(Qt.point(mouseX,mouseY));
draw_line.update();
break;
}
}
onPositionChanged: {
switch(addstate.currentText){
case 'Track':
draw_line.setColor("black");
draw_line.setEnd(Qt.point(mouseX,mouseY));
draw_line.update();
break;
}
}
}
draw_line is the Object's id that I registered to qml from main.cpp
paint.cpp
void Paint::paint(QPainter *painter)
{
QPen pen(m_color, 2);
painter->setPen(pen);
painter->setRenderHint(QPainter::Antialiasing,true);
painter->drawLine(startNode,endNode);
}
paint is the class that inherits from QQuickPaintedItem
A possible option would be to create an image and save the changes there, but this would solve your problem in the short term. A better option is that draw_line draw only one line, then add it as a child of the item you want the canvas to be.
lineitem.h
#ifndef LINEITEM_H
#define LINEITEM_H
#include <QQuickPaintedItem>
class LineItem: public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QPoint startPos READ startPos WRITE setStartPos NOTIFY startPosChanged)
Q_PROPERTY(QPoint endPos READ endPos WRITE setEndPos NOTIFY endPosChanged)
Q_PROPERTY(QColor lineColor READ lineColor WRITE setLineColor NOTIFY lineColorChanged)
public:
using QQuickPaintedItem::QQuickPaintedItem;
void paint(QPainter *painter);
QPoint startPos() const;
void setStartPos(const QPoint &startPos);
QPoint endPos() const;
void setEndPos(const QPoint &endPos);
QColor lineColor() const;
void setLineColor(const QColor &lineColor);
signals:
void startPosChanged();
void endPosChanged();
void lineColorChanged();
private:
QPoint m_startPos;
QPoint m_endPos;
QColor m_lineColor;
};
#endif // LINEITEM_H
lineitem.cpp
#include "lineitem.h"
#include <QPainter>
void LineItem::paint(QPainter *painter)
{
painter->setRenderHint(QPainter::Antialiasing, true);
QPen pen(m_lineColor, 2);
painter->setPen(pen);
painter->drawLine(m_startPos, m_endPos);
}
QPoint LineItem::startPos() const
{
return m_startPos;
}
void LineItem::setStartPos(const QPoint &startPos)
{
if(m_startPos == startPos)
return;
m_startPos = startPos;
emit startPosChanged();
update();
}
QPoint LineItem::endPos() const
{
return m_endPos;
}
void LineItem::setEndPos(const QPoint &endPos)
{
if(m_endPos == endPos)
return;
m_endPos = endPos;
emit endPosChanged();
update();
}
QColor LineItem::lineColor() const
{
return m_lineColor;
}
void LineItem::setLineColor(const QColor &lineColor)
{
if(m_lineColor == lineColor)
return;
m_lineColor = lineColor;
emit lineColorChanged();
update();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import com.eyllanesc.org 1.0
Window {
id: win
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property LineItem currentItem: null
Rectangle{
id: canvas
anchors.fill: parent
MouseArea{
anchors.fill: parent
onPressed: {
currentItem = create_lineitem(canvas)
currentItem.lineColor = "green"
currentItem.anchors.fill = canvas
currentItem.startPos = Qt.point(mouseX,mouseY)
currentItem.endPos = Qt.point(mouseX,mouseY)
}
onReleased: currentItem.endPos = Qt.point(mouseX,mouseY)
onPositionChanged: currentItem.endPos = Qt.point(mouseX,mouseY)
}
}
function create_lineitem(parentItem, color) {
return Qt.createQmlObject('import com.eyllanesc.org 1.0; LineItem{}',
parentItem);
}
}
main.cpp
#include "lineitem.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<LineItem>("com.eyllanesc.org", 1, 0, "LineItem");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
The complete example can be found in the following link.

Taking texture from Image and drawing it in QQuickFramebufferObject. Not all instances are rendered

What I'm doing, in short:
Deriving a MyItem class from QQuickFramebufferObject
In MyItem I have a QQuickItem* sourceItem property from which I fetch a texture and draw it in a triangle
From QML I supply an Image called sourceItem to MyItem. This Image has cache: false
When the Image finishes loading, I wait one frame and then call update on MyItem
I have a 6x6 grid of MyItem instances
The problem is that some of the instances of MyItem draw nothing:
Any ideas?
My code:
main.cpp:
#include <QQmlApplicationEngine>
#include <QGuiApplication>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
#include <QSGTextureProvider>
#include <QSGTexture>
#include <QQuickWindow>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include "propertyhelper.h" // this file is from http://syncor.blogspot.bg/2014/11/qt-auto-property.html
class MyItem : public QQuickFramebufferObject {
Q_OBJECT
AUTO_PROPERTY(QQuickItem*, sourceItem)
public:
Renderer *createRenderer() const;
};
class MyItemRenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions {
public:
MyItemRenderer() {
initializeOpenGLFunctions();
m_program.addShaderFromSourceCode(QOpenGLShader::Vertex,
"in highp vec2 aPos;\
out highp vec2 vTexCoord;\
\
void main() {\
gl_Position = vec4(aPos, 0.0, 1.0);\
vTexCoord = aPos * .5 + .5;\
}"
);
m_program.addShaderFromSourceCode(QOpenGLShader::Fragment,
"in highp vec2 vTexCoord;\
out vec4 outputColor;\
uniform sampler2D uTex;\
\
void main() {\
outputColor = texture(uTex, vTexCoord);\
}"
);
m_program.link();
m_program.setUniformValue("uTex", 0);
createGeometry();
}
void synchronize(QQuickFramebufferObject* qqfbo){
auto item = (MyItem*)qqfbo;
m_window = item->window();
m_tex = item->sourceItem()->textureProvider()->texture();
}
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
return new QOpenGLFramebufferObject(size, format);
}
void paintGeometry() {
m_program.enableAttributeArray("aPos");
m_program.setAttributeArray("aPos", m_vertices.constData());
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
m_program.disableAttributeArray("aPos");
}
void createGeometry() {
m_vertices << QVector2D(-1, -1);
m_vertices << QVector2D(1, -1);
m_vertices << QVector2D(-1, 1);
}
void render() {
glDisable(GL_DEPTH_TEST);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
m_program.bind();
glActiveTexture(GL_TEXTURE0);
m_tex->bind();
paintGeometry();
m_window->resetOpenGLState();
}
private:
QQuickWindow* m_window;
QVector<QVector2D> m_vertices;
QSGTexture* m_tex;
QOpenGLShaderProgram m_program;
};
QQuickFramebufferObject::Renderer *MyItem::createRenderer() const {
return new MyItemRenderer();
}
int main(int argc, char** argv) {
qmlRegisterType<MyItem>("MyItem", 1, 0, "MyItem");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
id: window
width: 600
height: 600
Flow {
anchors.fill: parent
Repeater {
model: 36
delegate: MyItemWrapper {
width: 100
height: 100
}
}
}
}
MyItemWrapper.qml:
import QtQuick 2.5
import MyItem 1.0
Item {
Image {
x: -100000 // hide the sourceItem
id: sourceItem
layer.enabled: true
source: "https://images-na.ssl-images-amazon.com/images/M/MV5BMTg2MTMyMzU0M15BMl5BanBnXkFtZTgwOTU3ODk4NTE#._V1_SX300.jpg"
cache: false
function updateCppItemOnce() {
window.afterRendering.disconnect(updateCppItemOnce);
cppItem.update();
}
onStatusChanged: {
if (status == Image.Ready) {
window.afterRendering.connect(updateCppItemOnce);
}
}
}
MyItem {
sourceItem: sourceItem
anchors.fill: parent
id: cppItem
}
}
Notes:
I tried enabling OpenGL logging but got no messages
I've tried the testcase on 3 other PCs, all of them much weaker/slower than this one. The bug doesn't occur on them. So I think the bug happens only on fast PCs - it may be a timing problem.
I now realized that instead of the clunky way I was calling update(), I could just connect to the item->sourceItem()->textureProvider()->textureChanged() signal. Did that and now it works like a charm.
I'd still like to know why my original way fails, though :)
Gunnar Sletta provided the solution as a comment in my bugreport.
I suspect the problem is your use of the 'afterRendering' signal. When running on a threaded render loop this signal will fire when rendering is done and the slot will be called almost right away on the main thread. Depending on where the threaded render loop is when the image completes loading that means you'll have the slot invoked potentially right away. When the scene graph reaches the sync phase, the order in which nodes are processed is arbitrary, so some nodes may be processed before the image and some after. Those processed before the image gets a texture gets null and those processed after gets a valid texture.
It would be better to connect to afterSynchronization signal as this would actually fire first after the synchronization of the Image was guaranteed to have completed.
Indeed, using afterSynchronizing fixed my problem.

Drawing in QQuickFramebufferObject via texture taken from an Item. It fails unless I repaint/update constantly

What my code does, in short:
Create a simple Rectangle named rect, with layer.enabled: true.
Pass rect to an instance of my QQuickFramebufferObject subclass called itemSnapshotter
In synchronize, take a pointer to the texture provided by rect
In render, draw a rectangle textured with the texture I took
When main.qml finishes loading, call itemSnapshotter.update(); for the first and only time.
The problem is that the QQFBO draws nothing, unless I uncomment the update call at the end of render(). Any idea why?
My code:
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
#include <QSGTextureProvider>
#include <QObject>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QQuickWindow>
#include <QOpenGLFunctions>
// propertyhelper.h is from http://syncor.blogspot.bg/2014/11/qt-auto-property.html
#include "propertyhelper.h"
class ItemSnapshotter : public QQuickFramebufferObject {
Q_OBJECT
AUTO_PROPERTY(QQuickItem*, sourceItem)
public:
Renderer *createRenderer() const;
};
class ItemSnapshotterRenderer
: public QObject
, public QQuickFramebufferObject::Renderer
, protected QOpenGLFunctions
{
Q_OBJECT
public:
ItemSnapshotterRenderer() {
initializeOpenGLFunctions();
initShader();
createGeometry();
}
void createGeometry() {
m_vertices << QVector2D(0, 0) << QVector2D(0, 1) << QVector2D(1, 1);
m_vertices << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1);
}
void initShader() {
m_shaderProgram.addShaderFromSourceFile(
QOpenGLShader::Vertex, ":/PassThrough.vert.glsl");
m_shaderProgram.addShaderFromSourceFile(
QOpenGLShader::Fragment, ":/PassThrough.frag.glsl");
m_shaderProgram.link();
m_shaderProgram.bind();
glActiveTexture(GL_TEXTURE0);
m_shaderProgram.setUniformValue("uTex", 0);
}
void prepareShader() {
m_shaderProgram.enableAttributeArray("aPos");
m_shaderProgram.setAttributeArray("aPos", m_vertices.constData());
m_shaderProgram.bind();
}
void render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
prepareShader();
m_tex->bind();
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
m_window->resetOpenGLState();
//update();
}
void synchronize(QQuickFramebufferObject* qqfbo){
auto parentItem = (ItemSnapshotter*)qqfbo;
m_window = parentItem->window();
copyTexture(*parentItem);
}
void copyTexture(const ItemSnapshotter &srcItem) {
QQuickItem* sourceItem = srcItem.sourceItem();
QSGTextureProvider* sourceTexProvider = sourceItem->textureProvider();
m_tex = sourceTexProvider->texture();
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR) {
qDebug("\tgl error: 0x%x", err, 0, 16);
}
}
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); // TODO simpler format
return new QOpenGLFramebufferObject(size, format);
}
private:
QOpenGLShaderProgram m_shaderProgram;
QQuickWindow* m_window;
QSGTexture* m_tex;
QVector<QVector2D> m_vertices;
};
QQuickFramebufferObject::Renderer *ItemSnapshotter::createRenderer() const {
return new ItemSnapshotterRenderer();
}
int main(int argc, char *argv[]) {
qmlRegisterType<ItemSnapshotter>("ItemSnapshotter", 1, 0, "ItemSnapshotter");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
import ItemSnapshotter 1.0
Window {
visible: true
width: 640
height: 480
Row {
Rectangle {
layer.enabled: true
id: rect
width: 100
height: 100
color: "red"
border.color: "black"
}
ItemSnapshotter {
id: itemSnapshotter
sourceItem: rect
width: sourceItem.width
height: sourceItem.height
}
}
Component.onCompleted: {
itemSnapshotter.update();
}
}
PassThrough.frag.glsl:
varying highp vec2 vTexCoord;
uniform sampler2D uTex;
void main() {
gl_FragColor = texture2D(uTex, vTexCoord);
}
PassThrough.vert.glsl:
attribute highp vec2 aPos;
varying highp vec2 vTexCoord;
void main() {
gl_Position = vec4(aPos, 0.0, 1.0);
vTexCoord = aPos;
}

QQmlListProperty : Cannot assign to non-existent property "lon" lon: "3"

I wish to see how QQmlListProperty is used. I tried the following but I am not sure if this is the correct way to do it. I got an error shown in the title.
aa.h
#ifndef IMO
#define IMO
#include <QQmlListProperty>
#include "DummyClass.h"
class SubClass: public QObject
{
Q_OBJECT
Q_PROPERTY (QQmlListProperty <DummyClass> functionWhat READ functionWhat)
public:
SubClass (QObject *parent = 0);
QQmlListProperty <DummyClass> functionWhat()
{
return QQmlListProperty <DummyClass> (this, dummyList);
}
private:
QList <DummyClass*> dummyList;
};
#endif
aa.cpp
#include "aa.h"
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
SubClass :: SubClass (QObject *parent) : QObject (parent)
{
qDebug ("In Constructor.");
}
void appendDummies (QQmlListProperty<DummyClass> *property, DummyClass *dc)
{
qDebug ("Do nothing. Can't add to a directory using this method.");
}
DummyClass.h
#ifndef IM
#define IM
#include <QObject>
class DummyClass : public QObject
{
private:
Q_OBJECT
Q_PROPERTY (bool functionWhat READ functionWhat WRITE functionSetWhat)
public:
DummyClass (QObject *parent = 0) {}
float lat; float lon;
bool functionWhat ()
{
qDebug ("In functionWhat");
}
public slots:
void functionSetWhat (bool arg)
{
qDebug ("In functionWhatSlot");
}
signals:
void functionWhatChanged (bool arg);
};
#endif
main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "/home/anisha/qmllistproperties/DummyClass.h"
#include <QtQml>
#include "/home/anisha/qmllistproperties/aa.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
const char* ocuui = "OCUUI"; // #uri OCUUI
qmlRegisterType <DummyClass> (ocuui, 1, 0, "DummyClass");
qmlRegisterType <SubClass> (ocuui, 1, 0, "SubClass");
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/untitled/main.qml"));
viewer.showExpanded();
return app.exec();
}
main.qml
import QtQuick 2.0
import OCUUI 1.0
Rectangle {
width: 360
height: 360
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
SubClass
{
functionWhat:
[
DummyClass
{
lat: "2"
lon: "3"
}
]
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
You seem to be missing the property declarations for lat and lon properties in your DummyClass:
Q_PROPERTY (int lat READ getLat WRITE setLat)
Q_PROPERTY (int lon READ getLon WRITE setLon)
You should study this example.

Resources