Add property to QML object after QQmlComponent::beginCreate() - qt

I am looking for a way to add properties in the scope of a certain objects in a QML object tree.
file.qml:
import QtQml 2.0
QtObject {
property var test1: QtObject {
objectName: "child1"
property string color: environment.color
}
property var test2: QtObject {
objectName: "child2"
property string color: environment.color
}
}
main.cpp:
#include <QtCore>
#include <QtQml>
int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv);
QQmlEngine engine;
QQmlComponent component(&engine,QUrl::fromLocalFile("file.qml"));
QObject* object = component.beginCreate(engine.rootContext());
QObject* child1 = object->findChild<QObject*>("child1", Qt::FindChildrenRecursively);
QObject* child2 = object->findChild<QObject*>("child2", Qt::FindChildrenRecursively);
QQmlPropertyMap env1;
QQmlPropertyMap env2;
env1["color"] = "green";
env2["color"] = "blue";
// Now I want to make object1 available for child1
// and object2 available for child2.
// But I can't do this:
// qmlContext(test1)->setContextProperty("environment", env1);
// qmlContext(test2)->setContextProperty("environment", env2);
component.completeCreate();
if (!component.errors().isEmpty()) {
qDebug() << component.errorString();
}
Q_ASSERT(child1->property("color") == QString("green"));
Q_ASSERT(child2->property("color") == QString("blue"));
qDebug() << "It works";
}
I admit that the example above looks a bit odd. It's a simplified variant of the example I posted in the Qt forum.

Related

Create an empty QStringList directly in a QML file

