Emit signal from C++ to QML without any parameters
-
Hi.
Im trying to implement logging mechanism into my app.
What I've done so far is created LoginView.qml file:
// LoginView.qml Item { id: loginView signal loginClicked(string textField_usr, string textField_pwd); objectName: "loginView" // ATTEMPTS SO FAR // Connections { // target: MasterController.ui_loginController // onLoginSuccessful: MasterView.contentFrame.replace("qrc:/views/DashboardView.qml") // onLoginFailed: console.log("login failed") // } // LoginController { // id: loginController // onLoginSuccessful: MasterView.contentFrame.replace("qrc:/views/DashboardView.qml") // onLoginFailed: console.log("login failed") // } GroupBox { /* Some styling, including TextFields etc*/ Button { id: button objectName: "proceed_button" /*more styling*/ onClicked: { loginClicked(username_textField.text, password_textInput.text) } } } }
LoginView.qml gets loaded by MasterView.qml onCompleted()
Window { /*Some stuff - nothing related to login process*/ StackView{ id: contentFrame /*...*/ initialItem: Qt.resolvedUrl("qrc:/views/SplashView.qml") } Component.onCompleted: { contentFrame.replace("qrc:/views/LoginView.qml") } }
With that in place, I have created LoginViewController class:
namespace cms{ namespace controllers{ class CMSLIBSHARED_EXPORT LoginViewController : public QObject{ Q_OBJECT private: QObject* loginButton {nullptr}; public: explicit LoginViewController(QObject* _parent = nullptr) : QObject(_parent) {} LoginViewController(QQmlApplicationEngine* engine){ QObject* loginView = engine->rootObjects().first()->findChild<QObject*>("loginView"); loginButton = loginView->findChild<QObject*>("proceed_button"); QObject::connect(loginView, SIGNAL(loginClicked(QString, QString)), this, SLOT(loginButtonClicked(QString, QString))); } public slots: void loginButtonClicked(QString user, QString pwd); signals: void loginSuccessful(); void loginFailed(); }; } }
LoginViewController is a member of MasterController, which I export as MasterController's Q_PROPERTY:
Q_OBJECT Q_PROPERTY(cms::controllers::LoginViewController* ui_loginController READ loginController CONSTANT) public: LoginViewController* loginController();
Here's a part that im not really sure of.
I registered my LoginViewController class as QML type inside main.cppqmlRegisterType<cms::controllers::MasterController>("CMS", 1, 0, "MasterController"); //newly added qmlRegisterType<cms::controllers::LoginViewController>("CMS", 1, 0, "LoginController");
Everything works fine when I emit the signal from Qml into C++, login function works as intended, but I am getting stucked when i try to emit different signals depending on login method output.
After signal from Qml button gets emitted, it triggers login function
void LoginViewController::loginButtonClicked(QString user, QString pwd) { std::cerr << "loginButtonClicked " << std::endl; if(cms::administration::Clinic::login(user.toStdString(), pwd.toStdString())){ loginSuccessful(); return; } loginFailed(); }
As you can see, here I'd like to emit signal into QML. I came by different posts, different documentation pages, and i feel like left with too many tools to work with.
Could somebody guide me, on how should I emit and handle the signal inside QML?
Moreover, if there's any "better" or more suitable solution for this mechanism, I'd be happy to hear about it.Best regards!
-
I would do the following :
- instead of registering your LoginViewController, I would simply create a context property for your controller instance. Something like :
cms::controllers::LoginViewController controler; QQmlApplicationEngine engine; engine->rootContext()->setContextProperty("loginCtrl", this);
- instead of retrieving the login button from c++ and create the connection etc...simply call loginButtonClicked on the controller property in the button onClicked handler.
Button { id: button objectName: "proceed_button" /*more styling*/ onClicked: loginCtrl.loginButtonClicked(username_textField.text, password_textInput.text) }
- use a "Connection" item in QML with the controller property as target to react to loginSuccess/loginFailure
Connection{ target: loginCtrl onLoginSuccessful: {} onLoginFailed:{} }
-
Hi @Charby, thanks for your feedback.
Your idea works just fine, but im getting those errors, which I cannot get rid of.I have added Connection item you have mentioned about:
- inside LoginView.qml
Connections { target: loginController onLoginSuccessful : { masterView.contentFrame.replace("qrc:/views/DashboardView.qml") } onLoginFailed: {console.log("Failed") } }
- inside main.cpp
cms::controllers::MasterController masterController; QQmlApplicationEngine engine; engine.addImportPath("qrc:/"); engine.rootContext()->setContextProperty("masterController", &masterController); engine.load(QUrl(QStringLiteral("qrc:/views/MasterView.qml"))); cms::controllers::LoginViewController lvc(&engine); engine.rootContext()->setContextProperty("loginController", &lvc);
But it produces following errors:
qrc:/views/LoginView.qml:13:5: QML Connections: Cannot assign to non-existent property "onLoginFailed" qrc:/views/LoginView.qml:13:5: QML Connections: Cannot assign to non-existent property "onLoginSuccessful" qrc:/views/LoginView.qml:14: ReferenceError: loginController is not defined
Any ideas?