How to assign a dictionary to a QML property? - qt

I have a custom QML object written in C++ where one property is of the type QVariantMap, so it should be compatible with Javascript objects.
However, I have some troubles actually assigning a JS object:
Uploader {
sample: selectSampleButton.fileUrl
parameters: {
x: '3'
y: "String"
}
}
Is this even possible like this? One possiblity I found that works is to assign it in Javascript:
uploader.parameters = {x: "Test"};

You need to wrap it in parentheses, otherwise it is parsed as a binding expression :
Uploader {
sample: selectSampleButton.fileUrl
parameters: ({
x: '3'
y: "String"
})
}

If you have a fixed set of keys you could create a "grouped property", e.g. like font or anchors
The type of a grouped property is just a QObject derived class with respective Q_PROPERTY declaration and an instance of that is used by the main class, in your case the class behin Uploader as a Q_PROPERTY of a pointer to the new type.
Roughly like this
class UploaderParameters : public QObject
{
Q_OBJECT
Q_PROPERTY(int x MEMBER m_x NOTIFY xChanged)
};
class Uploader : public QObject
{
Q_PROPERTY(UploaderParameters* parameters MEMBER m_parameters CONSTANT)
};
in QML
Uploader {
parameters.x: 3
}
or
Uploader {
parameters {
x: 3
}
}

Related

What is the proper usage of Q_DECLARE_SMART_POINTER_METATYPE?

