Modifying a QVariantMap with JavaScript - qt

The QtWebKit Bridge documentation states the following -
Compound (JSON) objects JavaScript
compound objects, also known as JSON
objects, are variables that hold a
list of key-value pairs, where all the
keys are strings and the values can
have any type. This translates very
well to QVariantMap, which is nothing
more than a QMap of QString to
QVariant. The seamless conversion
between JSON objects and QVariantMap
allows for a very convenient way of
passing arbitrary structured data
between C++ and the JavaScript
environment. The native QObject has to
make sure that compound values are
converted to QVariantMaps and
QVariantLists, and JavaScript is
guaranteed to receive them in a
meaningful way. Note that types that
are not supported by JSON, such as
JavaScript functions and
getters/setters, are not converted.
Does this mean that, while JavaScript is able to read a QVariantList, it is unable to modify it?
I've tried adding a getter and setter for test purposes -
Q_PROPERTY( QVariantMap Settings READ GetShadowSettings WRITE SetShadowSettings )
The getter function is being called when the JavaScript wants to access any data from the QVariantMap. Unfortunately, when the JavaScript attempts to update the QVariantMap, the getter function is called again (rather than the setter function).
I can modify the data using a simple helper function such as -
Q_INVOKABLE void Update( QString key, QVariant value ) {
settings[key] = value;
}
I was just wondering if there was a way of doing this without the need for a helper function?

I use QVariantMap for PhantomJS and it works just fine. For example, WebPage#viewportSize is just QVariantMap in its implementation. The usual problem is you can't try to update one of its property only, e.g. viewportSize.width = 300. You have to pass back an object, e.g.:
viewportSize = { width: 300, height: 200 }.
If you need to able to do the former, the only (ugly) workaround that might work is to create a helper object, e.g. Size in the above case, which has the proper setter and getter for the individual property and handle the housekeeping of bridging.

Related

How are QML property dependencies determined? (and how to manipulate them)

A property that is bound to an expression is updated when something in the expression changes.
This is called a dependency.
EDIT:
To clarify:
I'm interested in details on how Qt determines a list of dependencies
Dependencies on simple bindings such as x: y are more or less obvious
The question is about less obvious cases such as x: myItemId["y"] and x: myFunction(z) where myFunction(p) { if (p) return myItemId.y }
Sometimes QML engine is able to detect change even if the expression is a function call without arguments, other times it cannot do that (for example mapToItem(item,0,0).x).
Another example of imperfection is that setting JS array item value without reassigning the array itself doesn't normally produce onXxxxxChanged signal or update anything referring to that array value.
An expression with unused result (x: {myForcedDependency; return myActualCalculation()}) is sometimes suggested to force a dependency.
According to this KDAB article and Qt source code, a binding expression is not only evaluated but any properties "accessed" during that are "captured" in something called a "guard", then every guard properties onXxxxxChanged() signals are connected, but actual details of this process are unclear.
So my questions are:
Are there any defined rules of dependency resolution?
How does it really work?
How deeply does QQmlEngine/V8 scan "accesses" into functions called by the binding expression and what may prevent it from doing that?
Is dependency-detection only based on the first attempt at property resolution?
Are all possible code paths checked even if execution never reached there yet?
Are non-trivial accesses determined in those cases, such as object["property"] syntax?
What if some unexecuted code is (currently) erroneous (and does not produce an error but cannot be properly analyzed)?
How can the dependency resolution process be influenced?
Is there a way to avoid or block a dependency?
As far as I understand an intermediate "filter" property that only actually changes its value when it's necessary to update is the intended way, correct?
Is there an intended way to force a dependency?
Is manually emitting "XxxxxChanged" signal the correct/supported way to force an update?
Is adding an unused reference a legal/intended way to do it or undefined behavior based on the current implementation quirk?
Any information would be useful, although I did read the official documentation on QML properties, QML bindings and JavaScript expressions and didn't find any concrete explanation - if you refer to the official documentation please quote relevant parts.
Please note that I'm not asking you to test if any of this works on your system, but if it's supposed to work - if it can be relied on
It makes more sense if you just think of bindings as connected signals. If you have something like this:
property int x: y
It's just like doing this in C++:
connect(this, &SomeClass::yChanged, [this]() { x = y; });
The same goes for expressions:
property int x: y + z
would be equivalent to:
connect(this, &SomeClass::yChanged, [this]() { x = y + z; });
connect(this, &SomeClass::zChanged, [this]() { x = y + z; });
And the same with function calls:
property int x: someFunc()
function someFunc() {
return y;
}
The only time bindings don't update is when there is no onChanged signal to connect to, or the onChanged signal doesn't get emitted for whatever reason.
property int x: cppObject.invokable()
In the above case, the only property that x is able to connect to is cppObject. If invokable references other properties, those won't be connected to x and therefore the binding won't update.
property var array: [1, 2, 3]
property int x: array[0]
function updateArray() {
array = [2, 4, 6]
arrayChanged() // Manually call the onChanged signal to update `x`
}
var properties do not notify by default (for some reason). So in this case, we have to manually call the changed signal, but then the binding will still work.
For a property var, onChanged is emitted only when there is a direct assignment to the var itself, not to a property of some object it refers to. This also excludes modification of array contents, as JS arrays are JS objects.
This is consistent with QML being a JS extension. In JS you can modify prop in this code, because const only means variable will always refer to the same object:
const variable = { prop: 'value' };
Just like only direct assignments to const variables are regarded as change attempts when JS enforces const, QML only emits onChanged on direct assignments to a property var.
Coming from C++, I like to compare JS variables with object value to pointers:
SomeClass *variable = new SomeClass();
SomeClass *const variable = new SomeClass(); //const pointer to mutable object
Again, a change in the referred object is not regarded as a change in the variable.

