summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/qtobject/qml/qtobject/main.qml1
-rw-r--r--examples/qtobject/testobject.cpp2
-rw-r--r--examples/qtobject/testobject.h6
-rw-r--r--src/MetaObjectPublisher.qml266
-rw-r--r--src/qobject.js204
-rw-r--r--src/qtmetaobjectpublisher.cpp85
-rw-r--r--src/qtmetaobjectpublisher.h10
-rw-r--r--src/src.pro2
-rw-r--r--src/webchannel.js2
9 files changed, 467 insertions, 111 deletions
diff --git a/examples/qtobject/qml/qtobject/main.qml b/examples/qtobject/qml/qtobject/main.qml
index 7a63cd2..d00c9ac 100644
--- a/examples/qtobject/qml/qtobject/main.qml
+++ b/examples/qtobject/qml/qtobject/main.qml
@@ -49,6 +49,7 @@ import QtWebKit.experimental 1.0
Rectangle {
MetaObjectPublisher {
id: publisher
+ webChannel: webChannel
}
TestObject {
diff --git a/examples/qtobject/testobject.cpp b/examples/qtobject/testobject.cpp
index 4bcd92f..e18dbc0 100644
--- a/examples/qtobject/testobject.cpp
+++ b/examples/qtobject/testobject.cpp
@@ -18,6 +18,7 @@ void TestObject::setProp1(const QString& s)
p1 = s;
qWarning() << __func__ << p1;
emit sig1(1, 0.5, QStringLiteral("asdf"));
+ emit prop1Changed();
}
void TestObject::setProp2(const QString& s)
@@ -25,6 +26,7 @@ void TestObject::setProp2(const QString& s)
p2 = s;
qWarning() << __func__ << p2;
emit sig2();
+ emit prop2Changed(s);
}
QString TestObject::manyArgs(int a, float b, const QString& c) const
diff --git a/examples/qtobject/testobject.h b/examples/qtobject/testobject.h
index 39dc608..f3f1c03 100644
--- a/examples/qtobject/testobject.h
+++ b/examples/qtobject/testobject.h
@@ -7,8 +7,8 @@
class TestObject : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString prop1 READ prop1 WRITE setProp1)
- Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2)
+ Q_PROPERTY(QString prop1 READ prop1 WRITE setProp1 NOTIFY prop1Changed)
+ Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2 NOTIFY prop2Changed)
public:
explicit TestObject(QObject *parent = 0);
QString prop1() const { return "p1" + p1 + objectName(); }
@@ -21,6 +21,8 @@ signals:
void timeout();
void sig1(int a, float b, const QString& c);
void sig2();
+ void prop1Changed();
+ void prop2Changed(const QString& newValue);
public slots:
void startTimer(int millis)
diff --git a/src/MetaObjectPublisher.qml b/src/MetaObjectPublisher.qml
index 6ac588c..2b97c02 100644
--- a/src/MetaObjectPublisher.qml
+++ b/src/MetaObjectPublisher.qml
@@ -44,17 +44,57 @@ import Qt.labs.WebChannel 1.0
MetaObjectPublisherImpl
{
+ id: publisher
+
+ // The web channel this publisher works on.
+ property var webChannel
+
/**
* This map contains the registered objects indexed by their name.
*/
property variant registeredObjects
+ property var subscriberCountMap
+
+ // Map of object names to maps of signal names to an array of all their properties.
+ // The last value is an array as a signal can be the notify signal of multiple properties.
+ property var signalToPropertyMap
+
+ // Objects that changed their properties and are waiting for idle client.
+ // map of object name to map of signal name to arguments
+ property var pendingPropertyUpdates
+
+ // true when the client is idle, false otherwise
+ property bool clientIsIdle: false
+
+ // true when no property updates should be sent, false otherwise
+ property bool blockUpdates: false
+
+ // true when at least one client needs to be initialized,
+ // i.e. when a Qt.init came in which was not handled yet.
+ property bool pendingInit: false
+
+ // true when at least one client was initialized and thus
+ // the property updates have been initialized and the
+ // object info map set.
+ property bool propertyUpdatesInitialized: false
+
+ function convertQMLArgsToJSArgs(qmlArgs)
+ {
+ // NOTE: QML arguments is a map not an array it seems...
+ // so do the conversion manually
+ var args = [];
+ for (var i = 0; i < qmlArgs.length; ++i) {
+ args.push(qmlArgs[i]);
+ }
+ return args;
+ }
/**
* Handle the given WebChannel client request and potentially give a response.
*
* @return true if the request was handled, false otherwise.
*/
- function handleRequest(data, webChannel)
+ function handleRequest(data)
{
var message = typeof(data) === "string" ? JSON.parse(data) : data;
if (!message.data) {
@@ -64,60 +104,204 @@ MetaObjectPublisherImpl
if (!payload.type) {
return false;
}
- var object = payload.object ? registeredObjects[payload.object] : null;
-
- if (payload.type === "Qt.invokeMethod" && object) {
- var method = object[payload.method];
- webChannel.respond(message.id, method.apply(method, payload.args));
- } else if (payload.type === "Qt.connectToSignal" && object) {
- object[payload.signal].connect(function() {
- // NOTE: QML arguments is a map not an array it seems...
- // so do the conversion manually
- var args = [];
- for (var i = 0; i < arguments.length; ++i) {
- args.push(arguments[i]);
+
+ if (payload.object) {
+ var object = registeredObjects[payload.object];
+
+ if (payload.type === "Qt.invokeMethod") {
+ var method = object[payload.method];
+ if (method !== undefined) {
+ webChannel.respond(message.id, method.apply(method, payload.args));
+ return true;
}
- webChannel.sendMessage("Qt.signal", {
- object: payload.object,
- signal: payload.signal,
- args: args
- });
- });
- } else if (payload.type === "Qt.getProperty" && object) {
- webChannel.respond(message.id, object[payload.property]);
- } else if (payload.type === "Qt.setProperty" && object) {
- object[payload.property] = payload.value;
- } else if (payload.type === "Qt.getObjects") {
- webChannel.respond(message.id, registeredObjectInfos());
- } else if (payload.type === "Qt.Debug") {
+ return false;
+ }
+ if (payload.type === "Qt.connectToSignal") {
+ if (object.hasOwnProperty(payload.signal)) {
+ subscriberCountMap = subscriberCountMap || {};
+ subscriberCountMap[payload.object] = subscriberCountMap[payload.object] || {};
+
+ // if no one is connected, connect.
+ if (!subscriberCountMap[payload.object].hasOwnProperty(payload.signal)) {
+ object[payload.signal].connect(function() {
+ var args = convertQMLArgsToJSArgs(arguments);
+ webChannel.sendMessage("Qt.signal", {
+ object: payload.object,
+ signal: payload.signal,
+ args: args
+ });
+ });
+ subscriberCountMap[payload.object][payload.signal] = true;
+ }
+ return true;
+ }
+ return false;
+ }
+ if (payload.type === "Qt.setProperty") {
+ object[payload.property] = payload.value;
+ return true;
+ }
+ }
+ if (payload.type === "Qt.idle") {
+ clientIsIdle = true;
+ return true;
+ }
+ if (payload.type === "Qt.init") {
+ if (!blockUpdates) {
+ initializeClients();
+ } else {
+ pendingInit = true;
+ }
+ return true;
+ }
+ if (payload.type === "Qt.Debug") {
console.log("DEBUG: ", payload.message);
- } else {
- return false;
+ return true;
}
-
- return true;
+ return false;
}
function registerObjects(objects)
{
+ if (propertyUpdatesInitialized) {
+ console.error("Registered new object after initialization. This does not work!");
+ }
// joining a JS map and a QML one is not as easy as one would assume...
- for (var name in registeredObjects) {
- if (!objects[name]) {
- objects[name] = registeredObjects[name];
+ // NOTE: the extra indirection via "merged" is required, using registeredObjects directly
+ // does not work! this looks like a QML/v8 bug to me, but I could not find a
+ // standalone testcase which reproduces this behavior :(
+ var merged = registeredObjects;
+ for (var name in objects) {
+ if (!merged.hasOwnProperty(name)) {
+ merged[name] = objects[name];
+ }
+ }
+ registeredObjects = merged;
+ }
+
+ function initializeClients()
+ {
+ var objectInfos = classInfoForObjects(registeredObjects);
+ webChannel.sendMessage("Qt.init", objectInfos);
+ if (!propertyUpdatesInitialized) {
+ for (var objectName in objectInfos) {
+ var objectInfo = objectInfos[objectName];
+ var object = registeredObjects[objectName];
+ initializePropertyUpdates(objectName, objectInfo, object);
}
+ propertyUpdatesInitialized = true;
}
- registeredObjects = objects;
+ pendingInit = false;
}
- function registeredObjectInfos()
+ // This function goes through all properties of all objects and connects against
+ // their notify signal.
+ // When receiving a notify signal, it will send a Qt.propertyUpdate message to the
+ // server.
+ function initializePropertyUpdates(objectName, objectInfo, object)
{
- var objectInfos = {};
- for (var name in registeredObjects) {
- var object = registeredObjects[name];
- if (object) {
- objectInfos[name] = classInfoForObject(object);
+ for (var propertyIndex in objectInfo.properties) {
+ var propertyInfo = objectInfo.properties[propertyIndex];
+ var propertyName = propertyInfo[0];
+ var signalName = propertyInfo[1];
+
+ if (!signalName) // Property without NOTIFY signal
+ continue;
+
+ if (signalName === 1) {
+ /// signal name is optimized away, reconstruct the actual name
+ signalName = propertyName + "Changed";
+ }
+
+ signalToPropertyMap[objectName] = signalToPropertyMap[objectName] || {};
+ signalToPropertyMap[objectName][signalName] = signalToPropertyMap[objectName][signalName] || [];
+ var connectedProperties = signalToPropertyMap[objectName][signalName];
+ var numConnectedProperties = connectedProperties === undefined ? 0 : connectedProperties.length;
+
+ // Only connect for a property update once
+ if (numConnectedProperties === 0) {
+ (function(signalName) {
+ object[signalName].connect(function() {
+ pendingPropertyUpdates[objectName] = pendingPropertyUpdates[objectName] || {};
+ pendingPropertyUpdates[objectName][signalName] = arguments;
+ });
+ })(signalName);
+ }
+
+ if (connectedProperties.indexOf(propertyName) === -1) {
+ /// TODO: this ensures that a given property is only once in
+ /// the list of connected properties.
+ /// This happens when multiple clients are connected to
+ /// a single webchannel. A better place for the initialization
+ /// should be found.
+ connectedProperties.push(propertyName);
}
}
- return objectInfos;
+ }
+
+ function sendPendingPropertyUpdates()
+ {
+ if (blockUpdates || !clientIsIdle) {
+ return;
+ }
+
+ var data = [];
+ for (var objectName in pendingPropertyUpdates) {
+ var object = registeredObjects[objectName];
+ var signals = pendingPropertyUpdates[objectName];
+ var propertyMap = {};
+ for (var signalName in signals) {
+ var propertyList = signalToPropertyMap[objectName][signalName];
+ for (var propertyIndex in propertyList) {
+ var propertyName = propertyList[propertyIndex];
+ var propertyValue = object[propertyName];
+ propertyMap[propertyName] = propertyValue;
+ }
+ signals[signalName] = convertQMLArgsToJSArgs(signals[signalName]);
+ }
+ data.push({
+ object: objectName,
+ signals: signals,
+ propertyMap: propertyMap
+ });
+ }
+ pendingPropertyUpdates = {};
+ if (data.length > 0) {
+ webChannel.sendMessage("Qt.propertyUpdate", data);
+ clientIsIdle = false;
+ }
+ }
+
+ Component.onCompleted: {
+ // Initializing this in the property declaration is not possible and yields to "undefined"
+ signalToPropertyMap = {}
+ pendingPropertyUpdates = {}
+ registeredObjects = {}
+ }
+
+ onBlockUpdatesChanged: {
+ if (blockUpdates) {
+ return;
+ }
+
+ if (pendingInit) {
+ initializeClients();
+ } else {
+ sendPendingPropertyUpdates();
+ }
+ }
+
+ /**
+ * Aggregate property updates since we get multiple Qt.idle message when we have multiple
+ * clients. They all share the same QWebProcess though so we must take special care to
+ * prevent message flooding.
+ */
+ Timer {
+ id: propertyUpdateTimer
+ /// TODO: what is the proper value here?
+ interval: 50;
+ running: !publisher.blockUpdates && publisher.clientIsIdle;
+ repeat: true;
+ onTriggered: publisher.sendPendingPropertyUpdates()
}
}
diff --git a/src/qobject.js b/src/qobject.js
index d221525..012332e 100644
--- a/src/qobject.js
+++ b/src/qobject.js
@@ -42,17 +42,78 @@
function QObject(name, data, webChannel)
{
this.__id__ = name;
+ webChannel.objectMap[name] = this;
+
+ // List of callbacks that get invoked upon signal emission
this.__objectSignals__ = {};
- var methodsAndSignals = [];
- for (var i in data.methods)
- methodsAndSignals.push(data.methods[i]);
- for (var i in data.signals)
- methodsAndSignals.push(data.signals[i]);
+ // Cache of all properties, updated when a notify signal is emitted
+ this.__propertyCache__ = {};
var object = this;
- methodsAndSignals.forEach(function(method) {
+ // ----------------------------------------------------------------------
+
+ function addSignal(signal, isPropertyNotifySignal)
+ {
+ object[signal] = {
+ connect: function(callback) {
+ if (typeof(callback) !== "function") {
+ console.error("Bad callback given to connect to signal " + signal);
+ return;
+ }
+
+ object.__objectSignals__[signal] = object.__objectSignals__[signal] || [];
+ object.__objectSignals__[signal].push(callback);
+
+ if (!isPropertyNotifySignal) {
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ webChannel.exec({
+ type: "Qt.connectToSignal",
+ object: object.__id__,
+ signal: signal
+ });
+ }
+ }
+ // TODO: disconnect eventually
+ };
+ }
+
+ /**
+ * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
+ */
+ function invokeSignalCallbacks(signalName, signalArgs)
+ {
+ var connections = object.__objectSignals__[signalName];
+ if (connections) {
+ connections.forEach(function(callback) {
+ callback.apply(callback, signalArgs);
+ });
+ }
+ }
+
+ this.propertyUpdate = function(signals, propertyMap)
+ {
+ // update property cache
+ for (var propertyName in propertyMap) {
+ var propertyValue = propertyMap[propertyName];
+ object.__propertyCache__[propertyName] = propertyValue;
+ }
+
+ for (var signalName in signals) {
+ // Invoke all callbacks, as signalEmitted() does not. This ensures the
+ // property cache is updated before the callbacks are invoked.
+ invokeSignalCallbacks(signalName, signals[signalName]);
+ }
+ }
+
+ this.signalEmitted = function(signalName, signalArgs)
+ {
+ invokeSignalCallbacks(signalName, signalArgs);
+ }
+
+ function addMethod(method)
+ {
object[method] = function() {
var args = [];
var callback;
@@ -64,80 +125,123 @@ function QObject(name, data, webChannel)
}
webChannel.exec({"type": "Qt.invokeMethod", "object": object.__id__, "method": method, "args": args}, function(response) {
- if ((response != undefined) && callback) {
+ if ( (response !== undefined) && callback ) {
(callback)(response);
}
});
};
- });
+ }
- function connectToSignal(signal)
+ function bindGetterSetter(propertyInfo)
{
- object[signal].connect = function(callback) {
- if (typeof(callback) !== "function") {
- console.error("Bad callback given to connect to signal " + signal);
+ var propertyName = propertyInfo[0];
+ var notifySignal = propertyInfo[1];
+ // initialize property cache with current value
+ object.__propertyCache__[propertyName] = propertyInfo[2]
+
+ if (notifySignal) {
+ if (notifySignal === 1) {
+ /// signal name is optimized away, reconstruct the actual name
+ notifySignal = propertyName + "Changed";
+ }
+ addSignal(notifySignal, true);
+ }
+
+ object.__defineSetter__(propertyName, function(value) {
+ if (value === undefined) {
+ console.warn("Property setter for " + propertyName + " called with undefined value!");
return;
}
- object.__objectSignals__[signal] = object.__objectSignals__[signal] || [];
- webChannel.exec({"type": "Qt.connectToSignal", "object": object.__id__, "signal": signal});
- object.__objectSignals__[signal].push(callback);
- };
- }
- for (var i in data.signals) {
- var signal = data.signals[i];
- connectToSignal(data.signals[i]);
- }
+ object.__propertyCache__[propertyName] = value;
+ webChannel.exec({"type": "Qt.setProperty", "object": object.__id__, "property": propertyName, "value": value });
- function bindGetterSetter(property)
- {
- object.__defineSetter__(property, function(value) {
- webChannel.exec({"type": "Qt.setProperty", "object": object.__id__, "property": property, "value": value });
});
- object.__defineGetter__(property, function() {
- return (function(callback) {
- webChannel.exec({"type": "Qt.getProperty", "object": object.__id__, "property": property}, function(response) {
+ object.__defineGetter__(propertyName, function () {
+ return (function (callback) {
+ var propertyValue = object.__propertyCache__[propertyName];
+ if (propertyValue === undefined) {
+ // This shouldn't happen
+ console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
+ }
+
+ // TODO: A callback is not required here anymore, but is kept for backwards compatibility
+ if (callback !== undefined) {
if (typeof(callback) !== "function") {
console.error("Bad callback given to get property " + property);
return;
}
- callback(response);
- });
+ callback(propertyValue);
+ } else {
+ return propertyValue;
+ }
});
});
}
- for (i in data.properties) {
- bindGetterSetter(data.properties[i]);
- }
- for (i in data.enums) {
- object[i] = data.enums[i];
+ // ----------------------------------------------------------------------
+
+ data.methods.forEach(addMethod);
+
+ data.properties.forEach(bindGetterSetter);
+
+ data.signals.forEach(function(signal) { addSignal(signal, false); });
+
+ for (var name in data.enums) {
+ object[name] = data.enums[name];
}
}
window.setupQObjectWebChannel = function(webChannel, doneCallback)
{
+ // prevent multiple initialization which might happen with multiple webchannel clients.
+ var initialized = false;
+
webChannel.subscribe(
"Qt.signal",
function(payload) {
- var object = window[payload.object];
+ var object = webChannel.objectMap[payload.object];
if (object) {
- var connections = object.__objectSignals__[payload.signal];
- if (connections) {
- connections.forEach(function(callback) {
- callback.apply(callback, payload.args);
- });
- }
+ object.signalEmitted(payload.signal, payload.args);
+ } else {
+ console.warn("Unhandled signal: " + payload.object + "::" + payload.signal);
}
}
);
- webChannel.exec({type:"Qt.getObjects"}, function(payload) {
- for (var objectName in payload) {
- var data = payload[objectName];
- var object = new QObject(objectName, data, webChannel);
- window[objectName] = object;
+
+ webChannel.subscribe(
+ "Qt.propertyUpdate",
+ function(payload) {
+ for (var i in payload) {
+ var data = payload[i];
+ var object = webChannel.objectMap[data.object];
+ if (object) {
+ object.propertyUpdate(data.signals, data.propertyMap);
+ } else {
+ console.warn("Unhandled property update: " + data.object + "::" + data.signal);
+ }
+ }
+ setTimeout(function() { webChannel.exec({type: "Qt.idle"}); }, 0);
}
- if (doneCallback) {
- doneCallback();
+ );
+
+ webChannel.subscribe(
+ "Qt.init",
+ function(payload) {
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+ for (var objectName in payload) {
+ var data = payload[objectName];
+ var object = new QObject(objectName, data, webChannel);
+ window[objectName] = object;
+ }
+ if (doneCallback) {
+ doneCallback();
+ }
+ setTimeout(function() { webChannel.exec({type: "Qt.idle"}); }, 0);
}
- });
+ );
+
+ webChannel.exec({type:"Qt.init"});
};
diff --git a/src/qtmetaobjectpublisher.cpp b/src/qtmetaobjectpublisher.cpp
index 1646434..8e871e0 100644
--- a/src/qtmetaobjectpublisher.cpp
+++ b/src/qtmetaobjectpublisher.cpp
@@ -45,11 +45,33 @@
#include <QMetaObject>
#include <QMetaProperty>
-QtMetaObjectPublisher::QtMetaObjectPublisher(QObject *parent)
- : QObject(parent)
+static const QString KEY_SIGNALS = QStringLiteral("signals");
+static const QString KEY_METHODS = QStringLiteral("methods");
+static const QString KEY_PROPERTIES = QStringLiteral("properties");
+static const QString KEY_ENUMS = QStringLiteral("enums");
+
+QtMetaObjectPublisher::QtMetaObjectPublisher(QQuickItem *parent)
+ : QQuickItem(parent)
{
}
+QVariantMap QtMetaObjectPublisher::classInfoForObjects(const QVariantMap &objectMap) const
+{
+ QVariantMap ret;
+ QMap<QString, QVariant>::const_iterator it = objectMap.constBegin();
+ while (it != objectMap.constEnd()) {
+ QObject* object = it.value().value<QObject*>();
+ if (object) {
+ const QVariantMap &info = classInfoForObject(object);
+ if (!info.isEmpty()) {
+ ret[it.key()] = info;
+ }
+ }
+ ++it;
+ }
+ return ret;
+}
+
QVariantMap QtMetaObjectPublisher::classInfoForObject(QObject *object) const
{
QVariantMap data;
@@ -57,19 +79,54 @@ QVariantMap QtMetaObjectPublisher::classInfoForObject(QObject *object) const
qWarning("null object given to MetaObjectPublisher - bad API usage?");
return data;
}
- QStringList qtSignals, qtMethods, qtProperties;
+ QVariantList qtSignals, qtMethods;
+ QVariantList qtProperties;
QVariantMap qtEnums;
const QMetaObject* metaObject = object->metaObject();
- for (int i = 0; i < metaObject->propertyCount(); ++i)
- qtProperties.append(metaObject->property(i).name());
+ QSet<int> notifySignals;
+ QSet<QString> properties;
+ for (int i = 0; i < metaObject->propertyCount(); ++i) {
+ const QMetaProperty &prop = metaObject->property(i);
+ QVariantList propertyInfo;
+ const QString &propertyName = QString::fromLatin1(prop.name());
+ propertyInfo.append(propertyName);
+ properties << propertyName;
+ if (prop.hasNotifySignal()) {
+ notifySignals << prop.notifySignalIndex();
+ const int numParams = prop.notifySignal().parameterCount();
+ if (numParams > 1) {
+ qWarning("Notify signal for property '%s' has %d parameters, expected zero or one.",
+ prop.name(), numParams);
+ }
+ propertyInfo.append(QString::fromLatin1(prop.notifySignal().name()));
+ } else {
+ if (!prop.isConstant()) {
+ qWarning("Property '%s'' of object '%s' has no notify signal and is not constant, "
+ "value updates in HTML will be broken!",
+ prop.name(), object->metaObject()->className());
+ }
+ propertyInfo.append(QString());
+ }
+ propertyInfo.append(prop.read(object));
+ qtProperties.append(QVariant::fromValue(propertyInfo));
+ }
for (int i = 0; i < metaObject->methodCount(); ++i) {
- QMetaMethod method = metaObject->method(i);
- QString signature = method.methodSignature();
- QString name = signature.left(signature.indexOf("("));
+ if (notifySignals.contains(i)) {
+ continue;
+ }
+ const QMetaMethod &method = metaObject->method(i);
+ //NOTE: This will not work for overloaded methods/signals.
+ //NOTE: this must be a string, otherwise it will be converted to '{}' in QML
+ const QString &name = QString::fromLatin1(method.name());
+ if (properties.contains(name)) {
+ // optimize: Don't send the getter method, it gets overwritten by the
+ // property on the client side anyways.
+ continue;
+ }
if (method.access() == QMetaMethod::Public)
- qtMethods << signature << name;
+ qtMethods << name;
if (method.methodType() == QMetaMethod::Signal)
- qtSignals << signature << name;
+ qtSignals << name;
}
for (int i = 0; i < metaObject->enumeratorCount(); ++i) {
QMetaEnum enumerator = metaObject->enumerator(i);
@@ -79,9 +136,9 @@ QVariantMap QtMetaObjectPublisher::classInfoForObject(QObject *object) const
}
qtEnums[enumerator.name()] = values;
}
- data["signals"] = qtSignals;
- data["methods"] = qtMethods;
- data["properties"] = qtProperties;
- data["enums"] = qtEnums;
+ data[KEY_SIGNALS] = qtSignals;
+ data[KEY_METHODS] = qtMethods;
+ data[KEY_PROPERTIES] = QVariant::fromValue(qtProperties);
+ data[KEY_ENUMS] = qtEnums;
return data;
}
diff --git a/src/qtmetaobjectpublisher.h b/src/qtmetaobjectpublisher.h
index 573d120..0c19374 100644
--- a/src/qtmetaobjectpublisher.h
+++ b/src/qtmetaobjectpublisher.h
@@ -44,15 +44,19 @@
#include <QObject>
#include <QVariantMap>
+#include <QQuickItem>
-class QtMetaObjectPublisher : public QObject
+class QObjectWrapper;
+
+// NOTE: QQuickItem inheritance required to enable QML item nesting (i.e. Timer in MetaObjectPublisher)
+class QtMetaObjectPublisher : public QQuickItem
{
Q_OBJECT
public:
- explicit QtMetaObjectPublisher(QObject *parent = 0);
+ explicit QtMetaObjectPublisher(QQuickItem *parent = 0);
+ Q_INVOKABLE QVariantMap classInfoForObjects(const QVariantMap &objects) const;
Q_INVOKABLE QVariantMap classInfoForObject(QObject *object) const;
};
#endif // QTMETAOBJECTPUBLISHER_H
-
diff --git a/src/src.pro b/src/src.pro
index 8792e04..ee77e4b 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -3,7 +3,7 @@ include(src.pri)
TEMPLATE = lib
TARGET = qwebchannel
TARGETPATH = Qt/labs/WebChannel
-QT += qml
+QT += qml quick
CONFIG += qt plugin
TARGET = $$qtLibraryTarget($$TARGET)
diff --git a/src/webchannel.js b/src/webchannel.js
index 6141268..1e6603c 100644
--- a/src/webchannel.js
+++ b/src/webchannel.js
@@ -123,4 +123,6 @@ var QWebChannel = function(baseUrl, initCallback)
{
channel.send({"data" : {"type" : "Qt.Debug", "message" : message}});
};
+
+ this.objectMap = {};
};