Include another QML file from a QML file - c++

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

Related

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.

How can I improve memory management on Qt Text fields for big texts?

I am trying to build a log analysis tool using C++/QML using a MVC architecture. I'd like to keep the Model part in the C++ world, uncoupled to the Qt/QML world.
I wrote a simple app to open a file, read it in my Model and feed back the contents to the Controller, which feeds them to a TextArea as its "text". Just by opening a 5MB text file, as soon as the TextArea is starting to get populated, the application comes up to 3.3GB in memory usage and becomes unusable.
C++/Qt:
//log->getContent returns a std::string
logDisplayView->setProperty("text", QVariant(log->getContent().c_str()));
Qml:
import QtQuick.Controls 1.4
TextArea {
id: logDisplayView
objectName: "logDisplayView"
readOnly: true;
}
I'd like to make better use of the memory, but after days of research I haven't found any way of doing so. Is there any way to get Qt to let me manage the contents of the text field? Maybe in a similar way as how iOS/Swift's TableViewController utilizes your controller to load data as you scroll up and down.
Edit:
Printing the size of the log content comes out to 5MB:
qDebug() << "File length: " << strlen(log->getContent().c_str());
File length: 5031690
Edit2
logDisplayView is a *QObject, found with the following code:
if (engine.rootObjects().length() > 0) {
QObject *mainSplitView = engine.rootObjects()[0]->findChild<QObject*>("mainSplitView");
if (mainSplitView) {
QObject *logDisplayView = mainSplitView->findChild<QObject*>("logDisplayView");
if (logDisplayView) {
fileHandler->setLogDisplayView(logDisplayView);
}
}
}
Where fileHandler is a class I created in which the C++/Qt code in the original question resides.

External binary resource opened but not existing in QML

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");

Force signal/slot hookup of not-yet-visible QML item on startup

In Qt's QML language for writing GUI code, QML elements are (if I understand correctly) not actually created until they become visible. (EDIT: It sounds like the elements are created when the QML engine loads them, but it appears that signal/slot connections are not made unless the elements are visible.)
I have some QML elements (LineSeries objects from QtCharts) that record some data over time, and I would like them to start recording data as soon as my app starts up, even though the ChartView elements containing each series aren't immediately visible (users must navigate to a page containing these elements).
Is this possible?
One approach might be to keep each data series itself in some kind of QVariantList containing QPointFs in a global QML object, then dynamically assign it to the desired LineSeries object when the parent ChartView is instantiated. This might be possible using ChartView::createSeries, though I believe the only way to populate the new series would be to call ChartView::series() and pass that to some kind of Q_INVOKABLE method in my C++ backend that would populate the series.
EDIT: Not sure if this is relevant, but the GUI element I'm using for navigation (i.e. the reason the ChartView isn't visible on startup) is a TabView. The ChartView objects are not top-level TabView pages; they're a couple levels down.
EDIT 2: The answer below seems like it should work, but I get the rather unhelpful error TypeError: Type error when I try to implement it. I've put together a minimal (non-)working example here (use qmlscene to run it; I am using Qt 5.5).
EDIT 3: The above non-working version was fixed by simply adding a property alias, thanks to Mitch's answer and comments.
Items in Qt Quick are created as soon as the QML they reside in is loaded by the QML engine (even if they default to non-visible). The exception to this rule are items that dynamically load their content, like Loader.
In your edit, you said that you're using TabView. I think that the setup you have is something like this:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
ApplicationWindow {
title: qsTr("Hello World")
width: 800
height: 700
visible: true
TabView {
id: sv
anchors.fill: parent
Tab {
title: "Page 1"
}
Tab {
title: "Page 2"
ChartView {}
}
}
}
Try changing it to something like this:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
ApplicationWindow {
title: qsTr("Hello World")
width: 800
height: 700
visible: true
ChartView {
id: chartView
color: "red"
anchors.fill: parent
visible: false
Component.onCompleted: print("Monitoring data...")
}
TabView {
id: sv
anchors.fill: parent
Tab {
id: tab1
title: "Page 1"
}
Tab {
id: tab2
title: "Page 2"
onActiveChanged: {
if (active) {
chartView.parent = tab2;
chartView.visible = true;
}
}
}
}
}
Tabs are basically just Loaders, so they have all of Loader's API, including the active property. This property lets you know when that tab has been loaded. Before that stage, it's inaccessible.
When the tab becomes active, we reparent the ChartView to it so that it's displayed in the correct place, and then show it.
As you can see from the debug output, the ChartView is created at startup. You can move it somewhere else to control when it's loaded, or... use a Loader. :)
EDIT: Based on this answer and the discussion in the comments, OP has created a small working example that demonstrates how persistent items can be pre-loaded and re-parented.

QML FontLoader not working

I'm trying to use QML's built-in FontLoader element to load a custom font for our application without success.
I've tried using both OTF and TTF fonts with identical results.
The fonts are in the same directory as the project files. There is only one QML, the main one where this FontLoader lives.
This is what it should look like:
Here is my code:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
FontLoader {
id: cFontLoader
source: "./fontlol.ttf"
}
Text {
id: testText
anchors.centerIn: parent
text: "Hello Fonts!"
font.family: cFontLoader.name
font.pointSize: 32
}
}
I've had headaches with Qt/QML's font handling. Fonts with "various sub styles" seem to be the fundamental problem. When I absolutely needed to get at one particular problem font style in Qt, creating a custom version with fontforge of the font where the wanted style was renamed "normal" seemed to work.
I've also expirienced that problem, but in mine case it was because I've added the "name" property. When I deleted name font start showing.
The text component of QML recognizes fonts by their font name. However, if you load different font types, usually the font name within the metadata of the font is the same.
The text component has a property font.styleName, which you can use to access different types of a font:
FontLoader{id: loader source: "AwesomeFont-Bold.ttf"}
Text
{
font.family: loader.name
font.styleName: "Bold"
}