Connecting C++ with QML
-
OK, so this works, again none of the standard setup that I usually see in main.cpp when I start a new project.
The IDE reports that main.moc cannot be found but it still compiles and then stops complaining.
But it runs,Of course trying to move the QML stuff into the Main.qml file is not working.
-
OK, so this works, again none of the standard setup that I usually see in main.cpp when I start a new project.
The IDE reports that main.moc cannot be found but it still compiles and then stops complaining.
But it runs,Of course trying to move the QML stuff into the Main.qml file is not working.
@Groundbounce *.moc files are generated by the meta(m)object(o)compiler(c), needed for Qt-magic. You're not supposed to use the Q_OBJECT macro inside cpp files. The automatic setup doesn't work then, so you end up needing to include the moc file manually
-
Right so now have it working with the class in a header file of it's own and the QML stuff in Main.qml,
Thank you!
Next I'll try and put the QML stuff in a file of it's own. -
You think I have not been looking? I'll watch those tomorrow as it's late now.
So far connecting C++ and QML tutorials always turn out to be creating a class in C++ with the object instantiated in QML and used exclusively in QML, this is not what I am looking for. Much of the material is out of date and will throw compiler errors and warnings, because the reasons for things are not explained I have nothing to go on. Indeed I am convinced the only fool proof way to do it is via a server! But that is another skill i will have to master! so close, yet so far away.
This little gem from here: https://doc.qt.io/qt-6/qtqml-cppintegration-interactqmlfromcpp.html#connecting-to-qml-signals just throws errors, I am not even sure where the code is supposed to go:
// Using QQmlComponent
QQmlEngine engine;
QQmlComponent component(&engine,
QUrl::fromLocalFile("MyItem.qml"));
QObject *object = component.create();
...
delete object;@Groundbounce said in Connecting C++ with QML:
So far connecting C++ and QML tutorials always turn out to be creating a class in C++ with the object instantiated in QML and used exclusively in QML, this is not what I am looking for
That's curious, most of those tutorials instanciate those classes in C++ and pass it to QML via (as JHilk already suggested)
engine->rootContext()->setContextProperty ( ... )
as the easiest is to instanciate in C++ and pass it to QML. Creating a qml module for a component to be instanciatable in QML is a little more tricky ;) ;) ;) -
Try https://academy.qt.io/enrollments/254032569/details
This has no C++ code in it at all except creating the actual class, it simply takes a mouse input and changes something on the screen, the class is instantiated in the QML file.
This is the sort of thing the manual talks about a lot.
again this video from the playlist linked above is very vague on actual detail: https://www.youtube.com/watch?v=quFciLDIxUw&list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc&index=46
-
I don't know. I don't know what I don't know. My programming background is microcontrollers. This is my fist experimenting with programming on an operating system and it is as I expected very different.
It almost looks like he set up the variables within the setContextProperty - this is video 46 - I don't see at any point the actual C++ variable he is linking to. in fact I would say those are constants. The he talks about gadgets.
The code that @J.Hilk posted gets it in one does exactly what every newcommer to QML will be asking, how do I get variables in C++ into QML. I get that someone has this dream of QML stuff being practically a separate program from the C++ back end but they must communicate somehow.
As I said the only way to do this without doing anything that upsets anyone in terms of total reusability is to totally separate the two (automatic multithreading done right there) into two programs and have them communicate via something that we know both speak like a webserver or database (I have no abilities in either yet).
The way QML is presented feels like someone wants to one day have just QML, no C++. I have an entire book on QML, but the author explicitly said that he would not even talk about how QML and C++ share data as the book is about QML only. Well as Jesper said in video 46, without C++ QML programs are just toys.
-
Qt is not all about QML for sure and C++ still has a huge importance in it, apps are not just about user interface, there's lots of core stuff behind that the author was probably not focusing about. Yeah just like toys like Jesper said. You still need a lots of C++ for communication, data handling and so on.
The author of your book probably just wanted to focus on the QML language itself, I don't know the reasons about that, but I guess he just wanted to focus on UI creation only and forgetting all the core stuff and how to integrate core with QML ? Does he explain the reasons of the choice when he makes the statement ?
I just guess this might a book just about UI creation, not that much intened for developpers. -
The book is just about QML: Qt6 QML for beginners by Daniel Gakwaya. Very good for QML yes, I recommend it. But someone has to explain how to do this, he came so close with QML functionality but said he would not touch on the C++ side of it.
-
The most simple way to use QML is treat it as user interface only. All calculations, validations and logic makes cpp. My QML is dumb: it sends input and receives output to show. I am migrating from Qt Widget to QML now and can share my experience.
The most difficult part for me was to find an approach to provide communications between these two parts of app and register their binding. I have made a controller cpp class which manages requests and sends data from backend cpp classes and UI through Qt signal-slot mechanism. No Q_PROPERTY in my app just signals. Any communication between any calculator class and UI goes through controller. Controller owns calculators and makes all decisions.Controller.h class Controller : public QObject { Q_OBJECT QML_SINGLETON QML_ELEMENT public: explicit Controller(QObject *parent = nullptr); //next is a set of functions which are triggered by QML, Q_INVOKABLE is a keyword that connects Q_INVOKABLE void goToMainWindow(); Q_INVOKABLE void loadWindow(const QString &qmlFileName, const QString &key); Q_INVOKABLE void window1TextFieldsParser(); Q_INVOKABLE void window2TextFieldsParser(); Q_INVOKABLE void window3TextFieldsParser(); Q_INVOKABLE void profileEditTextFieldsParser(); Q_INVOKABLE void on_loadProfileButton_clicked(const QString &nameOfProfile); //I extract and send only strings, cpp decides how to treat them
signals: //To QML void window1Changed(QString summary); void showInfo(const QString &message, const QString &title, QObject* window); void showConfirmationDialog(const QString &message, const QString &title, QObject* window, const QString &requestId); void window2Changed(const QString summary); void clearListViewItems(); void addItemsToListView (const QString &profileName); void updateLabel(const QString &summary);
How QML sends signal?
Button{ id: button } //LOGIC onClicked: { //QML emits signal to Controller that switches window Controller.loadWindow("Window2.qml", "Window2") } }
Button{ id: loadProfileButton text: qsTr("Load") onClicked:{ //LOGIC if(profileListView.currentIndex!==-1){ //assign variable let selectedProfileName = model.get(profileListView.currentIndex).name //and emit signal with value Controller.on_loadProfilePushButton_clicked(selectedProfileName) }else{ //show error message messenger.showInfo(qsTr("Select a profile"), qsTr("Error")) } }
How QML receives signal?
Connections { target: Controller function onClearListViewItems() { model.clear() } function onAddItemsToListView(profileName) { model.append({ name: profileName }) } }
Easy but very verbose language
-
Why even do it in c++ if all your data is stored in QML and you are not using properties or models in c++?
Given the name of the invokables and slots it seems that the c++ is too coupled to the QML and it goes against what is usually recommended.
-
Because this is my first experience in QML and I never used JavaScript as well to write additional code in QML but I feel confident in c++.
I have two dozens of TextField objects to accept user input in different ways with input validation. It might be string, integer or double that`s why I use the most simple approach for my current level of knowledge.I have a window with 7 TextFields and single Input button. How do I parse them?
//QML Button{ id: inputWindow1Button objectName: "inputWindow1Button" text: qsTr("Input") //LOGIC onClicked: { //emits signal to run Controller::window1TextFieldsParser() which is declared as Q_INVOKABLE Controller.window1TextFieldsParser() } }
.cpp
void Controller::window1TextFieldsParser(){ QQuickWindow* multiWindow = m_currentWindow; //currently active window where cpp looks for objectName to parse QObject* speed = multiWindow->findChild<QObject*>("speed1of3"); //look for QML objectName QString speedText = speed->property("text").toString().trimmed(); //extract text from "speed1of3" TextField QML object double speedCheck; if (speedText.isEmpty()){//check if QString is empty //Error signal to QML emit showInfo(tr("Speed field is empty"), tr("Invalid input"), m_currentWindow); return; }else{ //convert QString to double, normalize input (treat both coma and dot as decimal separator) speedCheck = convertStringToDouble(speedText); } //validate input range if(speedCheck<=0 || std::isnan(speedCheck)){ //handle invalid input through emit showInfo() return; }else{ m_speed=speedCheck; //assign member variable with valid value } }
-
Because this is my first experience in QML and I never used JavaScript as well to write additional code in QML but I feel confident in c++.
I have two dozens of TextField objects to accept user input in different ways with input validation. It might be string, integer or double that`s why I use the most simple approach for my current level of knowledge.I have a window with 7 TextFields and single Input button. How do I parse them?
//QML Button{ id: inputWindow1Button objectName: "inputWindow1Button" text: qsTr("Input") //LOGIC onClicked: { //emits signal to run Controller::window1TextFieldsParser() which is declared as Q_INVOKABLE Controller.window1TextFieldsParser() } }
.cpp
void Controller::window1TextFieldsParser(){ QQuickWindow* multiWindow = m_currentWindow; //currently active window where cpp looks for objectName to parse QObject* speed = multiWindow->findChild<QObject*>("speed1of3"); //look for QML objectName QString speedText = speed->property("text").toString().trimmed(); //extract text from "speed1of3" TextField QML object double speedCheck; if (speedText.isEmpty()){//check if QString is empty //Error signal to QML emit showInfo(tr("Speed field is empty"), tr("Invalid input"), m_currentWindow); return; }else{ //convert QString to double, normalize input (treat both coma and dot as decimal separator) speedCheck = convertStringToDouble(speedText); } //validate input range if(speedCheck<=0 || std::isnan(speedCheck)){ //handle invalid input through emit showInfo() return; }else{ m_speed=speedCheck; //assign member variable with valid value } }
-
Thanks to your recommendations I rejected the ugly practice to dig out input values from QML with findChild<QObject*>("objectName"); and implemented a Q_PROPERTY binding.
I see a disappointing limitation, maybe you can give me an advise how to overcome it
Consider the simple example with several TextFields, Input button and Start button
QML Button{ id: inputButton onClicked:{ //extract values from TextFieldName.text AppController.setTextFieldValue([value1, value2,...]) } Button{ id: startButton onClicked:{ AppController.startCalculations() }
I have connected my QML and cpp as follows
cpp Q_PROPERTY (QVariantList someValues READ someValues WRITE setSomeValues NOTIFY someValuesChanged FINAL) As soon as user pushes Input button cpp receives and processes data because QML sends them through Q_INVOKABLE void setSomeValues() like this: void ControllerClass::setSomeValues(const QVariantList &someValues){ m_temperature = someValues[0]; //and so on ... m_windSpeed = someValues[5]; }
Let`s image the situation that user has provided input but forgotten to push Input button
Cpp has not received data yet but those values exist in QML TextFieldsvoid ControllerClass::startCalculation(){ if(!temperature.hasValue()){ //std:optional<int> //Instead of request QML for current values requestQML(); //user has forgotten to push button to set temp //I need to emit error message emit showInfo("Error", "Invalid input"); return; }
So the limitation I see in this case that QML can send data to cpp, but cpp cannot request current values.
How cpp can ask QML for missing data? How to handle such cases properly? -
That depends on what you specify for your UX. If you don't want to have to use a submit button then mapping each textfield to a c++ property is the more direct way yes. Doing that on
editingFinished
is a good solution.