QML FontLoader not working - c++

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"
}

Related

How to implement Palette type for different platforms in QML

So at the moment I have 3 Qml types:
ThemeToUse.qml
QtObject {
property Palette palette: Palette {}
Component.onCompleted: {
if (themeContext) {
palette = themeContext.palette
}
}
}
Palette.qml
QtObject {
property color WindowsColor: "red"
}
BaseTheme.qml
QtObject {
property Palette palette: BaseTheme.Palette {}
}
BaseTheme.Palette.qml
Palette {
WindowsColor: "green" // can reimplement existing colors
property color NewColor: "black" // and add some more colors
}
And I have Theme.qml for an android app:
BaseTheme {
palette: BaseTheme.Palette {
NewColor: "blue"
readonly property color androidColor: "#010101"
}
}
Also I make the ThemeToUse in the main:
QQmlComponent qmlComponent(&engine, "path to AndroidTheme");
auto createdComponent = qmlComponent.create(context);
context->setContextProperty("themeContext", createdComponent);
And somewhere I heard that this is not the best way to make a Palette which will contain only the needed colors for different platforms.
So the question is: are there any methods to make different Palettes for different platforms, so that I can access their properties in this form: Palette.SomeColor (not the WinPalette.SomeColor and AndroidPalette.SomeColor)
Btw. I'm sorry for my english
I recommend using a QQmlFileSelector. It will allow you to add different versions of any qml file, based on platform or any other selector string that you choose.
Let's say I have a file called MyTheme.qml. And let's say, I want that file to look different if I run on Windows vs Android. My directory structure would look something like this:
qml/
MyTheme.qml
+android/
MyTheme.qml
In this case, the Windows version will be the default because I'm not specifying a special path for it. But if Qt detects that I'm running on the Android platform, whenever I request a qml file, Qt will look for it first in the +android path.
If you are using a QQmlApplicationEngine, then you don't even need to make any changes in C++. The QQmlFileSelector is already created for you.

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.

QML C++ Goodbye World

I created an Hello World app in QML. Now I want to learn how to modify the text from "Hello World" to "Goodbye World" from C++.
The qml looks like so:
import QtQuick 2.6
Rectangle {
property alias mouseArea: mouseArea
width: 360
height: 360
MouseArea {
id: mouseArea
anchors.fill: parent
}
Text {
id: helloText
anchors.centerIn: parent
text: "Hello World"
}
}
I've attempted to follow the
https://wiki.qt.io/Introduction_to_Qt_Quick#Integration_with_C.2B.2B_applications
But no luck. The code seems incomplete. For example, it leaves off information with ellipses like so:
QDeclarativeContext *context = …;
And I can't find the header for the QDeclarativeContext even if it didn't. I suspect the documentation is old, but I'm not sure.
Anyway, I just want to see a simple example that lets me change the text from "Hello World" to "Goodbye World" from inside a C++ program.
That code is for the old QtQuick1 API, which was based on QGraphicsScene and is now outdated, obsolete and IIRC removed from Qt.
I would recommend against mingling with QML from C++, I'd even go further and call it an anti-pattern, in 99.9999% of the cases there is a better solution. You should keep the interaction between C++ and QML to a well defined API.
That being said, it is still possible to find objects and manipulate their properties. You can use QQmlApplicationEngine::rootObjects() to get access to the root objects, from there you can findChild() any object you have provided a objectName on the QML side, you can use QMetaObject:invokeMethod() (works for QML functions too!), use qobject_cast, set properties and whatnot.
All those techniques are covered in the documentation.

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.

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