Get user input from qml to c++ - c++

I'm trying to get user input from qml TextField to c++, but it only works if text property value is hardcoded (const value).
c++
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *serverField1 = rootObject->findChild<QObject*>("serverField1");
qDebug() << serverField1->property("text"); //Here I expect to get user input
Qml
ApplicationWindow {
id: applicationWindow
visible: true
width: 300
height: 550
TextField {
id: serverField1
objectName: "serverField1"
width: 200
height: 110
// text: "hardcoded value" //If text is const value, qDebug will get data from this property
}
}

You are asking for the text of the TextField when the window is displayed so what you will get is the text you have initially set, what you should do is get it every time it changes. I think that you have some class that will handle some processing with that data, that class will be called Backend, it must inherit from QObject so that it can have a qproperty and embed an object of that class to QML through setContextProperty, so every time it changes the text in QML will also change the text in the Backend class object.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlProperty>
#include <QDebug>
class Backend: public QObject{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
QString text() const{
return mText;
}
void setText(const QString &text){
if(text == mText)
return;
mText = text;
emit textChanged(mText);
}
signals:
void textChanged(const QString & text);
private:
QString mText;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// test
QObject::connect(&backend, &Backend::textChanged, [](const QString & text){
qDebug() << text;
});
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TextField {
id: serverField1
x: 15
y: 46
width: 120
height: 45
topPadding: 8
font.pointSize: 14
bottomPadding: 16
placeholderText: "Server Ip"
renderType: Text.QtRendering
onTextChanged: backend.text = text
}
}
You can find the complete code in the following link.

Related

Control Qt QML SwipeView with Signals/Slots

