How to connect QML signals to C++ slots?

Hi,

I’m new to developing apps for SailfishOS, but I have developed an app in Qt/QML for desktop before.
I cannot figure out how to connect QML signals with C++ slots.
On desktop Qt, I had this code (for example) to achieve that:

QmlApplicationEngine engine;
    qmlRegisterType<BackupManager>("at.nis.ocb", 1, 0, "BackupManager");

    BackupManager manager;
    QQmlContext* context = engine.rootContext();
    context->setContextProperty("backupManager", &manager);

    QObject* item = engine.rootObjects().value(0);
    QObject::connect(item, SIGNAL(startBackup()), &manager, SLOT(startBackup()));

However, when I try something similar for Sailfish, I get a compilation error:

grafik

Can anyone explain to me please, what’s the correct way of doing this on Sailfish?

The Sailfish Code I tried
int main(int argc, char *argv[])
{
QScopedPointer<QGuiApplication> app(SailfishApp::application(argc, argv));
    QScopedPointer<QQuickView> v(SailfishApp::createView());

    // If you wish to publish your app on the Jolla harbour, it is recommended
    // that you prefix your internal namespaces with "harbour.".
    //
    // For details see:
    // https://harbour.jolla.com/faq#1.5.0
    qmlRegisterType<TaskListModel>("harbour.at.nis", 1, 0, "TaskListModel");
//    qmlRegisterType<TasksManager>("harbour.at.nis", 1, 0, "TasksManager");

    QQmlApplicationEngine engine;

    TasksManager manager;
    QQmlContext* context = engine.rootContext();
    context->setContextProperty("tasksManager", &manager);

    QObject* item = engine.rootObjects().value(0);
    QObject::connect(item, SIGNAL(setActiveTaskList(QString)), &manager, SLOT(setActiveTaskList(QString)));


    // Start the application.
    v->setSource(SailfishApp::pathTo("qml/harbour-silicatasks.qml"));
    v->show();
    return app->exec();
}

Disclaimer, i have very limited Qt experience.
But can’t you just, in QML:

onMyQmlSignal: {
  idOfMyCppThing.aSlot()
}

Your compiler is complaining that the taskmanager is already deleted, because you have to create it with “new” otherwise it will be deleted

Your code should work. Check if you need to use qmlRegisterType<TaskManager>(…., it’s commented out in your example? If TaskManager inherits from QObject? Did you use ancient Qt on desktop as well? Maybe something was changed after 5.6, the version used by Sailfish?

@x2s no, the compiler does not complain about object live time. And it’s ok here to pass a pointer to a local variable as nothing will live longer than main

The compiler complains about missing type information in QVariant, a class that can store anything but needs to know a bit about what it stores

3 Likes

To understand the compiler error message you have to think like the compiler :wink:

QQmlContext::setContextProperty() can take either pointer to QObject or a reference to QVariant. Maybe you were trying to give it the former, but perhaps TasksManager does not inherit QObject? The compiler thought that perhaps you were aiming for the second option, and tries to convert TasksManager pointer to QVariant by first converting it to void pointer and then giving it to QVariant(void *) constructor - which has been deleted. Deleted in this context has nothing to do with memory management, it really means that you are not supposed to call that constructor.

All in all, this rather misleading error message is trying to tell you that you TasksManager should inherit QObject which it does not.

6 Likes

Thank you all for your help!In my hurry, I must have forgotten to derive from QObject With that being fixed, I still cannot get it to work. I still have the code from above (now with TasksManager derived from QObject) and in QML I have this:

Page
{
   id: mainPage
   signal setActiveTaskList(string listName)

   /*
   some other stuff
   */

MainPageButton
{
    text: name
    onClicked: {
         tasksManager.setActiveTaskList(name)
         var listPage = pageStack.animatorPush(Qt.resolvedUrl("ListPage.qml"), {name: name})
     }
}
}

Now I can compile, but when running the app, I get an error in the console

[W] unknown:0 - QObject::connect: Cannot connect (null)::setActiveTaskList(QString) to QObject::setActiveTaskList(QString)

This is now exactly like I did it on my desktop app, so I’m really confused, what’s wrong here

Is your project available somewhere, e.g. github? It’s quite difficult to try to guess the source of the issue when you have only posted small pieces of the code.

It’s probably this project NIS / SilicaTasks · GitLab

1 Like

Probably. The repository does not seem to have the above code snippets though.

I would recommend you to switch to “modern”-style connect and use

QObject::connect(item, &TaskItem::setActiveTaskList, &manager, &TaskManager::setActiveTaskList);

This way you get compile time checks and errors instead of run-time. It’s a bit faster as well, as the old-style use a string lookup table

1 Like

Wow, how did you find it? This is actually correct :smile:

It’s on another branch. On 4-create-listmodel-logic-for-task-lists

I will try that, thanks!

  1. https://forum.sailfishos.org/u/nis/summary
  2. NIS · GitLab
  3. NIS · GitLab
2 Likes

Thanks for everyone who tried to help here!

I’ve now solved the issue by using another method to connect the signal.

Thanks again for your help!