Getting C++ Signals From QML



  • Hello everyone,

    I have a problem with binding c++ signals to QML

    I've registred my c++ class Like this

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include<QQmlContext>
    #include<QtQml>
    #include"productback.h"
    #include"database.h"
    #include"transactions.h"
    #include"solditems.h"
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
    
    
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
    
    
    
    
    qmlRegisterType<ProductBack>("co.pro.back",1,0,"ProBack");
    qmlRegisterSingletonType<Transactions>("co.trans.back",1,0,"TransList",Transactions::soldListSingleton);
     engine.rootContext()->setContextProperty("TransaListao",&Transactions::instance());
    
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    
    }
    

    My class That i want to Send Signals from to Qml is <Transactions> and ive registred using Singleton type Registration (Hope thats not The problem )

    Although ive registred it again With Contex Properties , But in both ways i cannot Access Signals from Qml Using

    import co.trans.back 1.0 as HistoryList
    //my Codes
    
    connections {
    target:HistoryList
    
    ontransIndexSet: console.log("connected")
    }
    
    //
    

    this is My Transactions Header

    /#ifndef TRANSACTIONS_H
    #define TRANSACTIONS_H
    
    #include<QObject>
    #include"transaction.h"
    #include"pos-core_global.h"
    #include<QVariant>
    #include<QVector>
    #include<QString>
    class DataBase;
    class QQmlEngine;
    class QJSEngine;
    #include"pos-core_global.h"
    
    class Transactions: public QObject
    {
    
    Q_OBJECT
    
    
    
    
    
    public:
    
    
        Transactions(QObject *parent=nullptr);
    
        static Transactions &instance();
    
    void setHistoryList(QVector<Transaction*> tList);
    
    QVector<Transaction*> getHistoryList() const;
    
    Q_INVOKABLE QVariant getTransData(int role,int index) const;
    Q_INVOKABLE bool transForDate(int choice,QVariant Date);
    
    Q_INVOKABLE int transSize();
    
    Q_INVOKABLE void clear();
    
    Q_INVOKABLE void setProList(const int index);
    
    Q_INVOKABLE void setCurrentTransIndex(unsigned int index);
    
    Q_INVOKABLE unsigned int getCurrentTransIndex();
    
    //----------------------------------------------------------
    
    //Sold List Functions:
    
    Q_INVOKABLE QVariant getProData(int transIndex,int soldListIndex,int role) const;
    
    Q_INVOKABLE int getSoldListSize(int transIndex) const ;
    
    
    
    
    //Singleton Return for Qml Use
    static QObject *soldListSingleton(QQmlEngine *engine, QJSEngine *scriptEngine)
    {
        Q_UNUSED(engine)
        Q_UNUSED(scriptEngine)
    
        Transactions *example=new Transactions();
        return example;
    }
    
    
    public :
    signals:
    
    void transIndexSet();
    
    
    
    private:
    DataBase &mDatabase;
    QVector<Transaction*> mTransactions;
    QVector<Transaction*> mHistoryList;
    QVector<Products*> soldList;
    unsigned int currentTransIndex;
    
    };
    
    #endif // TRANSACTIONS_H
    

    It will be very kind from you if you help me with this , ive worked on solving this for 3 day's

    Thanks Again !


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You are doing the camel casing wrong for your slot name, it should be onTransIndexSet.

    See the Exposing Signals part in the Exposing Attributes of C++ Types to QML chapter of Qt's documentation.



  • Hello , Thanks For your Replay !

    Ive done your provided solution Like this

    import QtQuick 2.0
    import QtQuick.Window 2.3
    import co.trans.back 1.0 as HistoryList
    
    
    
    Window
    {
    
    
    
        height: 700
    
        width: 700
        maximumHeight: 700
        maximumWidth: 700
    
    
    
    
    
    Connections{
    
        target: HistoryList.TransList
        onTransaIndexSet:console.log("connected")
    }
    

    but Still Nothig Happens when my Transaction class (that i registred as singleton in Qml) emits transIndexSet()

    i dont know what is the problem !



  • @Havaloow Try adding debug messages to your code, to try to isolate the problem, to make sure that the singleton is firing the event, I have had issues with them not firing, and found that was the whole issue and stopped using them, I never did figure out this issue, but found that by not using them, my code worked much better and faster, try without the singleton and see if that is the issue, before trying to figure out what is, meaning if the code works without it, then you know what the issue is, and you do not show your code to make the singleton, normally the qmldir, and what not, it is tricky to set one up correctly, I found in theory, they may sound like a good idea, in use they are a bad idea, but that does not really help to solve your issue, by telling you about my failures, trying to use them, but I had this issue and did a lot of research to solve it, and that was the solution, I ended up writing JavaScript to make var globalVariables, and made getters and setters for them, this is by far a better solution to Global Variables, but they must be initialed, so I use LocalStorage for storing state changes, and just repopulate my queries on demand, when calling from other pages, trust me, lost a week trying to make singletons work the way I wanted, and that was not the way they were intended, in fact, they do have their use, but for what you want to accomplish, why add use them, when you can use LocalStorage as state change storage, and JavaScript to save the values, so you only have to init once, so it is faster, keep in mind you can use singletons to store properties, and mainly so you can access them between pages, you can also just export those properties, and make them globally available, without the need for a singleton, in my option, they are good for what they were designed for, but in practice, they can cause issues with Transactions, all due to how they fire events and when they fire them, I keep getting blank or uninitialized variable errors, on one call, and the last results from the next call to the same variable, and if you figure out how to fix this, you have more of a reason to structure your program using them then I did, at first coming from other languages that worked well with them, Qml is not really big on the concept, sure it is there, but is it really recommended is what you need to ask yourself, what is the best way to do this, I do not know, this is just my conclusion after spending a week trying to make it work for Sqlite and LocalStorage, so in short, I think your issue is with the singleton, but I could be wrong, just because that was my problem does not mean it is yours, and I do have examples where they work very well for other things, like properties for controls, and that is what they were designed for, so they work well, but not for any database work, they are just not designed to handle transactions, in my case, the code was causing multiple updates on the same variable, and Qml only fired on the first one, ignoring the second one, maybe not ignoring it, but it got eaten by the Qml JavaScript cue or the control model, but it was clear that it did not fire on the last of the those updates, and left the variable in the wrong state, this is a race condition, and why I found it unacceptable to use them for this purpose, and they were not designed to, so it is not a bug, just a limitation, and there might be a fix for it, maybe manually force a trigger after the transaction, and only update a local variable in the Transaction, and do not update the global variable until the end of the Transaction, so you are not firing events every time you update a variable that is stored in a singleton, which is what causes this race to beging with.



  • Guys , Thanks for your replays .. ive figured out the problem and i will share with anyone who have a same problem

    First , As Mr.SGaist said ive updated my qml slot call and made the fisrt letter capital Like this

    Window
    {
    
    
    
        height: 700
    
        width: 700
        maximumHeight: 700
        maximumWidth: 700
    
    
    
    
    
    Connections{
    
        target: HistoryList.TransList
        onTransaIndexSet:console.log("connected")
    }
    

    When Ive made it like that the error " Reference error cannot assign to non-existent property " when calling the signal from my Registred Singleton c++ class was gone but however , the signal never got worked in QML and nothing happend when my class triggered that Signal.

    ive figured out that the problem was not importing my class in Main.qml where it contained all of my other qml pages in StackView , and Secondly , in all other qml files when i wanted to use that class ive imported Like this

    import co.trans.back 1.0 as HistoryList
    

    when i removed the " as HistoryList" in all of my qml files that uses Transaction Class , the signal worked and it became GLOBAL to all of my qml files and signals from my class worked , i think the problem is when you import your class "as SomeThing" it creates a new instance from that class and it have its own state and become local to the file ( i dont know if im right about this but in my case it worked when i removed that )

    So again Thanks for your support and Replay ! hope my solution help others with this issue.



  • @Flesh Thanks for your replay ,

    ive had that idea of saving the values in a global Javascript Variables , and ive worked alot on it .

    but i was never able to make that Var Global to all of my QML files , and In my project the case of using singleton class for transactions is a MUST because there are some states that has to be global to all of my Transaction Related QML files and needs some serious c++ processing in the backend , i know it maybe simpler and somehow Faster using javascript variables , but i think it only applies to some simple values that dont need alot of processing .

    Thanks again for your kind replay and guidance !



  • I recently had the same issue importing another JavaScript, naming it the same as a function inside, it worked with limited scope, but could not find any children do so the scope of the import, while at the same time, I have JavaScript's that runs that way fine, from what I can tell, they have to be written that way, the as operator changes the scope of that file to local by design, for example, you can use two libraries that would normally step on each other, like 1.0 and 2.0 of the same library, using as on one, will allow both to run at the same time with no conflicts, and it is due to how it scopes it. I found that in C++ I can name it the same as the class and it works fine, not so much with JavaScript, not that they have the class concept anyway, but with Prototypes, it has issues, mainly finding children, so I do believe you are right about the global issue, and that could have been my problem as well, since I also did an as, so maybe one day I will try this to see if that is what was causing my issue as well, so thanks for figuring this out, I never thought about the as that way, this was the same issue I faced, but I found that exporting properties and using LocalStorage to save state changes, just worked, not sure about better, from what I see, as a Global Singleton has to fire update notification, and in my case, that was more overhead than required, for example, if I use a property, the update will trigger a signal same as a Singleton, so I looked at all the pages that actually required real time updates, and used exported properties to update them, I used JavaScript calls to update variables on the fly, and LocalStorage to save State Changes, this works well to save the position and size of the window, and other changes you want to track at shut down, so you can use them at startup. Glad you got it working, I gave up, but do not miss that way, it is not the Qml way of doing things, but it does work when it works.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.