External binary resource opened but not existing in QML - c++

I have the following main.qml file:
import QtQuick 2.5
import QtQuick.Controls 1.4
Item
{
anchors.centerIn: parent
Label
{
id: textLabel
anchors.fill: parent
x: 200
y: 400
}
CustomObject
{
id: customObjectId
}
}
CustomObject is a QML file defined in an external binary resource, generated by the rcc command:
rcc -binary -o redTheme.qrc redTheme.rcc
CustomObject.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
Item
{
Rectangle
{
width: 200
height: 120
color: "blue"
Label
{
text: "customObject"
}
}
}
In the C++ side, I register my resource like this:
QResource::registerResource(QCoreApplication::applicationDirPath() + "/data/themes/redTheme.rcc");
The function returns true, which means the file is opened.
Yet, CustomObject does not exist in my main.qml file. Why?
CustomObject is not a type
EDIT: I've wrapped CustomObject into a QML Module and then compiled it into a .rcc file (it means the qmldir file is inside the .qrc). No difference whatsoever, CustomObject still isn't recognized as a type, even if I add an import statement (import redTheme 1.0). Content of my qmldir file:
module redTheme
CustomObject 1.0 CustomObject.qml

I am not 100% sure, but I think QML files as types only works for "internal" QML files, that is the QML files that are in the internal resource file.
In order for external QML files to work as types, you need to have a valid QML module defined, with its qmldir file and such. It may also be possible to expose it as a type using the C++ API, but I haven't investigated it, basically, it is what the qmldir file parser does.
The other way to use external QML files is as path/url, that is, if you want it instantiated, you either need to use a Loader or manually instantiate it dynamically.
This might help to register external QML files as QML types:
int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
This function registers a type in the QML system with the name
qmlName, in the library imported from uri having the version number
composed from versionMajor and versionMinor. The type is defined by
the QML file located at url. The url must be an absolute URL, i.e.
url.isRelative() == false.
Normally QML files can be loaded as types directly from other QML
files, or using a qmldir file. This function allows registration of
files to types from C++ code, such as when the type mapping needs to
be procedurally determined at startup.

I've encountered a similar effect with external rcc files. Try adding 'qrc' scheme to your url when loading qml files from external resources:
QQmlApplicationEngine engine("qrc:/main.qml");

Related

Add a QTreeView in QML

I would like to register a QTreeView c++ object to QML.
I tried to register it like this:
main.cpp:
qmlRegisterType<QTreeView>("com.MyApp.QTreeView", 1, 0, "QTreeView");
relevant code in main.qml
import com.MyApp.QTreeView 1.0
QWindow {
QTreeView{
headerHidden: true
}
}
Result: it compiles. headerHidden property is found so it is registered correctly. However I have an error at runtime:
ASSERT: "!d->isWidget" in file kernel\qobject.cpp, line 2090
QWidgets are not directly compatible with QML such that they can be embedded in a QML view. They are two different UI technologies and cannot be used together in that fashion.
You can however embed a QML view inside of a QWidget hierarchy:
https://www.ics.com/blog/combining-qt-widgets-and-qml-qwidgetcreatewindowcontainer
Or just use the QML TreeView component instead:
https://doc.qt.io/qttreeview/qml-treeview.html

How to access QML Surface3DSeries from 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.

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.

Read property from QML singleton with C++

Is it possible to access/read the properties of a QML singleton inside your C++ code?
For example if my QML singleton looks like this:
pragma Singleton
import QtQuick 2.5
QtObject {
property int myProperty: 5
}
How can I access myProperty from C++ code. I need this as I do not want to have my "magic" numbers both in QML and C++ and it is only very rarely required in C++.
For normal QQuickItem's it was always easy. Just get access to the QuickItem (by dynamic creating it or with findChild()) and than call quickItem->property("myProperty").toInt()
But with the singleton I can't see how to get access to it.
Although not directly, one way to access a QML singleton is via a function in a non-singleton QML object, that you can access in the usual way:
Constants.qml
pragma Singleton
import QtQuick 2.5
QtObject {
objectName: "Constants"
property double phi: 1.6180339887498948482
}
main.qml (e.g.)
import QtQuick 2.5
import "."
function getPhi()
{
return Constants.phi;
}
C++
//...
// Create the engine and load QML
//...
QObject* rootObject = engine->rootObjects().constFirst();
QVariant phi;
QMetaObject::invokeMethod(rootObject, "getPhi", Q_RETURN_ARG(QVariant, phi));
qDebug() << phi.toFloat();
Don't forget you'll need a qmldir file to access the singleton in QML:
qmldir
singleton Constants Constants.qml

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