What parts of an object are stored when it's saved into a session variable [duplicate]

The title is obvious, I need to know if methods are serialized along with object instances in C#, I know that they don't in Java but I'm a little new to C#. If they don't, do I have to put the original class with the byte stream(serialized object) in one package when sending it to another PC? Can the original class be like a DLL file?
No. The type information is serialized, along with state. In order to deserialize the data, your program will need to have access to the assemblies containing the types (including methods).
It may be easier to understand if you've learned C. A class like
class C
{
private int _m;
private int _n;
int Meth(int p)
{
return _m + _n + p;
}
}
is essentially syntactic sugar for
typedef struct
{
int _m;
int _n;
// NO function pointers necessary
} C;
void C_Meth(C* obj, int p)
{
return obj->_m + obj->_n + p;
}
This is essentially how non-virtual methods are implemented in object-oriented languages. The important thing here is that methods are not part of the instance data.
Methods aren't serialized.
I don't know about your scenario, but putting in a library (assembly / dll) and using that in the other end to deserialize gets you all.
Ps. you probably should create some ask some more questions with the factors involved in your scenario. If you are intending to dynamically send & run the code, you can create awful security consequences.
I was confused when .NET first came up with serialization. I think it came from the fact that most books and guides mention that it allows you to serialize your 'objects' as XML and move them around, the fact is that you are actually hydrating the values of your object so you can dehydrate them latter. at no point your are saving your whole object to disk since that would require the dll and is not contained in the XML file.

When, where and why use namespace when registering custom types for Qt

Similar questions have been raised multiple times, but I'm focussing on the namespace and pointer issues.
MyClass.h
namespace foo {
class MyClass {
MyClass();
};
QDataStream &operator<<(QDataStream &out, const MyClass & myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);
} // namespace foo
Q_DECLARE_METATYPE(foo::MyClass) // #1
Q_DECLARE_METATYPE(foo::MyClass*) // #2
fooMyClass.cpp (so many permutations):
MyClass::MyClass()
{
qRegisterMetaType<MyClass>("MyClass"); // #3
qRegisterMetaType<MyClass*>("MyClass*"); // #4
qRegisterMetaType<MyClass>("foo::MyClass"); // #5
qRegisterMetaType<MyClass*>("foo::MyClass*"); // #6
qRegisterMetaType<foo::MyClass>("foo::MyClass"); // #7
qRegisterMetaType<foo::MyClass*>("foo::MyClass*"); // #8
qRegisterMetaType<MyClass>(); // #9
qRegisterMetaType<MyClass*>(); // #10
qRegisterMetaType<foo::MyClass>(); // #11
qRegisterMetaType<foo::MyClass*>(); // #12
// same for qRegisterMetaTypeStreamOperators<T>();
}
So my question is, when and why is it required to provide the namespace and/or the pointer variant if I intend to use the custom objects for signals and slots (potentially as reference and pointer) inside as well as outside the namespace. Do I always have to fully qualify the namespace?
I'm referring to Qt5 in this answer. Qt4 doesn't go well with this use case.
Data stream operators
Data stream operators are not required for your type if you only intend to use it in signals and slots. They are required if you want to do some serialization.
Pointers, references and values
Qt considers MyClass and MyClass* two different unrelated types. You should declare, register and use them separately. Using const MyClass & argument type is compatible with MyClass in Qt meta-object system. Note that using MyClass and MyClass* meta types simultaneously in one program is unusual and can cause mistakes and confusion. You should choose one of the options and use it throughout the program. Also passing pointers to slots is not recommended because it causes unsolvable ownership problem. So I recommend to use passing by const reference (which sometimes will be converted to passing by value internally in Qt signal-slot system). If MyClass objects contain massive data, you should implement implicit data sharing using QSharedDataPointer.
Declaring a meta type
First of all, you always need to declare your meta type:
Q_DECLARE_METATYPE(foo::MyClass)
It works at compile time, so there are no limitations on how you refer to your class. The following code will work as well:
using namespace foo;
Q_DECLARE_METATYPE(MyClass)
Registering a meta type
Now you need to register your classes. Theoretically, you need to specify all strings that you want to use to refer to your type, i.e.:
qRegisterMetaType<foo::MyClass>("MyClass");
qRegisterMetaType<foo::MyClass>("foo::MyClass");
It doesn't matter how you refer to MyClass in the template argument. The following code will work similarly:
using namespace foo;
qRegisterMetaType<MyClass>("MyClass");
qRegisterMetaType<MyClass>("foo::MyClass");
For example, the "MyClass" and "foo::MyClass" strings are used to identify argument types when you refer to your signals and slots like SIGNAL(signal1(MyClass)).
New signal and slot syntax
If you using new signal slot syntax with pointers to member functions, you need to do only one registration with arbitrary string argument. It seems that it is intended to make it work even without any registrations. This part of the docs instructs to only add Q_DECLARE_METATYPE, in opposite to this that requires qRegisterMetaType(). Unfortunately, now in my Qt installation it works only with direct connections. Queued connections still require at least one registration call.
Implicit registration of class without namespace
I was experimenting with some variants of registration in Qt 5.1 and found out that Qt automatically registers aliases without namespace. So if you write
qRegisterMetaType<foo::MyClass>("foo::MyClass");
, Qt will additionally automatically register "MyClass" alias. So, after executing this statement you will be able to refer to your type as MyClass and foo::MyClass. There is no information in the documentation about how Qt handles namespaces. We could assume that this behavior is intended and will not be removed in next versions but I wouldn't rely on that. The following code makes implicit registration obvious:
qRegisterMetaType<foo::MyClass>("foo::MyClass");
qRegisterMetaType<bar::MyClass>("MyClass");
Qt 5.1 says:
QMetaType::registerTypedef: Binary compatibility break -- Type name 'MyClass' previously registered as typedef of 'MyClass' [1030], now registering as typedef of 'bar::MyClass' [1032].
Qt 4.8 works without error (it seems that this behavior is not yet introduced in this version).