I tried to define a function that returns std::shared_ptr<OrderModel> as follows:
Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
namespace tradeclient {
class OrderModel : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString marketId READ marketId CONSTANT)
Q_PROPERTY(quint64 id READ id CONSTANT)
...
};
using OrderPtr = std::shared_ptr<OrderModel>;
class MarketModel : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE tradeclient::OrderPtr createLimitOrder()
{
return m_orders.front();
}
private:
//In my app I initialize OrderModel-s and add them so some container.
std::vector<OrderPtr> m_orders;
};
} //namespace tradeclient
Q_DECLARE_METATYPE(tradeclient::OrderPtr)
qRegisterMetaType<tradeclient::OrderPtr>();
and then call createLimitOrder() from QML:
var order = market.createLimitOrder()
console.log("Limit order type: %1, value: %2, id: %3".arg(typeof(order)).arg(JSON.stringify(order)).arg(order.id))
but with no success, because I can't access order.id and the console output is:
Limit order type: object, value: "", id: undefined
I also tried to define functions like this:
Q_INVOKABLE QVariant createLimitOrder()
{
return QVariant::fromValue(m_orders.front());
}
Q_INVOKABLE QSharedPointer<tradeclient::OrderModel> createLimitOrder()
{
return QSharedPointer<tradeclient::OrderModel>(new OrderModel());
}
but nothing changed.
But at least, QML treats order as a non-empty value and the following QML code:
if (order)
console.log("The order is defined.");
else
console.log("The order is not defined.");
prints:
"The order is defined."
So order is defined, but its properties are not. What did I miss?
Buy the way, the above QML code works correctly if I return naked pointer:
Q_INVOKABLE tradeclient::OrderModel* createLimitOrder()
{
return m_orders.front().get();
}
it prints
Limit order type: object, value: {"objectName":"","marketId":"BTCUSDT","id":0,"side":1,"type":0,"status":0,"price":{"precision":2},"stopPrice":{".....
Does QVariant that wraps std::shared_ptr expose object properties to QML?
EDIT1
As far as I understand, Q_DECLARE_SMART_POINTER_METATYPE is a relatively simple thing, it requires the class template to have operator -> (see ./qtbase/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp):
namespace MyNS {
template<typename T>
class SmartPointer
{
T* pointer;
public:
typedef T element_type;
explicit SmartPointer(T* t = nullptr)
: pointer(t)
{
}
T* operator->() const { return pointer; }
};
}
Q_DECLARE_SMART_POINTER_METATYPE(MyNS::SmartPointer)
so it is reasonable to expect that it behaves like a naked pointer in QML. However, I have concerns that this is one of those cases where they simply did not implement this simple thing well enough.

Is there a way to type an object parsed from json in qml

The json document is like this. I want to define a class for it in qml, just like the interface keyword in typescript does.
{
"ScannerID": "ID",
"Status": 1,
"SuccessRate": 0.999,
"Result": [{
"Codes": "result_11111",
"Positions": {
"CenterX": 10.0,
"CenterY": 10.0,
"Width": 100.0,
"Height": 100.0,
"Angle": 50
},
"Types": "QrCode"
},
//more items
]
}
I tried to define a qml class in file ScannerResult.qml
import QtQuick 2.4
QtObject {
property string pScannerID
property int pStatus
property real pSuccessRate
// how to define Result with strong type?
property ? pResult
function load(obj) {
pScannerID = obj.ScannerID
//......
}
}
then use it
...
ScannerResult {
id: scannerResult
}
function log(jsonstr) {
let obj = JSON.parse(jsonstr);
scannerResult.load(obj)
console.log(scannerResult.pScannerID) // it works
}
...
But it's hard to handle the vector of objects under "Result" key. Because qml doesn't allow to define class B under class A.
So does anyone have a good idea about how to define a strong-typed class to hold the object parsed from json in qml? Then I can use that class with auto-completion in qtcreator.
You can create a property var and assign it as a list/array/vector to hold pResult. To populate this list, you can create a "class B" Component, and dynamically create instances of this class them when parsing your JSON object.
Using your code structure, here is an updated version of ScannerResult.qml:
import QtQuick 2.4
QtObject {
property string pScannerID
property int pStatus
property real pSuccessRate
property var pResult: []
function load(obj) {
pScannerID = obj.ScannerID
//... etc
for (var element of obj.Result) {
let resultQtObject = resultComponent.createObject(this, {})
resultQtObject.pCodes = element.Codes
resultQtObject.pTypes = element.Types
resultQtObject.pPositions.pCenterX = element.Positions.CenterX
//... etc
pResult.push(resultQtObject)
}
}
property Component resultComponent: Component {
QtObject {
property string pCodes
property string pTypes
property QtObject pPositions: QtObject {
property real pCenterX
property real pCenterY
property real pWidth
property real pHeight
property int pAngle
}
}
}
}
Your updated log function works as follows:
function log(jsonstr) {
let obj = JSON.parse(jsonstr);
scannerResult.load(obj)
console.log(scannerResult.pScannerID) // it works
console.log(scannerResult.pResult[0].pPositions.pCenterX) // it works
}
Because you are dynamically creating instances of this Component at runtime, QtCreator cannot make auto-completion suggestions for it. As you mentioned your vector is unknown length, I believe that runtime instantiation is your only option, and thus there is no solution that can accommodate auto-completion.
One other thing to note is that if you are re-running log()/load() many times you will need to clean the pResult list and destroy() the dynamically created objects.

Abstract function qml

i want to create an abstract class with abstract functions in QML in theory some thing like this:
//abstract.qml
QtObject{
abstract function implementLater(var input);
}
Abstract{
//and here i have to implement it
function implementLater(var input){
console.log(input)
}
}
how can i do this in qml or maybe in c++ and register it to qml?
update :
here is what i'm trying to do, in my uvaluemask object i should have a function named mask which will be abstract and i call this for every child which will be different, you can call childs method directly i'm doing this right now and its working, but my component "UValueMask" is missing difinition for mask method,i mean i need to force users of UValueMask to define a mask method .
//UValueMask.qml
QtObject {
property string name:""
}
//singleton Object
UListObject{
property UValueMask timeMask :UValueMask{
id:timemask
name: "time"
function mask(input,splitter){
return innerObj.convertIntToTime(input,splitter)
}
}
property UValueMask dateMask: UValueMask{
id:datemask
name:"date"
function mask(input,splitter){
return innerObj.convertIntToDate(input,splitter)
}
}
}

bind something to property of type registered with qmlRegisterSingletonType

Is it possible to bind something to property of registered as singleton type? I can't find correct syntax to do something like
Item {
id: rootItem
MySingleton.anProperty: rootItem.width
}
(above results in: "Non-existent attached object")
MySingleton registered registered via
int qmlRegisterSingletonType(const char *uri, int versionMajor, int
versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine
* ) callback)
There is no MySingleton object or group property in the scope of Item and you cannot use this syntax to bind properties for external objects.
It is possible via the Binding element:
Binding {
target: MySingleton
property: 'anProperty'
value: rootItem.width
}
Since your title and question is ambiguous, maybe what you actually want to do is as simple as:
Item {
id: rootItem
width: MySingleton.anProperty
}

QML: Make property readonly in derived class

I'm pretty sure the answer is no, but I thought I should ask anyway. Can an inherited property be made readonly/constant in a pure QML derived class?
// Base.qml
Item {
property int foo: 42
}
// Derived.qml
Base {
foo: 99 // Make constant somehow!
}
I'm currently tackling this by detecting a change in foo and printing an error to the console, but that's hardly a mark of good API...
I have a menu item class which can represent menu entries, submenus, separators, etc. I need to specialise it's behaviour, but only for when it is in submenu mode. I can change the inheritance tree so I have a subclass for each menu type, but this seems silly as each subclass will only contain a single readonly property. However if there isn't another way, I will have to.
You can also transform foo as a readonly alias to an internal property _foo that you will use only for assignment in the derived class:
// Base.qml
Item {
id: base
readonly property alias foo: base._foo
property int _foo: 42 // only used for assignment in subclasses
}
To be used in another Component:
Item {
Base {
id: child0
}
Base {
id: child1
_foo: 99
}
Base {
id: child2
_foo: child1.foo
}
Component.onCompleted: {
console.log("child0:", child0.foo,"child1:", child1.foo, "child2:", child2.foo)
child1.foo = 5 // not allowed!
console.log("child0:", child0.foo,"child1:", child1.foo, "child2:", child2.foo)
}
}
Output:
child0: 42 child1: 99 child2: 99
TypeError: Cannot assign to read-only property "foo"
Note: of course it is still possible to modify _foo later. But if you remember to use it only for assignment, then you have the desired behavior with a readonly foo in the derived class.
Why not use inherited getter/setters for a private foo?
something like :
//Base.qml
Item
{
QtObject
{
id : privateFoo
property int foo : 42
}
function getFoo() {return privateFoo.foo;}
function setFoo(newFoo) { privateFoo.foo = newFoo; }
}
//derived.qml
Base
{
function getFoo() {return 99;}
function setFoo(newFoo) { /*do nothing */ }
}
(I haven't tested this code)

Resources