I hope someone can help me with this. I have an external QML Module which accepts a QStringList as parameter. However, what I have is a simple String. My question is: Is there a way in QML to convert a list of Strings into a QStringList without any external c++ functions?
Thanks
I tried to pass a simple string but it is not accepted.
You can use a JavaScript array of strings or list<string> depending on your Qt version. Have a look here.
main.qml
import QtQuick
Rectangle {
id: root
width: 640
height: 480
property var jsArray: ["apple", "banana", "mango"]
property list<string> stringList: ["Oslo", "Berlin", "New York"]
Component.onCompleted: {
var arr = ["more", "strings", "here"]
applicationData.setSomething(arr)
applicationData.setSomething(root.stringList)
applicationData.setSomething(root.jsArray)
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
class ApplicationData : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void setSomething(const QStringList &list) const
{
for (const auto &s : list)
qDebug() << s;
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
ApplicationData data;
view.rootContext()->setContextProperty("applicationData", &data);
view.setSource(QUrl(u"qrc:/75085103/main.qml"_qs));
view.show();
return app.exec();
}
#include "main.moc"

QML Slot not displaying double value from serial input

I'm struggling to get my serial input "analogread2" converted to a double, to display in QML.
Ive added the Q_property, the root context in the main.cpp, and I can add the property in the qml, but I cant get it to display in text format.
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
SerialPort serialport;
//engine.rootContext()->setContextProperty("serialport", &serialport);
qmlRegisterType<SerialPort>("SerialPortlib", 1, 0, "SerialPort");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")))
header.
Q_PROPERTY(double newValue MEMBER m_oil_pressure_volt WRITE set_oil_pressure_volt NOTIFY oil_pressure_volt_Changed )
qml
Window {
visible: true
width: 640
height: 480
id: gauge
SerialPort{
// what would I put in here to display the text value (double)
}
}
any help is much appreciated.
To do the test I am assuming that you are sending the data from the arduino in one of the following ways:
Serial.println(data);
Serial.print(data);
Assuming the above has been implemented the serialport class, this has a function called openDefault that scans the devices that are connected through serial and searches for the word "Arduino" within description or manufacturer and if one finds it connects to it.
The complete code can be found at: https://github.com/eyllanesc/stackoverflow/tree/master/Arduino-QML
serialport.h
#ifndef SERIALPORT_H
#define SERIALPORT_H
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
class SerialPort : public QObject
{
Q_OBJECT
Q_PROPERTY(double oil_pressure_volt READ get_oil_pressure_volt WRITE set_oil_pressure_volt NOTIFY oil_pressure_volt_Changed )
public:
SerialPort(QObject *parent = 0);
~SerialPort();
double get_oil_pressure_volt() const;
void set_oil_pressure_volt(double newValue);
public slots:
void onReadData();
signals:
void oil_pressure_volt_Changed(double newValue);
private:
QSerialPort *arduino;
double mOil_pressure_volt;
QSerialPortInfo portInfo;
void openDefault();
};
serialport.cpp
#include "serialport.h"
#include <QDebug>
SerialPort::SerialPort(QObject *parent):QObject(parent)
{
arduino = new QSerialPort(this);
connect(arduino, &QSerialPort::readyRead, this, &SerialPort::onReadData);
openDefault();
}
SerialPort::~SerialPort()
{
delete arduino;
}
void SerialPort::set_oil_pressure_volt(double newValue)
{
if (mOil_pressure_volt == newValue)
return;
mOil_pressure_volt = newValue;
emit oil_pressure_volt_Changed(mOil_pressure_volt);
}
void SerialPort::onReadData()
{
if(arduino->bytesAvailable()>0){
QByteArray data = arduino->readAll();
QString value = QString(data).trimmed();
qDebug()<< value;
bool ok;
double val = value.toDouble(&ok);
if(ok)
set_oil_pressure_volt(val);
}
}
void SerialPort::openDefault()
{
for(auto info: QSerialPortInfo::availablePorts()){
qDebug()<<info.portName()<<info.description()<<info.manufacturer();
if(!info.isBusy() && (info.description().contains("Arduino") || info.manufacturer().contains("Arduino"))){
portInfo = info;
break;
}
}
if(portInfo.isNull()){
return;
}
arduino->setPortName(portInfo.portName());
arduino->setBaudRate(QSerialPort::Baud115200);
arduino->setDataBits(QSerialPort::Data8);
arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);
arduino->setFlowControl(QSerialPort::NoFlowControl);
if(arduino->open(QSerialPort::ReadWrite))
qDebug()<<"Connected to "<< portInfo.manufacturer()<< " on " << portInfo.portName();
else
qCritical()<<"Serial Port error: " << arduino->errorString();
}
double SerialPort::get_oil_pressure_volt() const
{
return mOil_pressure_volt;
}
Example:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "serialport.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<SerialPort>("SerialPortLib", 1, 0, "SerialPort");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import SerialPortLib 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Test")
SerialPort{
onOil_pressure_voltChanged: {
tx.text = "%1".arg(oil_pressure_volt);
}
}
Text {
id: tx
anchors.fill: parent
font.family: "Helvetica"
font.pointSize: 20
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

How to call slot with QFlags argument from QML

I have an enum defined in a QObject with a few values, and I am registering the enum as QFlags as the Qt documentation specifies. I have registered the enum and the QObject as metatypes that I can access just fine from QML.
The problem is that once I have a C++ QObject slot defined that has the QFlags as an argument it doesn't get an error when it is called, but instead passes in the first defined value in the enum (ie. its value is that of the enum entry with the number 0).
It is hard to describe, so I created a small working example (using C++11/Qt 5.7). When you run it and click anywhere in the window that opens, QFlags<QMLThing::MyEnum>(VALA) is printed out, even though in main.qml I am calling thing.doThing(QMLThing.VALC).
I started by creating a "Qt Quick Application" in QtCreator. Then added a class called "QMLThing". Here is the source code for each file:
QMLThing.hpp
#ifndef QMLTHING_HPP
#define QMLTHING_HPP
#include <QObject>
class QMLThing : public QObject
{
Q_OBJECT
public:
enum MyEnum {
VALA = 0,
VALB = 1,
VALC = 2,
VALD = 4,
};
Q_ENUM(MyEnum)
Q_DECLARE_FLAGS(MyEnums, MyEnum)
public:
explicit QMLThing(QObject *parent = 0);
public slots:
void doThing(QMLThing::MyEnums val);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMLThing::MyEnums)
Q_DECLARE_METATYPE(QMLThing::MyEnums)
#endif // QMLTHING_HPP
QMLThing.cpp
#include "QMLThing.hpp"
#include <QDebug>
QMLThing::QMLThing(QObject *parent) : QObject(parent)
{}
void QMLThing::doThing(QMLThing::MyEnums val)
{
qDebug() << val;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "QMLThing.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<QMLThing>("stuff", 1, 0, "QMLThing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import stuff 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
thing.doThing(QMLThing.VALC)
}
}
Text {
text: qsTr("Click here and look in the terminal")
anchors.centerIn: parent
}
QMLThing {
id: thing
}
}
This seems a lot like a bug, but maybe I'm just missing something.
You're missing Q_FLAG(MyEnums):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QObject>
class QMLThing : public QObject
{
Q_OBJECT
public:
enum MyEnum {
VALA = 0,
VALB = 1,
VALC = 2,
VALD = 4,
VALE = VALC | VALD
};
Q_DECLARE_FLAGS(MyEnums, MyEnum)
Q_FLAG(MyEnums)
public:
explicit QMLThing(QObject *parent = 0) :
QObject(parent)
{
}
public slots:
void doThing(QMLThing::MyEnums val)
{
qDebug() << val;
}
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMLThing::MyEnums)
Q_DECLARE_METATYPE(QMLThing::MyEnums)
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<QMLThing>("stuff", 1, 0, "QMLThing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.7
import QtQuick.Window 2.2
import stuff 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
thing.doThing(QMLThing.VALC)
thing.doThing(QMLThing.VALC | QMLThing.VALD)
}
}
Text {
text: qsTr("Click here and look in the terminal")
anchors.centerIn: parent
}
QMLThing {
id: thing
}
}
As mentioned here, you don't need to use Q_ENUM():
Note: The Q_FLAG macro takes care of registering individual flag
values with the meta-object system, so it is unnecessary to use
Q_ENUM() in addition to this macro.
Not sure exactly what is going on but first of all:
public:
enum MyEnum {
VALA,
VALB,
VALC,
VALD,
};
You need to remove the last coma.
I would also recommend to set at least the first enum, to a certain value, usually 0 so you know where you are going but no need to set the following enum items as they will be auto-incremented from the last one set.
Last, I'm not entirely sure about the QMLThing.ValC, shouldn't it be QMLThing::MyEnums::ValC ?

