Signal-Slot connection after destruction of qml objects? - c++

What happens with the connection of signals from c++ objects to qml objects slots after destruction of qml objects?
Item {
function qmlFunction() {
cppObject.cppObjectFunction()
}
Component.onCompleted: {
cppObject.someSignal.connect(qmlFunction);
}
Component.onDestruction: {
cppObject.someSignal.disconnect(qmlFunction);
}
}
Before I wrote Component.onDestruction with disconnect() the program displayed an error message:
qrc:/qml/xxxxxxxx.qml:77: TypeError: Cannot call method 'cppObjectFunction' of undefined
Is the disconnection of signals and slots not performed automatically?
Object cppObject "always" exist and passed to qml this way:
main.cpp
QQmlApplicationEngine engine;
Model* model = new Model::instance(&engine);
engine.setObjectOwnership(model, QQmlEngine::CppOwnership);
engine.rootContext()->setContextProperty("cppObject", model);
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
QML item is loaded by the Loader and can be reloaded several times during the program. Naturally, the error occurs after the QML item has been reloaded and cppObject triggers a signal someSignal.
on windows: Qt 5.6.2 <- my program log error to debug console
on linux: Qt 5.9.2 <- my program crashes
main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
File with error
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.3

Easy solution is to :
istantiate the cppObject directly from QML to ensure that both objects are properly cleaned up by adding to your main.cpp file before you load the QML file
qmlRegisterType<Model>("com.your.app", 1, 0, "CppModel");
and then in your QML file include:
import com.your.app 1.0
....
CppModel {
id: cppObject
onSomeSignal: { cppObject.cppObjectFunction(); }
}
This way will make the extra connections and the extra qmlFunction() both unnecessary.
and maybe this too:
If cppObjectFunction() is a public slot from cppObject that is defined on the C++ side, you can try adding Q_INVOKABLE to the front of it in order to add it to the metaobject system.
Q_INVOKABLE void cppObjectFunction() { /* your code */ }

Related

Tumbler in C++/Qt

I'm trying to implement a tumbler to set the time for an alarm in C++. Yet I've only seen tumblers in Qt quick and therefore coded in QML. Now I've tried to get QML code in my C++ code by doing:
void SmartAlarm::showTumbler(){
// Create the QML view
QQuickView* quickView = new QQuickView(QUrl(":/files/includes/AlarmTumbler.qml"));
// Make the QML view resize when the parent is resized
quickView->setResizeMode(QQuickView::SizeRootObjectToView);
QWidget* quickWidget = QWidget::createWindowContainer(quickView);
rightLayout->addWidget(quickWidget);
}
My QML file looks like this:
import QtQuick 2.12
import QtQuick.Window 2.2
import QtQuick.Controls 2.12
import QtQuick.Extras 1.4
TumblerColumn{
id: weekdayTumbler
model: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
}
TumblerColumn {
id: hourTumbler
model: 24
}
TumblerColumn {
id: minuteTumbler
model: 60
}
All I get is a blank, white widget in my layout. What am I doing wrong? Is there a way to implement a tumbler in Qt without using QMLs?
I think it doesn't find the tumbler-file. You can check this the easiest by starting the program and check in the Application Output (Bottom Menu in QTCreator) for following message:
":/files/includes/AlarmTumbler.qml: No such file or directory"
If you can't find it, it might be because you use a Shadow Build and the actual execution-files are in a different folder than the QML Files. To solve this, you can go to "Projects" and deactivate "Shadow Build", rebuild and you should see the tumbler.
The implementation itself should work fine. I tested it locally, added everything to the MainWindow though cause I don't know where your "rightLayout" comes from.
ui->setupUi(this);
// Create the QML view
QQuickView* quickView = new QQuickView(QUrl("tumbler.qml"));
// Make the QML view resize when the parent is resized
quickView->setResizeMode(QQuickView::SizeRootObjectToView);
QWidget* quickWidget = QWidget::createWindowContainer(quickView);
this->ui->rightLayout->addWidget(quickWidget);

Mapbox is not loading on raspberry

I'm trying to load a mapbox in my c++/qt application. On my PC the application is working fine, and the map is loaded. However, when the
application is deployed on the raspberry(v3) the application is running but the map is not loaded(a blank screen appears).
I've tried a different map plugins such as esri and they work fine
on the raspberry. But the mapbox plugin wont work
Here is the code I'm working on
import QtQuick 2.0
import QtQuick.Window 2.0
import QtLocation 5.6
import QtPositioning 5.6
Window {
width: 512
height: 512
visible: true
Plugin {
id: mapPlugin
name: "mapboxgl" // "mapboxgl", "esri", ...
// specify plugin parameters if necessary
// PluginParameter {
// name:
// value:
// }
}
Map {
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(41.38, 2.16) // Oslo
zoomLevel: 14
}
}
And the C++ code
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
I'm expecting a mapbox type map to be loaded but only a blank screen appears. There are no any error messages.
I think it's because a plugin is missing or something. But then why it is working on my laptop since I have the same version of QT in both machines: (QT 5.10)

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

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

Extending QML ApplicationWindow in C++

How can you properly extend the QML ApplicationWindow type? According to the documentation, ApplicationWindow instantiates a QQuickWindow. So I tried sub classing from QQuickWindow and exposing the type to QML as MyWindow. The problem is that MyWindow doesn't actually extend the QML type ApplicationWindow, so you don't get all the properties like menuBar and toolBar. How can I extend ApplicationWindow in C++ and expose it to QML? Here is what I'm currently doing:
class MyQuickWindow : public QQuickWindow
{
//...irrelevant additions
}
int main()
{
QGuiApplication app(argc, argv);
qmlRegisterType<MyQuickWindow>("MyExtensions", 1, 0, "MyApplicationWindow");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
Here is the QML file:
import QtQuick 2.0
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import MyExtensions 1.0
MyApplicationWindow {
width: 800
height: 600
visible: true
menuBar: MenuBar { // ERROR: menuBar is not a property
Menu {
title: "File"
MenuItem { text: "New" }
MenuItem { text: "Open" }
}
}
}
Note that I need to have my own additions in C++ to the QQuickWindow for other reasons.
It seems like the job for qmlRegisterType. And it is a bit hard to say if you miss something with your C++ part but registering the type should help. That is for exposing the type itself and should enable the derived QQuickWindow functionality (derived from ApplicationWindow actually). But for what you are adding you need to deal with Q_PROPERTY and Q_INVOKABLE (which is for functions) mechanism. See the whole bunch of Q_* QObject macro.
And if that was not enough then there is an example for such inheritance.
Correction: the author is dealing with QML-made type but he can still try to mimic the type on his own. The path to ApplicationWindow.qml source code is: C:\Qt\5.3\Src\qtquickcontrols\src\controls where C:\Qt\5.3\ is the root for selected Qt version.I would attempt that and that is feasible unless we want to find out about explicit QML inheritance. That file can also be found at Qt source code repository.