How to access QML Surface3DSeries from C++? - c++

How to access a qml Surface3DSeries object from C++?
I’m having serious problems with the QObject::findChildren() approach. My main.qml declares a Surface3D with objectName “mySurface” and a Surface3DSeries item with objectName: “mySurfaceSeries”, something like this:
Surface3D {
objectName: "mySurface"
width: parent.width
height: parent.height
Surface3DSeries {
objectName: "mySurfaceSeries"
ItemModelSurfaceDataProxy {
[...]
}
}
}
After loading the qml in main.cpp, my C++ code calls rootObject->findChildren(). The returned object tree contains an object named “mySurface” but does NOT contain an object named “mySurfaceSeries”. I have no idea why - I thought that every item in qml would be represented in the object tree. There are no obvious errors in the qml, and the Surface3D is displayed properly with data provided by the Surface3DSeries.
The tree's “mySurface” object is not a Q3DSurface (through which I could access the series) but rather a QtDataVisualization::DeclarativeSurface which is not described anywhere that I can find. The DeclarativeSurface class is defined in qtdatavis3d/src/datavisualizationqml2/declarativesurface_p.h and contains a promising method seriesList() - but QtCreator says “No type named ‘DeclarativeSurface’ in namespace ‘QtDataVisualization’”, apparently that file is not in QtCreator’s include-file path.
So I am at a loss right now - how can I access Surface3DSeries from C++?
Thanks!
Tom

It is possible, but not supported and not documented.

Related

QML: modifying a property of a Child object that is defined in a different QML file (not main.qml)

Basically, I have something like:
Main.qml:
ApplicationWindow{
width: 500
height: 500
Page{
id: page0
DataPage{
id: datapage0
}
}
}
DataPage.qml:
Page{
id: displayPage
DataDisplay{
id: dataShow
}
}
DataDisplay.qml:
Label{
text: "data: "
}
TextArea{
id: dataArea
text: ""
}
I've removed the stuff I think isn't relevant (such as anchors, height, width, etc.). Now, in main.qml, I have a signal coming from the c++ backend:
Connections{
target: modb
onPostData: {
page0.datapage0.dataShow.dataArea.text = string;
}
And I get the following error:
TypeError: Cannot read property 'dataArea' of undefined
So, I wanted to ask: how do I connect that signal to the child object that is defined in DataDisplay.qml? I'm able to get info into main.qml using signals, but seem to be unable to dereference child objects
Edit:
main.cpp:
QQmlContext* ctx0 = engine.rootContext();
ctx0->setContextProperty("ark", &ark);
QQmlContext* ctx1 = engine.rootContext();
ctx1->setContextProperty("comm", ark.comm);
QQmlContext* ctx2 = engine.rootContext();
ctx2->setContextProperty("modb", ark.modb);
is how I set the Context (of 3 classes, as you can see). I'm able to get signals from any of the three into main.qml, as well as call slots in any of the three in main.qml; I haven't yet tried to call slots from the c++ classes in the other qml files, but I assume it would work because I can access the parent's properties from the child
1 - you have 3 pointers pointing to the same object. One is enough. Really!
2 - as long as ark is properly implemented, you can access ark.comm and ark.modb from QML, no need to expose them individually.
3 - you don't seem to understand the scope of ids. Take a look at this exhaustive answer. dataShow is simply not visible from wherever you made the connection.
4 - context properties are not very efficient, that's more of a "quick and dirty" approach to expose C++ to qml. For optimal performance consider a more efficient approach.
All in all, you exhibit the typical symptoms of "getting ahead of yourself". Take the time to learn before you practice.
As you indeed assume you can use the modb variable in the other qml's as well, since it is added to the rootContext. I would advise this option.
Another option you could try is just using dataArea.text = string since the id's go all over the place (it's javascript after all), you should use strong id's in this case.
Another option is to define property alias's to pass the string through over the objects (See Qt docs). Or use property string, but that's even more work.

What is the difference between a QML component and a QML object type?