How to create QML object within a singleton?

I have defined a QML object under MyQMLObject.qml. This QML file looks like this:
import QtQuick 2.4
Item {
id: rootItem
implicitWidth: LayoutUtils.maxImplicitWidth(children)
implicitHeight: LayoutUtils.maxImplicitHeight(children)
Text {
id: text1
}
Text {
id: text2
}
// ...
Text {
id: textN
}
}
The text is added dynamically when the application starts. For each language different text is added, there for the width of the rootItem varies by the chosen language. I would like to somehow create MyQMLObject only once at application startup without even visualizing it and save its actual width in a singleton for example so I can reuse that value throughout my code without creating MyQMLObject more then once. How could I achieve this?
Right now I have a singleton QML file, which holds a QtObject which contains some constant values. Can I somehow create an instance of MyQMLObject within this singleton QtObject?
My singleton Style.qml looks like this:
pragma Singleton
import QtQuick 2.4
QtObject {
readonly property int maxWidth: 400
// ...
}
Firstly, if possible, you could use a Column instead of manually calculating the maximum width:
MyQMLObject.qml
import QtQuick 2.4
Column {
Text {
text: "blah"
}
Text {
text: "blahblah"
}
}
You can use dynamic object creation to create the temporary Column item:
Style.qml
pragma Singleton
import QtQuick 2.4
QtObject {
readonly property int maxWidth: {
var component = Qt.createComponent("qrc:/MyQMLObject.qml");
if (component.status === Component.Error) {
console.error(component.errorString());
return 0;
}
return component.createObject().width;
}
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import App 1.0
Window {
visible: true
Component.onCompleted: print(Style.maxWidth)
}
Then, register the singleton:
main.cpp
#include <QtGui>
#include <QtQml>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterSingletonType(QUrl("qrc:///Style.qml"), "App", 1, 0, "Style");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
However, note that this approach could be improved by calculating the maximum width from C++, eliminating the need to construct an item only to throw it away. Working off this example:
#include <QtGui>
#include <QtQml>
class Style : public QObject
{
Q_OBJECT
Q_PROPERTY(int maxWidth READ maxWidth CONSTANT)
public:
Style(QObject* parent = 0) :
QObject(parent),
mMaxWidth(0)
{
QFontMetrics fontMetrics(qApp->font());
// Here is where you'd fetch the text...
QStringList dummyText;
dummyText << "blah" << "blahblah";
foreach (const QString &string, dummyText) {
const int width = fontMetrics.boundingRect(string).width();
if (width > mMaxWidth)
mMaxWidth = width;
}
}
int maxWidth() const
{
return mMaxWidth;
}
private:
int mMaxWidth;
};
static QObject *singletonTypeProvider(QQmlEngine *, QJSEngine *)
{
Style *style = new Style();
return style;
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<Style>("App", 1, 0, "Style", singletonTypeProvider);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
It uses QFontMetrics to calculate the width.
main.qml remains unchanged.

passing signal with parameters from qml to c++

I dont know how to pass parameters from QML file to c++ file in Qt.
QML code:
import QtQuick 1.1
Rectangle{
id:loin
height: 272
width:480
property alias loguid:loginuid
signal sigHome()
Rectangle{
id:rect1
width:parent.width-80
height:24
TextInput {
id:loginuid
maximumLength: 16
width: maximumLength * 20
focus: false
validator: RegExpValidator { regExp: /\d+/ }
KeyNavigation.down: login1
}
}
Button{
id: login1
x: 195
y: 187
height:30;
focus:false
border.color:"black"
opacity: activeFocus ? 1.0 : 0.5
Text{
text:"LOGIN"
anchors.horizontalCenter:login1.horizontalCenter;
anchors.verticalCenter:login1.verticalCenter;
}
Keys.onReturnPressed: {
if(loginuid.text < 1000000000000000)
{
text1.opacity=0.1
error1.visible=true
errorText.text="\n enter valid 16 digit number\n"
errorOk.focus=true
loginuid.focus=false
}
else{
loginuid.focus=false
loin.sigHome()
}
}
}
}
c++ code:
#include <QApplication>
#include <QDeclarativeView>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
int uid;
QDeclarativeView view;
view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();
return app.exec();
}
When I press the login button rect1.text content shud go to main.cpp file and uid in the main.cpp get dat value. Something like this uid=rect1.text.
How to do it?
I wouldn't try to listen for a QML signal from the C++ side. Calling a C++ method with arguments is much easier and achieves the same:
To do so you have to:
define a slot or invokable method accepting the required arguments
register the class carrying the method with the declarative engine
then you can set an instance of this class as a property of your root context and finally call this method from QML
This topic is also well covered in the official documentation.
Thanks, sebasgo, your response helped me. I used signals and slots to communicate.
I created a signal in main.qml.
signal info(string msg)
and in login page
else{
info(loginUid.text)
loginuid.focus=false
loin.sigHome()
}
and in main.cpp I connected it to d slot
main.cpp goes like this
#include <QtGui>
#include <QApplication>
#include <QDeclarativeView>
#include <QtDeclarative>
class DeclarativeView : public QDeclarativeView
{
Q_OBJECT
public:
DeclarativeView(const QUrl & source) : QDeclarativeView(source)
{
}
public slots:
void readText(QString quid)
{
qdebug<<quid;
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QString file = "main.qml";
QApplication app(argc, argv);
DeclarativeView view(QUrl::fromLocalFile(file));
QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(view.rootObject());
QObject::connect(item, SIGNAL(info(QString)), &view, SLOT(readText(QString)));
view.show();
return app.exec();
}
Create a GUI controller C++ class:
class UiController : public QObject
{
Q_OBJECT
public:
UiController();
virtual ~UiController();
public slots:
void cal_daysoff__onDoubleClicked(const QDate& date);
};
In QML file you define, say, a calendar control in which you connect a signal to a slot in the controller:
Calendar{
id: cal_daysoff
Layout.fillWidth: true
Layout.fillHeight: true
onDoubleClicked: UiController.cal_daysoff__onDoubleClicked(date)
}
In main file, when launching the QML interface, connect the interface to the controller:
#include "uicontroller.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
UiController control;
engine.rootContext()->setContextProperty("UiController", &control);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}

Resources