Unsolved C++ Signals to QML
-
I've been trying to make a QML module receive a signal from a C++ class but have not been able to make it work. I did create a small test program based on a course and got it to work but when I duplicate that in my project it does not work. I get no errors but the signal never appears to be caught since it's not logged to the console. I have tried using qmlRegisterType and creating an instance of the class as well as setting the context property in the root and using connections. While there is a lot of posts and articles many of them are for older versions of Qt. I've seen posts that use the connect
I get no errors in building, the project runs and I get the debug output but the signal never appears to be received. To help troubleshoot I also setup a timer that was triggered when the object was instantiated. And that produced an interesting result.
The timer signal that was emitted was caught by both the qmlRegisterType and the setContextProperty code in QML - the timer increments every second and the values were displayed. The timer code is below.
So why doesn't the signal emitted in the dbimportdata function get seen. It's emitted. It shouldn't make a difference but the dbimportdata function is called from another C++ class and returns the result back to that class.
Thank you for any help.
What I did.Timer code
DataBase::DataBase(QObject *parent) : QObject{parent}, mTimer(new QTimer(this)), mValue(0) { connect(mTimer, &QTimer::timeout, [=](){ ++mValue; emit cppTimer(QString::number(mValue)); }); mTimer->start(1000); }
- Create a signal in my header, database.h file
void cppTimer(QString value)
signals: void phaseChanged(QString thephase); void cppTimer(QString value);
- In database.cpp I have a function that is called from another C++ class and in the database.cpp function I emit the signal.
int DataBase::dbimportdata(QList<QString> header, QList<QString> data) { // Set the phase to import. m_thephase = "Importing"; qDebug() << "Emitting. " << m_thephase; emit phaseChanged(m_thephase); // Cut much code out. }
I get the qDebug output but the signal is never caught by the main.qml.
- In main.cpp I have included the database.h header and have tried both qmlRegisterType and setContextProperty.
#include "Database/database.h" DataBase Dbase; // My class is Database engine.rootContext()->setContextProperty("DBaseSignals", &DBase); qmlRegisterType<DataBase>("DataBaseSignal", 1, 0, "DBaseSignal");
- In main.QML I have tried Connections and creating a instance of the class.
import DataBaseSignal // For the qmlRegisterType // For the qmlRegisterType I use this. DBaseSignal { id: mainDBaseSignal onCppTimer: { console.log("Object caught the signal." + value) } } // For the setContextProperyt Connections { target: DBaseSignals onCppTimer : { console.log("I got the value " + thephase) } }
-
I think you're missing a argument in the connect function ? Maybe this will work ?
connect(mTimer, &QTimer::timeout, this, [=](){ ++mValue; emit cppTimer(QString::number(mValue)); });
-
@johngod said in C++ Signals to QML:
I think you're missing a argument in the connect function ? Maybe this will work ?
connect(mTimer, &QTimer::timeout, this, [=](){ ++mValue; emit cppTimer(QString::number(mValue)); });
Thank you. The timer function does work and sends the signal which is picked up (I copied the example from a QML online course). What does not work is my emit phaseChanged(m_thephase). If I put this emit statement in the constructor the signal is picked up by QML.
As I understand it if I have the emit blahblah(somearg) in my dbimportdata function it will be called and emit a signal. I assume it's being called and emitting a signal but QML does not pick it up. In main.qml I have a connection that has an onPhaseChanged that should print the phase.
-
I've done a LOT more testing on this and something just isn't adding up.
In my C++ class function importdata I defined the signal. and a function to call it. As soon as I enter the importdata function. I call
None of this works.QString DataProcess::importdata(QString sim, const QUrl &urlpath) { sendPhaseChange("Importing"); .... }
dataprocess.h
signals: void phaseChanged(QString thephase);
In dataprocess.cpp
void DataProcess::sendPhaseChange(QString thephase) { qDebug() << "Sending change." << thephase << ", " << m_thephase; if (thephase.toLower() != m_thephase.toLower()) { m_thephase = thephase; qDebug() << "Will send signal"; emit phaseChanged(m_thephase); } else { qDebug() << "No change"; emit phaseChanged("No Change"); } } // End function sendPhaseChange
When I select the menu item in QML that requests the import the sendPhaseChange is called, drops into the first part of the if, qDeug() triggers that it is sending the signal. However, QML sees nothing with this connection.
Connections { target: DProcess onPhaseChanged: function onPhaseChanged(thephase) { console.log("Connection has the phase " + thephase) } }
In main.cpp I have
DataProcess DProcessSignals; engine.rootContext()->setContextProperty("DProcess", &DProcessSignals);
However, if I add a slot in the C++ header and a button in QML that triggers that slot when clicked things work. QML sends the signal and it's received by the C++ slot which then emits the phaseChanged signal which is picked up by QML.
I shouldn't have to fire a signal from QML to my C++ class to get things working. That kind of defeats the purpose of signals and slots. I know I must be doing something wrong but just can't find it so any help is appreciated.
-
Are you sure you don't have multiple DataProcess instances?
Are you sure you don't emit the signal before the QML is loaded? -
QML is loaded before the signal is called but you are close on the DataProcess instances. I found the issue last night. I have a QML module that is called when a menu item or button is clicked to start the data import and this module calls the C++ function. I had been trying to get my head around the setContextProperty vs qqmlRegisterType debates and made changes so I used setContextProperty and then used that name in the QML modules. I changed main.qml but never thought about the getdata QML modulel - I mean it just calls the function, right <G>! I used the setContextProperty name in the get data module and it all works now.
Thanks for answering.