Qt's doc, says that :
Components are reusable, encapsulated QML types with well-defined
interfaces.
Components are often defined by component files - that is, .qml files.
The Component type essentially allows QML components to be defined
inline, within a QML document, rather than as a separate QML file.
What is the meaning of "encapsulated QML types with well-defined interfaces" ?
Also when it comes to define a new QML object type, from Qt's doc we have :
To create an object type, a QML document should be placed into a text
file named as TypeName.qml where TypeName is the desired name of
the type
So what is the difference between a QML component and a QML object type ?
Thank you.
in this document they call custom objects as component.
A component is a reusable type with a well-defined interface, built entirely in QML. Any snippet of QML code can become a component, by placing the code in a file ".qml" where is the new component name, beginning with an uppercase letter. These QML files automatically become available as new QML element (object) types to other QML components and applications in the same directory.
custom object type and component are the same but object like rectangle isn't component .
whenever you create a custom object with TypeName.qml file , engin loads the TypeName.qml document as a component.
we have 2 kind of component:
defined by component file - that is, .qml files. so we can say this kind of component is same as qml document.
The Component type essentially allows QML components to be defined inline, within a QML document, rather than as a separate QML file.
in this document they say :
An instance of the object type defined by a document may be created using a Component in QML code, or a QQmlComponent in C++. Alternatively, if the object type is explicitly exposed to the QML type system with a particular type name, the type may be used directly in object declarations in other documents.
so if you create a custom object type you actually create a component and vice versa.
What is the meaning of "encapsulated QML types with well-defined interfaces" ?
To me, the encapsulated part means that the QML contained in the component is self-contained and hence can be reused. The well-defined part means that only the properties defined in the root object of the component can be accessed by outside code, similar to how you would use the protected and private keywords in C++.
So what is the difference between a QML component and a QML object type ?
My understanding of what the documentation is saying is that a Component that is declared inline can't be instantiated in the same manner [1]. For example:
Component {
id: myComponent
Text {
text: "Hello"
}
}
You can use myComponent as e.g. a delegate:
ListView {
// ...
delegate: myComponent
}
But you can't use it to directly instantiate the type it represents:
myComponent {
// ...
}
That's why you need to move the component into its own file (a "QML Object Type"):
import QtQuick 2.0
Text {
text: "Hello"
}
Assuming you named the file MyType.qml, you can directly instantiate the type like this:
MyType {
text: "Some text"
}
[1] As of Qt 5.15, it will be possible to instantiate components inline. See this section once 5.15 is released.

Accessing Qt / QML objects with C++

I'm working on a C++ Qt project that will eventually communicate with the serial port. One part of this is accessing QML objects in the C++ portion. I have code that can set properties of the QML, but accessing those features that are methods now has me stumped. View the following code:
object = view.rootObject();
rect = object->findChild<QObject *>("box");
rect->setProperty("color", "red"); // Verifies the object tree is accessible
viewer = object->findChild<QObject *>("viewer"); // Access the viewer text box
viewer->append("dummy text"); // OOPS! This doesn't compile!!!
Now, the type as a method setProperty(..), but how do you access methods of an object. "viewer" is a TextArea and I want to first do a selectAll(), then a cut() to clear the box.
The question here is how is this coded? Thanks all.
Of course it would not compile, QObject doesn't have an append() method.
If it is a C++ function, you will have to qobject_cast to the appropriate type that has it. This however is not always readily available for many of the stock QML types that are implemented in C++, and as C++ types they are not part of the public API and not generally intended for direct use by an end user.
If it is a JS function, you will have to use QMetaObject::invokeMethod. That will also work for C++ functions for which meta data has been generated. Which is also how setProperty() works, whereas setColor() would not work with a QObject* much like append() doesn't.
Last but not least, there is absolutely no good reason for you to be doing those kinds of things from C++. Using QML objects from C++ is poor design and an anti-pattern. You will only develop bad habits trying to do that. Such interactions must be limited to a clearly defined interface using signals, slots and properties. Generally speaking, it is OK for QML to reach into C++, because that only happens through an exposed interface, but the opposite way, even if possible, should not utilized.
Think of it like this - a car uses the engine, the engine doesn't use the car. And the engine control is interfaced through the starter key and the gas pedal, it is not used directly. The C++ stuff should be reserved to the application engine - the high performance or efficiency core logic, or the back-end, whereas the QML part is for the GUI/front-end.
The author's QML part may expose alias property to operate with desired text field content:
import QtQuick 2.0
import QtQuick.Controls 1.2
Item {
property alias viewerText: viewer.text // added
width: 350
height: 450
TextArea {
id: viewer
x: 8
y: 8
width: 223
height: 415
text: "text"
font.pixelSize: 12
objectName: "viewer"
}
Button {
id: open
x: 251
y: 8
text: "Open"
}
}
And then the author's C++ part can easily do:
auto* object = view.rootObject();
viewer = object->findChild<QObject *>("viewer");
viewer->setProperty("viewerText", "dummy text"); // using the new property added
Using the posted answer here using the invoke method, here's the solution that works:
// C++ Code to call function reset()
QMetaObject::invokeMethod(object, "reset");
// QML code to select all text the delete it
function reset() {
viewer.selectAll()
viewer.cut()
}