I'm very new to Qt, and right now struggling to figure out how to change the index of the SwipeView through a different C++ class. I'd like to make some kind of a signal that emits from the C++ class saying "Swipe Right" or something like that, and having the SwipeView respond. I'm also new to signals and slots, so I might be misunderstanding how they work.
When you want to control a QML element from C ++ the best strategy is to create a QObject that has Q_PROPERTY, slot and signals and export it to QML through setContextProperty(), and perform the direct binding or use Connections to connect the signals, in this case it is only necessary to emit a signal and connect it in QML.
In the following example I show how to change to the right or left using QPushButton:
main.cpp
#include <QApplication>
#include <QPushButton>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QHBoxLayout>
class SwipeManager: public QObject{
Q_OBJECT
public:
using QObject::QObject;
public slots:
void moveToRight(){
emit toRight();
}
void moveToLeft(){
emit toLeft();
}
signals:
void toRight();
void toLeft();
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
SwipeManager manager;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("manager", &manager);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QWidget w;
QPushButton left("to left");
QPushButton right("to right");
QHBoxLayout *lay = new QHBoxLayout(&w);
lay->addWidget(&left);
lay->addWidget(&right);
QObject::connect(&left, &QPushButton::clicked, &manager, &SwipeManager::moveToLeft);
QObject::connect(&right, &QPushButton::clicked, &manager, &SwipeManager::moveToRight);
w.show();
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
title: qsTr("SwipeView Test")
SwipeView {
id: view
anchors.fill: parent
currentIndex: 4
Repeater {
model: 10
Loader {
active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem
sourceComponent: Item{
Text {
text: "Page: "+index
anchors.centerIn: parent
}
}
}
}
}
Connections{
target: manager
onToRight: if(view.currentIndex + 1 != view.count) view.currentIndex += 1
onToLeft: if(view.currentIndex != 0) view.currentIndex -= 1
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
The complete example can be found in the following link.

Dynamically created Custom QML object not visible

I have a custom QML item called "Cell.qml" which I'd like to insert dynamically in my root window and make it visible. I also tried changing the z property, but I couldn't fix it, the object is still invisible (even tough the output of root->dumpObjectTree() says the object exists as a child of the root window)
This is the result after executing the code
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *root = engine.rootObjects()[0];
QQmlComponent component(&engine, "qrc:/Cell.qml");
if (component.isReady()){
QObject *object = component.create();
object->setParent(root);
engine.setObjectOwnership(object, engine.CppOwnership);
}
root->dumpObjectTree();
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
Cell.qml
import QtQuick 2.0
import QtQuick.Controls 2.3
Item{
property string txt: ""
property color c: "#d4ccc4"
visible: true
z:20
Rectangle {
width: 75; height: 75
color: c
visible: true
radius : 3
scale : 1
z:10
Text{
anchors.centerIn: parent
text: txt
font.family: "Helvetica"
font.pointSize: 20
color: "white"
}
}
}
Output of root->dumpObjectTree();:
QQuickWindowQmlImpl::
QQuickRootItem::
Cell_QMLTYPE_0::
QQuickRectangle::
QQuickText::
setParent() sets a non-visual, QObject level parent member. QQuickItem has another property, parentItem which is the parent QQuickItem property in QML, it is that property that needs to be set so the object appears visually.

Qt: Connect list in c++ to listview in QML

I just started learning Qt. To be honest, some of confuses my a lot. I'm trying to load a predefined .csv table into a listview.
I created a qml file with the simple layout of a textfield, a listview and a button to load a file. The file looks like this:
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: applicationWindow
objectName: "App"
visible: true
width: 640
height: 480
title: qsTr("Rosinenpicker")
ColumnLayout {
id: columnLayout
anchors.rightMargin: 40
anchors.leftMargin: 40
anchors.bottomMargin: 40
anchors.topMargin: 40
anchors.fill: parent
TextField {
id: name
text: qsTr("Text Field")
Layout.preferredHeight: 50
Layout.fillWidth: true
}
ListView {
id: list
x: 0
y: 0
width: 110
height: 160
spacing: 0
boundsBehavior: Flickable.DragAndOvershootBounds
Layout.fillHeight: true
Layout.fillWidth: true
objectName: "listView"
model: guestModel
delegate: Item {
x: 5
width: 80
height: 40
Text {
text: model.modeldata.name
font.bold: true
anchors.verticalCenter: parent.verticalCenter
}
}
}
Button {
id: button
width: 50
text: qsTr("Open File")
Layout.preferredWidth: 100
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: fileDialog.visible = true;
}
FileDialog {
id: fileDialog
title: "Please choose a valid guestlist file"
objectName: "fileDialog"
nameFilters: ["Valid guestlist files (*.csv)"]
selectMultiple: false
signal fileSelected(url path)
onAccepted: fileSelected(fileDialog.fileUrl)
}
}
}
In my main file I create a CsvParser class a connect it to the fileSelected signal of the file dialog.
I can parse the csv-File. But when I try to connect it to the listview, I'm lost.
The main file:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtDebug>
#include <QUrl>
#include <QQuickView>
#include "csvparser.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
auto root = engine.rootObjects().first();
auto fileDialog = root->findChild<QObject*>("fileDialog");
CsvParser parser(engine.rootContext());
QObject::connect(fileDialog, SIGNAL(fileSelected(QUrl)), &parser, SLOT(loadData(QUrl)));
return app.exec();
}
My parser looks like this:
#include "csvparser.h"
#include <QtDebug>
#include <QFile>
#include "guest.h"
CsvParser::CsvParser(QQmlContext *context)
{
this->context = context;
}
void CsvParser::loadData(QUrl path)
{
friendList.clear();
guestList.clear();
QFile file(path.toLocalFile());
file.open(QIODevice::ReadOnly);
// ignore first lines
for(int i=0; i<3; i++)
file.readLine();
while(!file.atEnd()) {
auto rowCells = file.readLine().split(',');
if(rowCells.size() != 6)
continue;
checkedAdd(friendList, rowCells[0], rowCells[1]);
checkedAdd(guestList, rowCells[3], rowCells[4]);
}
qDebug() << guestList.size();
auto qv = QVariant::fromValue(guestList);
context->setContextProperty("guestModel", qv);
}
void CsvParser::checkedAdd(QList<QObject*> &list, QString name, QString familyName)
{
if(name == "" && familyName == "")
return;
list.append(new Guest(name, familyName));
}
And the guest, which has only a name and a familyname looks like this:
#ifndef GUEST_H
#define GUEST_H
#include <QObject>
class Guest : public QObject
{
public:
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString fName READ fName WRITE setfName NOTIFY fNameChanged)
public:
Guest(QString name, QString fName);
QString name();
QString fName();
void setName(QString name);
void setfName(QString fName);
signals:
void nameChanged();
void fNameChanged();
private:
QString m_name, m_fName;
};
#endif // GUEST_H
I get the following output:
qrc:/main.qml:41: ReferenceError: guestModel is not defined
QFileInfo::absolutePath: Constructed with empty filename
2
qrc:/main.qml:49: TypeError: Cannot read property 'name' of undefined
qrc:/main.qml:49: TypeError: Cannot read property 'name' of undefined
What am I doing wrong? Any help is appreciated. Thanks!
There are a few things I think are wrong. QML will not find guestModel, because it tries to render the ListView but it has not been made available to QML since you are setting the context property in loadData(), which is however only invoked when the user interacts with the FileDialog. Also, the Q_OBJECT macro needs to appear in the private section of the class (see http://doc.qt.io/qt-5/qobject.html#Q_OBJECT). Also, I do not think it is nice to make a data container/parser class aware of the UI, i.e. you export the rootContext to the parser. It would be better if you export something from the parser to the rootContext in main. The parser should have no notion of the UI IMHO. Finally, if you want to implement a custom list of data items to be displayed, you really should think about using an AbstractListModel as Yuki has suggested (see, http://doc.qt.io/qt-5/qabstractlistmodel.html) or another suitable List-based model (see, http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html). This way, changes in C++ will automatically result in updates in the UI.

How to access properties of another file from main.qml after they are modified by C++ code first

I’m having problems with QML and C++. If someone could take a look at this piece of code and tell me what I am doing wrong. I ‘m just trying to print “msg.author” on a window (main window) but everytime I try to access it from main.qml there’s an error saying msg is not defined. Thank you for your time.
main.qml
import QtQuick 2.2
import QtQuick.Window 2.1
Window {
property alias name: value
visible: true
id: main_window
width: 500; height: width
color:"black"
}
MyItem.qml
import QtQuick 2.0
import QtQuick.Window 2.0
Text {
id: text1
visible: true
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
color: "green"
font.pixelSize: 20
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
message.h
#ifndef MESSAGE
#define MESSAGE
#include <QObject>
#include <QString>
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
void setAuthor(const QString &a) {
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
QString author() const {
return m_author;
}
signals:
void authorChanged();
private:
QString m_author;
};
#endif // MESSAGE
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>
#include "message.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine_e;
engine_e.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQmlEngine engine;
Message msg;
engine.rootContext()->setContextProperty("msg", &msg);
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
if(component.status()!=component.Ready){
if(component.Error){
qDebug()<<"Error: "<<component.errorString();
}
}
return app.exec();
}
The code is not working, and the main has two engines. The second is not properly set-up and it should also be noted, from QQmlApplicationEngine documentation, that:
This class combines a QQmlEngine and QQmlComponent to provide a convenient way to load a single QML file. It also exposes some central application functionality to QML, which a C++/QML hybrid application would normally control from C++.
Hence, QQmlApplicationEngine can perfectly suit your needs and load the QML file. Now, it should also be noted that context properties should always be set before loading a QML file. The final correct code is the following:
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine_e;
Message msg;
engine_e.rootContext()->setContextProperty("msg", &msg);
engine_e.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
EDIT
For what concerns QML files, they should be placed inside the resources (as I hope you have done). My test setting is the following:
main.qml:
import QtQuick 2.3
import QtQuick.Window 2.1
Window {
// property alias name: value
visible: true
id: main_window
width: 500; height: width
// color:"black"
MyItem {
anchors.centerIn: parent // <--- added to the main window to see the result
}
}
MyItem.qml
import QtQuick 2.0
Text {
id: text1
visible: true
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
color: "green"
font.pixelSize: 20
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
}

Set loader component by calling C++ function

I'm just wondering if anything like this is possible :
Loader {
id: loader
objectName: "loader"
anchors.centerIn: parent
sourceComponent: cppWrapper.getCurrentDisplay();
}
And in C++ :
QDeclarativeComponent currentDisplay;
and
Q_INVOKABLE QDeclarativeComponent getCurrentDisplay() const
{ return currentDisplay; }
I have trouble compiling it (it fails in the moc file compilation), but if it's possible, it can be a real shortcut for me
Sure, you can create Component in C++ part (as QQmlComponent) and return it to QML part.
Simple example (I use Qt 5.4):
First of all I create a class to use it as singleton (just for ease using)
common.h
#include <QObject>
#include <QQmlComponent>
class Common : public QObject
{
Q_OBJECT
public:
explicit Common(QObject *parent = 0);
~Common();
Q_INVOKABLE QQmlComponent *getComponent(QObject *parent);
};
common.cpp
QQmlComponent *Common::getComponent(QObject *parent) {
QQmlEngine *engine = qmlEngine(parent);
if(engine) {
QQmlComponent *component = new QQmlComponent(engine, QUrl("qrc:/Test.qml"));
return component;
}
return NULL;
}
Now I create and register my singleton:
main.cpp
#include "common.h"
static QObject *qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
static Common *common = new Common();
return common;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterSingletonType<Common>("Test", 1, 0, "Common", qobject_singletontype_provider);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Ok, now we have a singleton and it is very simple to use it in QML:
main.qml
import QtQuick 2.3
import Test 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: mainWindow
Loader {
id: loader
objectName: "loader"
anchors.centerIn: parent
sourceComponent: Common.getComponent(mainWindow)
}
}
Also our component we created in C++:
Test.qml
import QtQuick 2.3
Rectangle {
width: 100
height: 100
color: "green"
border.color: "yellow"
border.width: 3
radius: 10
}
Pay attention: all our QML files are in resource but it's just for example and you can put it in any place you want in your case