What's the Nicest Way to do This? (Qt and Enum style arguments)

In Qt, it is common to see something similar to the following:
QSettings obj3(QSettings::SystemScope, "MySoft", "Star Runner");
The important bit is the QSettings::SystemScope, which is an enum.
I want to have a settings provider (pay no attention to the previous example here, it has nothing to do with the following), with a get/set property.
Settings.set(Settings::refreshRate)
The refreshRate has to link to a key (string), and a default value (variant).
Should I make an enum and two dicts for the key and default values, or make a struct and a whole bunch of variables that encapsulate the settings I need? Should I try something else?
Thanks!
Edit!
This is what I did.
// Interface
class Settings {
public:
static QVariant get(Setting setting);
static void set(Setting setting, QVariant value);
const static Setting serverRefreshRate;
const static Setting serverReportTimeout;
};
// Implementation
const Setting Settings::serverRefreshRate = { "server/refreshRate", 10000 };
const Setting Settings::serverReportTimeout = { "server/reportTimeout", 1000 };
Well I guess since you're using enum which most likely will be easily castable to numbers from to 0 to N-1 I guess just storing variants and strings in two vectors or one vector of pairs would work just fine.
There's also another question though -- how to initialize all of that and how you will be adding new settings to it. I can suggest two methods - first one writing a bunch of function calls with arguments: enum, string, variant. Thus way though if programmer adds another value to enum he can forget to call initializing function. The other way is to create function (or maybe two) which will do switch on all enum values (without default case) and will return pair of string and variant. You can turn on the compiler warning about all enum values being processed in switch and thus way control if you forget to implement some of them in that function. And then initialize your structures using loop on all of enum values. These initializing functions should be called somewhere near the beginning of your program (before reading settings initially).
Well, that's my thoughts on it, you are free to try some different ways though.

Storing pointers using QListWidgetItem::setData

I have a QListWidget of calendars. Each QListWidgetItem is logically associated with an instance of Calendar, which is a class that belongs to the Model side of the application.
Can I store this association in the form of a pointer using QListWidgetItem::setData? When I attempt to do this, I get the following error:
error: 'QVariant::QVariant(void*)' is private
There is another constructor for void*: QVariant::QVariant(int typeOrUserType, const void * copy) where you should pass an unique integer to represent the pointer type.
But as stated by the documentation, you could declare your pointer type with Q_DECLARE_METATYPE(Calendar*) and use QVariant::fromValue<Calendar*>(...) and QVariant::value<Calendar*>() to store and retrieve the value.
Or instead, because you are using a QListWidget instead of a regular model, you can just subclass QListWidgetItem, and add a Calendar* member variable with the required accessors, to avoid the overhead of using QVariant.
I would suggest looking at this solution as well, which I think is quite elegant:
(there are minor syntax errors, but you will spot them quickly or the compiler will issue an error)
https://web.archive.org/web/20171025163314/http://blog.bigpixel.ro/2010/04/storing-pointer-in-qvariant/

Resources