How to get a valid instance of a QQuickItem on C++ side

Alright. I have searched a lot but haven't got a good solution yet. I am new to Qt. I have a class which is a QQuickItem like so,
class MyQuickItemClass : public QQuickItem
{
Q_OBJECT
SetInfo(SomeCppClass object)
};
I do a qmlRegisterType in my main.cpp to register it on the qml side like this,
qmlRegisterType< MyQuickItemClass >("MyQuickItemClass", 1, 0, "MyQuickItemClass");
All fine till here. But -> I want to set an object instance & some properties in MyQuickItemClass which some C++ logic in it as well & then pass the MyQuickItemClass object to qml. Or, get a valid instance of MyQuickItemClass from Qml. How can I get a vlid instance MyQuickItemClass object instance from QML on C++ side in main.cpp ?
I tried doing the following learning from the link here. But this technique creates two separate objects of MyQuickItemClass. One from QML, & one from c++ side. Hence does not work for me.
Following is how I am trying to do this after lot of searching.
int main(int argc, char *argv[])
{
qmlRegisterType< MyQuickItemClass >("MyQuickItemClass", 1, 0, "MyQuickItemClass");
QQmlApplicationEngine engine;
SomeCppClass someCppClassObject;
someCppClassObject.updateSomething();
MyQuickItemClass myquickItemObject;
myquickItemObject.SetInfo(someCppClassObject);
engine.rootContext()->setContextProperty("myquickItemObject", &myquickItemObject);
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
return app.exec();
}
But, doing the above gets the constructor of MyQuickItemClass called twice. Once from cpp side when I created an object, and once from qml side. Verified this by placing a breakpoint in the constructor of MyQuickItemClass as well. As a result, someCppClassObject that I had set is null inside MyQuickItemClass when program runs. Because qml has made the final call to MyQuickItemClass to instantiate, thusly ignoring the MyQuickItemClass object that I created in main.cpp.
Here is my qml code for MyQuickItemClass:
import QtQuick 2.5
import MyQuickItemClass 1.0
ParentContainerItem {
id: parentItem
color: "black"
MyQuickItemClass {
id: myQuickItemID
visible: true
objectName: "myQuickItem"
property bool someProperty1: false
property bool someProperty2: true
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
}
//Other qml components
}
And this is the C++ class whose object needs to be set into MyQuickItemClass.
SomeCppClass {
//Pure C++ class. No Qt
}
Please note that I need to keep MyQuickItemClass derived from QQuickItem. Please suggest...
Generally it is a good idea to avoid accessing QML instantiated objects from outside as most of the access methods generated a dependency from C++ toward QML, restricting the way the QML tree is done.
E.g. requiring certain objects to exist at certain point in times, having specific objectName values, etc.
It is better to either "register" the object from QML side by calling a method on an exposed C++ object/API or to make the QML instantiate object register itself from within its own C++ code.
The latter is obviously inherently automatic, i.e. each instance of such a class would do that, while the former puts it at the discretion of the QML code which of the created instances it wants to make known.
Doing the following from a suggestion in discussion here solves the issue & gets a valid object to the QuickItem qml file
QQuickItem *myItem = engine.rootObjects()[0]->findChild<QQuickItem *>("myQuickItem");

