Pass data between qtquick controls (slider) and C++ application
-
Hello ,
I am facing some confusion regarding passing data between the qt qick slider and QQuickItem object in C++, that results in application crash. I have the following class with the defined properties:
[code]
class TessellationSceneItem : public QQuickItem
{
Q_OBJECT
//the property defintion must be at the top of the class
//the notify signal is needed for correct property bindings
Q_PROPERTY(float outer READ outer WRITE setOuter NOTIFY outerChanged)
Q_PROPERTY(float inner READ inner WRITE setInner NOTIFY innerChanged)
Q_PROPERTY(float maxPatchVertices READ maxPatchVertices NOTIFY maxPatchVerticesChanged)public: TessellationSceneItem(); void setOuter(float); void setInner(float); float inner(); float outer(); float maxPatchVertices() const; signals: void innerChanged(); void outerChanged(); void maxPatchVerticesChanged(); ............. ............ private: TeapotTessellation* mTessScene; QTime mTIme; };
[/code]
The definition of the setter and getter methods:
[code]
void TessellationSceneItem::setOuter(float outerFactor)
{
if(outerFactor == mTessScene->outerTessellationFactor())
return;mTessScene->setOuterTessellationFactor(outerFactor); //do not emit notifier if value does not actually change emit outerChanged(); } void TessellationSceneItem::setInner(float innerFactor) { if(innerFactor == mTessScene->innerTessellationFactor()) return; mTessScene->setInnerTessellationFactor(innerFactor); emit innerChanged(); } float TessellationSceneItem::inner() { return (float)(mTessScene->innerTessellationFactor()); } float TessellationSceneItem::outer() { return (float)(mTessScene->outerTessellationFactor()); } float TessellationSceneItem::maxPatchVertices() const { return (float) (mTessScene->getMaxPatchVertices()); }
[/code]
The idea is to have the slider value loaded with the value set in the underlying C++ application. Then with every slider interaction , the underlying value( outer, inner) in the C++ side will be updated. I tried the following in the .qml file , but the application crashes.
[code]
import QtQuick 2.0
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import TeapotTessellation 1.0Item { id: root width: 512; height: 512 TessellationSceneItem { id: tessSceneItem // outer: outerTessellationSlider.value // inner: innerTessellationSlider.value } GridLayout { id: controlLayout columns: 2 Text { id: outerTessellationText text: qsTr("Outer Tess. Factor: ") color: "black" opacity: 1.0 } Slider { id: outerTessellationSlider opacity: 1.0 // value: tessSceneItem.outer minimumValue: 1.0 maximumValue: 64//tessSceneItem.maxPatchVertices stepSize: 1.0 Layout.fillWidth: true } Text { id: innerTessellationText text: qsTr("Inner Tess. Factor: ") color: "black" opacity: 1.0 } Slider { id: innerTessellationSlider opacity: 1.0 // value: tessSceneItem.inner minimumValue: 1.0 maximumValue: 64.0//tessSceneItem.maxPatchVertices stepSize: 1.0 Layout.fillWidth: true } } Rectangle { id: rect color: "red" radius: 10 opacity: 0.1 border.color: "black" anchors.fill: controlLayout } }
[/code]
The commented part of the .qml file are the places that cause the application crash.
Even assigning fixed values for inner / outer inside the .qml file results in application crash. I tried as follows:
[code]
Q_PROPERTY(float inner READ inner WRITE setInner NOTIFY innerChanged)
[/code]Then inside the .qml file I have the following snippet:
[code]
TessellationSceneItem
{
id: tessSceneIteminner: 10 }
[/code]
I get the crashing point while running with debugger at the following points:
[code]
void TessellationSceneItem::setInner(float innerFactor)
{
if(innerFactor == mTessScene->innerTessellationFactor())
return;mTessScene->setInnerTessellationFactor(innerFactor); emit innerChanged(); } .............................. .............................. GLfloat TeapotTessellation::innerTessellationFactor() { return mInnerTessellationFactor; }
[/code]
I am clueless. Any one in the forum would like to address this issue ?
The slider value is of qreal type , where the C++ side is of float type. Do I have to do any explicit casting ? If yes, where do I have to do it?
Thanks
-
Hi,
Just a silly question, have you initialized TeapotTessellation ?
-
If the following is what you ask , then I guess , yes:
[code]
int main(int argc, char **argv)
{
QGuiApplication app(argc,argv);/* * any QObject derived C++ class can be registered as the definiton of a QML object type. Once a class is registered * with the QML type system, the class can be declared and instantiated like any other object type * from QML code. Once created, a class instance can be manipulated from QML. And the properties, methods and signals * of any QObject-derived class are accessible from QML code. * */ qmlRegisterType<TessellationSceneItem>("TeapotTessellation",1,0,"TessellationSceneItem"); QQuickView view; //The view will automatically resize the root item to the size of the view. view.setResizeMode(QQuickView::SizeRootObjectToView); view.setSource(QUrl("qrc:///qml/main.qml")); view.show(); return app.exec();
}
[/code]As you see from the last post that that slider value type is qreal in the qml whereas , on the C++ side i am dealing with float type as follows:
[code]
//the property defintion must be at the top of the class
//the notify signal is needed for correct property bindings
Q_PROPERTY(float outer READ outer WRITE setOuter NOTIFY outerChanged)
Q_PROPERTY(float inner READ inner WRITE setInner NOTIFY innerChanged)
Q_PROPERTY(float maxPatchVertices READ maxPatchVertices NOTIFY maxPatchVerticesChanged)
[/code]Could that be the reason behind the crash. Where and when to make this casting, if it is mandatory?
Thanks
-
That is just the registration of TessellationSceneItem so that you can use it as a Component in QML. But in TessellationSceneItem code you have used TeapotTessellation object in one of the function:
@
void TessellationSceneItem::setInner(float innerFactor)
{
if(innerFactor == mTessScene->innerTessellationFactor())
return;
...
}
@where mTessScene is object of class TeapotTessellation. Have you initialized this one ?
-
yes i have, as follows:
[code]
void TessellationSceneItem::sync()
{
if(!mTessScene)
{
//initialize the tessellation renderer
mTessScene = new TeapotTessellation();//start tracking the time mTIme.start(); //make the connection to render the opengl scene before the scene graph rendering connect(window(),SIGNAL(beforeRendering()),mTessScene,SLOT(paint()),Qt::DirectConnection); QTimer *timer = new QTimer(); connect(timer,SIGNAL(timeout()),this,SLOT(updateTime()),Qt::DirectConnection); timer->start(); } mTessScene->setViewportSize(window()->width() * window()->devicePixelRatio(), window()->height() * window()->devicePixelRatio()); //set the timer value from the qml to the mTessScene //in other words, copy the state of the item into the renderer
}
[/code]
And the above gets called in the following manner:
[code]
TessellationSceneItem::TessellationSceneItem()
:mTessScene(0)
{
connect(this,SIGNAL(windowChanged(QQuickWindow*)),this,SLOT(handleWindowChanged(QQuickWindow*)));
}void TessellationSceneItem::handleWindowChanged(QQuickWindow *win)
{
if(win)
{
connect(win,SIGNAL(beforeSynchronizing()),this,SLOT(sync()),Qt::DirectConnection);
connect(win,SIGNAL(sceneGraphInvalidated()),this,SLOT(cleanup()),Qt::DirectConnection);//define the opengl surface format //with the most modern profile //while retaining the compatibility profile QSurfaceFormat f = win->format(); f.setMajorVersion(4); f.setMinorVersion(3); f.setSamples(4); f.setStencilBufferSize(8); f.setProfile(QSurfaceFormat::CompatibilityProfile); win->setFormat(f); win->setClearBeforeRendering(false); }
}
[/code]
-
Ok.. This seems to be good. Now, from your first post
@
void TessellationSceneItem::setInner(float innerFactor)
{
if(innerFactor == mTessScene->innerTessellationFactor())
return;mTessScene->setInnerTessellationFactor(innerFactor); emit innerChanged(); } .............................. .............................. GLfloat TeapotTessellation::innerTessellationFactor() { return mInnerTessellationFactor; }
@
Where do you exactly get the crash ? During mTessScene->setInnerTessellationFactor(innerFactor) ?
How does setInnerTessellationFactor look like ? -
Hello
The issue is resolved now. Let me explain how I solved it. I have been following the documentation "Scene Graph - OpenGL Under QML" as a standard to adapt to my own project. According to it the renderer was initialized a bit later as follows:
[code]
void Squircle::sync()
{
if (!m_renderer) {
m_renderer = new SquircleRenderer();
connect(window(), SIGNAL(beforeRendering()), m_renderer, SLOT(paint()), Qt::DirectConnection);
}
m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
m_renderer->setT(m_t);
}
[/code]In my project i did something similar as follows:
[code]
void TessellationSceneItem::sync()
{
if(!mTessScene)
{
//initialize the tessellation renderer
mTessScene = new TeapotTessellation();//start tracking the time mTIme.start(); //make the connection to render the opengl scene before the scene graph rendering connect(window(),SIGNAL(beforeRendering()),mTessScene,SLOT(paint()),Qt::DirectConnection); QTimer *timer = new QTimer(); connect(timer,SIGNAL(timeout()),this,SLOT(updateTime()),Qt::DirectConnection); timer->start(); } mTessScene->setViewportSize(window()->width() * window()->devicePixelRatio(), window()->height() * window()->devicePixelRatio());
}
[/code]But the the Qt's Property System call the property setter and getter function right after the instantiation of the TessellationSceneItem, while the TeapotTessellation* mTessScene is not initialized yet. This is why i was getting the application crash. Now i moved the renderer class object instantiation inside the constructor of TessellationSceneItem as follows:
[code]
TessellationSceneItem::TessellationSceneItem()
:mTessScene(0)
{//initialize the tessellation renderer mTessScene = new TeapotTessellation(); //start tracking the time mTIme.start(); QTimer *timer = new QTimer(); connect(timer,SIGNAL(timeout()),this,SLOT(updateTime()),Qt::DirectConnection); timer->start(); connect(this,SIGNAL(windowChanged(QQuickWindow*)),this,SLOT(handleWindowChanged(QQuickWindow*)));
}
[/code]Now I am encountering a new issue, i think it could be addressed in the same thread.
The slider control inside the qml file has a property called "maximumValue" and it contains a value that depends on the opengl initialization of the underlying renderer class.
[code]
class TessellationSceneItem : public QQuickItem
{
Q_OBJECT
......................
......................
Q_PROPERTY(float maxPatchVertices READ maxPatchVertices CONSTANT).....................
.....................
};
[/code]Once the TessellationSceneItem is initialized the maxPatchVertices is called and it is containing the value 0 since by then the opengl initialization has not executed yet. How to deal with this issue. This "maxPatchVertices" contains the value for the slider value for "maximumValue" and "maxPatchVertices" comes from the opengl function
[code]
glGetIntegerv(GL_MAX_PATCH_VERTICES,&mMaxPatchVertices);
[/code]The above function should return some value greater than 0 . I am getting 0 instead. Is there any way slider get the updated value in the qml side. The value remain constant in the qml side. Thats whay i have CONSTANT defined the peoperty definition.
In a nutshell i would like to have the maximum vertices per patch to be assingned to the property maxPatchVertices and accessible in qml. I am not getting the correct one .
Any hint to get it done ?
Thanks
-
Hello forum,
I did not get much of a response from my last post. I believe that I did not explain the issue well enough. Let me try again. Consider the following class structures:
[code]
class A : public QObject
{
Q_OBJECTpublic:
A(); ~A(); void initialize(); GLint method1();
private:
GLint var1;
}
[/code]Lets describe a bit about the class A. The public method method1() returns the value for var1. The initialize() must be called prior calling method1() so that the member variale value contains the updated value.
Now lets look into another class as follows:
[code]
class B : public QQuickItem
{
Q_OBJECTpublic: B(); Q_INVOKABLE float method2();
private:
float var2; A* a;
};
................
................B::B()
{
a = new A();
}float B::method2()
{
return (float)(a->method1());
}[/code]
While running the program I found that B::method2() is always returning ZERO, which is incorrect value. The method2 is called from the qml file as follows:
[code]
BItem { id: bItem } Slider { id: slider1 opacity: 1.0 minimumValue: 1.0 maximumValue: bItem.maxPatchVertices() stepSize: 1.0 Layout.fillWidth: true onValueChanged: bItem.outer = value }
[/code]
I tried the understand the program flow with the help of the debugger and found that B::method() is called and the value is not updated even after the A::initialize() changes the value. I have seen examples where C++ class member variable is updated from the qml , not vice versa. I believe I am experiencing the latter issue here. As you can see from the qml file, I trying to get the updated value from C++ class member function and set it as a property value fom the slider.
Any idea/refference/example to resolve this ?
Thanks