Include another QML file from a QML file

There's another question on Stackoverflow about this matter but I don't find the accepted solution possible. So I ask again because the old question is out of attention.
The situation is this way. I have application screens defined by 'main.qml', 'feature1.qml', 'feature2.qml'.
These screens share the same toolbar below title bar. The toolbar has multiple items so copy-paste the QML code is like crazy. This question: QML file include - or one monolithic file (structure QML code)? says it's possible to just use QML file name as component name but I can't get it working.
Any solution? with details pls.
Let's assume you have a file called main.qml and a component in another file called MyCustomText.qml. If both files are in the same directory you can directly load the component like this:
// in Main.qml
Rectangle {
id: root
MyCustomText {
text: "This is my custom text element"
}
}
If MyCustomText.qml is in another subdirectory MyComponents for example to group all your custom components together, you first need to import the directory before using the component the same way:
// in Main.qml
import "MyComponents"
Rectangle {
id: root
MyCustomText {
text: "This is my custom text element"
}
}
Another important thing to note is that your QML files should always start with an uppercase letter if you want to be able to use them this way
Of course your Loader solution works too but this is the easiest way to import QML files in other components.
Finally I have dug it out from internet. Let's say the to-be-included file is 'mycomponent.qml' in this directory structure (Qt Quick):
projectdir/
qml/
projectname/
main.qml
mycomponent.qml
The content of 'mycomponent.qml' (for example):
Text {
text:"Hello, Scooby Doo!";
}
We have to load it this way (in 'main.qml'):
Rectangle {
...
Loader {
source:"mycomponent.qml";
}
...
}
See Qt documentation about reuseable components.
The imported QML file defines a type whose name is the same as the filename (capitalized, less the .qml suffix). QML calls the type a reuseable component. You use that type name to instantiate an object in the importing QML document (file.)
Its not like a C language include, where the text of the included file is inserted into the including file. Its more like importing the name of a class in Python, and then instantiating an object of that class in the importing file. Or somewhat similar to Javascript, the imported file is creating a prototype object, and the importing file is prototypically inheriting from it. Except note the discussion about the root object and what properties of the component will be visible (because of QML's document scoping.) You won't be able to access everything in the imported file as if it were a C include, a Python import, or a JS inheritance.
It's easy like that. Put all your file components in a folder like "components". In your case, the name of the file can be Toolbar.qml. Write the QML code for you toolbar, my example will draw a red rectangle.
import QtQuick 2.6
Item {
width: 500
height: 100
Rectangle {
width: 500
height: 100
color: "red"
radius: width * 0.5
}
}
And then, in your screens which you want to use this component (for example, file main.qml), is simple like that:
import "components" as Components
Components.Toolbar {
Layout.fillHeight: true
}
Take care about the location of files, and still all components should start with a Caps letter, in this example:
\main.qml
\components\Toolbar.qml
You can just call the Name of the qml.
for ex.
I have 2 qml file.
The main.qml and Merchant.qml
I just called the Merchant. it should be showed in intellisense.
ApplicationWindow {
id: mainWindow
visible: true
Component{
id: merchantsComponent
Merchant{
id: merchants
width: mainWindow.width
height: mainWindow.height
}
}
}
You can just call that compenent to Loader
You can directly call:
Window {
id: mainWindow
visible: true
Feature1{}
}
like this, to load Feature1.qml