├── CppRedisTest.cpp ├── CppRedisTest.h ├── MainForm.ui.qml ├── QMLRedisInterface.cpp ├── QMLRedisInterface.h ├── RedisInterface.cpp ├── RedisInterface.h ├── main.cpp ├── main.qml ├── qml.qrc ├── qt-redis.pro └── qt-redis.pro.user /CppRedisTest.cpp: -------------------------------------------------------------------------------- 1 | #include "CppRedisTest.h" 2 | 3 | CppRedisTest::CppRedisTest(QObject *parent) : 4 | QObject(parent), 5 | _redisInterface(new RedisInterface("http://localhost:7379/", this)), 6 | _timer(new QTimer()) 7 | { 8 | _redisInterface->subscribeToEvent("event:from:redis", "eventFromRedis"); 9 | _redisInterface->publishEvent("eventFromCpp", "event:from:cpp"); 10 | _redisInterface->publishProperty("propertyFromCpp", "property:from:cpp"); 11 | _redisInterface->subscribeToProperty("property:from:redis", "propertyFromRedis"); 12 | 13 | _timer->setInterval(1000); 14 | _timer->setSingleShot(false); 15 | connect(_timer, SIGNAL(timeout()), this, SLOT(update())); 16 | _timer->start(); 17 | } 18 | 19 | QString CppRedisTest::propertyFromCpp() const 20 | { 21 | return _propertyFromCpp; 22 | } 23 | 24 | QString CppRedisTest::propertyFromRedis() const 25 | { 26 | return _propertyFromRedis; 27 | } 28 | 29 | void CppRedisTest::eventFromRedis() 30 | { 31 | qDebug() << "(C++) eventFromRedis() called"; 32 | } 33 | 34 | void CppRedisTest::setPropertyFromCpp(QString arg) 35 | { 36 | if (_propertyFromCpp != arg) { 37 | _propertyFromCpp = arg; 38 | emit propertyFromCppChanged(arg); 39 | } 40 | } 41 | 42 | void CppRedisTest::setPropertyFromRedis(QString arg) 43 | { 44 | if (_propertyFromRedis != arg) { 45 | _propertyFromRedis = arg; 46 | emit propertyFromRedisChanged(arg); 47 | } 48 | } 49 | 50 | void CppRedisTest::update() 51 | { 52 | emit eventFromCpp(); 53 | setPropertyFromCpp(QString(rand())); 54 | _redisInterface->get("test:meta:value", RedisInterface::getSlot(this, "getRequestResponse(QString, QVariant)")); 55 | } 56 | 57 | void CppRedisTest::getRequestResponse(QString key, QVariant value) 58 | { 59 | qDebug() << "(C++) Get request response:" << key << "==" << value; 60 | } 61 | -------------------------------------------------------------------------------- /CppRedisTest.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPREDISTEST_H 2 | #define CPPREDISTEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "RedisInterface.h" 8 | 9 | class CppRedisTest : public QObject 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(QString propertyFromCpp READ propertyFromCpp WRITE setPropertyFromCpp NOTIFY propertyFromCppChanged ) 13 | Q_PROPERTY(QString propertyFromRedis READ propertyFromRedis WRITE setPropertyFromRedis NOTIFY propertyFromRedisChanged) 14 | 15 | public: 16 | explicit CppRedisTest(QObject *parent = 0); 17 | 18 | QString propertyFromCpp() const; 19 | QString propertyFromRedis() const; 20 | 21 | signals: 22 | 23 | void eventFromCpp(); 24 | void propertyFromCppChanged(QString arg); 25 | void propertyFromRedisChanged(QString arg); 26 | 27 | public slots: 28 | 29 | void eventFromRedis(); 30 | void setPropertyFromCpp(QString arg); 31 | void setPropertyFromRedis(QString arg); 32 | 33 | void update(); 34 | 35 | void getRequestResponse(QString key, QVariant value); 36 | 37 | private: 38 | 39 | RedisInterface* _redisInterface; 40 | QTimer* _timer; 41 | 42 | QString _propertyFromCpp; 43 | QString _propertyFromRedis; 44 | }; 45 | 46 | #endif // CPPREDISTEST_H 47 | -------------------------------------------------------------------------------- /MainForm.ui.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 1.1 3 | 4 | Rectangle { 5 | property alias btnPublishEvent1: btnPublishEvent1 6 | property alias btnPublishEvent2: btnPublishEvent2 7 | property alias btnGetAsync: btnGetAsync 8 | property alias btnGetSync: btnGetSync 9 | property alias btnSetRemoteDouble: btnSetRemoteDouble 10 | property alias btnSetRemoteString: btnSetRemoteString 11 | property alias btnClearAll: btnClearAll 12 | 13 | Grid { 14 | spacing: 10 15 | columns: 2 16 | 17 | property real buttonWidth: (parent.width / 2) 18 | 19 | Button { 20 | id: btnPublishEvent1 21 | width: parent.buttonWidth 22 | text: "PUBLISH remote:subscribed:event1" 23 | } 24 | 25 | Button { 26 | id: btnPublishEvent2 27 | width: parent.buttonWidth 28 | text: "PUBLISH remote:subscribed:event2" 29 | } 30 | 31 | Button { 32 | id: btnGetAsync 33 | width: parent.buttonWidth 34 | text: "Asynchronous GET get:async" 35 | } 36 | 37 | Button { 38 | id: btnGetSync 39 | width: parent.buttonWidth 40 | text: "Synchronous GET get:sync" 41 | } 42 | 43 | Button { 44 | id: btnSetRemoteDouble 45 | width: parent.buttonWidth 46 | text: "SET remote:published:double" 47 | } 48 | 49 | Button { 50 | id: btnSetRemoteString 51 | width: parent.buttonWidth 52 | text: "SET remote:published:string" 53 | } 54 | 55 | Button { 56 | id: btnClearAll 57 | width: parent.buttonWidth 58 | text: "Clear all" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /QMLRedisInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "QMLRedisInterface.h" 2 | 3 | /*! 4 | \mainclass 5 | \class QMLRedisInterface 6 | \inmodule RedisInterface 7 | \brief A simple wrapper class that enables a RedisInterface to be instantiated as a QML Item. 8 | 9 | This QQuickItem-based wrapper provides call-throughs to a RedisInterface instance, which 10 | provides a common interface for QObject-based objects to communicate via Redis + HTTP. 11 | 12 | Subscribed/published events and properties are defined in a declarative fashion using the \c{variant} properties 13 | \l{subscribedEvents}, \l{publishedEvents}, \l{subscribedProperties}, and \l{publishedProperties}. 14 | 15 | Example: 16 | \code 17 | // Remote properties we wish to pull from Redis automatically 18 | subscribedProperties: [ 19 | { remote: "remote:published:double", local: "localSubscribedDouble" }, 20 | { remote: "remote:published:string", local: "localSubscribedString" } 21 | ] 22 | 23 | // Local properties we wish to push to Redis automatically 24 | publishedProperties: [ 25 | { local: "localPublishedDouble", remote: "remote:subscribed:double" }, 26 | { local: "localPublishedString", remote: "remote:subscribed:string" } 27 | ] 28 | 29 | // Remote events to which we wish to subscribe, triggering local signals/methods automatically 30 | subscribedEvents: [ 31 | { remote: "remote:signal:trigger", local: "localSignal" }, 32 | { remote: "remote:method:trigger", local: "localMethod" }, 33 | { remote: "remote:wildcard*", local: "localWildcard" } 34 | ] 35 | 36 | // Local events (signals) we wish to publish to Redis when they are emitted 37 | publishedEvents: [ 38 | { local: "publishedSignal1", remote: "remote:subscribed:event1" }, 39 | { local: "publishedSignal2", remote: "remote:subscribed:event2" } 40 | ] 41 | 42 | // Local properties that are 'pulled' from Redis 43 | property double localSubscribedDouble 44 | property string localSubscribedString 45 | 46 | // Local properties that are 'pushed' to Redis 47 | property double localPublishedDouble: 0.00 48 | property string localPublishedString: "." 49 | 50 | // Signals we wish to publish to Redis 51 | signal publishedSignal1 52 | signal publishedSignal2 53 | 54 | // Signal to trigger when "remote:signal:trigger" is emitted by Redis 55 | signal localSignal 56 | onLocalSignal: { 57 | console.log("(QML) localSignal() emitted"); 58 | } 59 | 60 | // Method to invoke when "remote:method:trigger" is emitted by Redis 61 | function localMethod() { 62 | console.log("(QML) localMethod() called"); 63 | } 64 | 65 | // Method to invoke when "remote:wildcard*" is emitted by Redis 66 | function localWildcard() { 67 | console.log("(QML) localWildcard() called"); 68 | } 69 | \endcode 70 | 71 | \sa RedisInterface 72 | */ 73 | 74 | QMLRedisInterface::QMLRedisInterface(QQuickItem *parent) : 75 | QQuickItem(parent) 76 | { 77 | setFlag(ItemHasContents, true); 78 | } 79 | 80 | QVariant QMLRedisInterface::subscribedEvents() const 81 | { 82 | return _subscribedEvents; 83 | } 84 | 85 | QVariant QMLRedisInterface::get(const QString &key) const 86 | { 87 | if(this->isComponentComplete()) 88 | return _redisInterface->get(key); 89 | 90 | return QVariant(); 91 | } 92 | 93 | void QMLRedisInterface::get(const QString &key, QJSValue callback) const 94 | { 95 | if(this->isComponentComplete()) 96 | _redisInterface->get(key, callback); 97 | } 98 | 99 | bool QMLRedisInterface::subscribeToEvent(const QString& remoteEventName, const QString& localMethodName) 100 | { 101 | if(this->isComponentComplete()) 102 | return _redisInterface->subscribeToEvent(remoteEventName, localMethodName); 103 | 104 | return false; 105 | } 106 | 107 | void QMLRedisInterface::init() 108 | { 109 | _redisInterface = new RedisInterface(serverUrl(), this); 110 | 111 | // Subscribe to events. 112 | QListIterator subscribedEventsIter = subscribedEvents().toList(); 113 | while(subscribedEventsIter.hasNext()) 114 | { 115 | QVariantMap element = subscribedEventsIter.next().toMap(); 116 | 117 | QString remoteEventName = element.value("remote").toString(); 118 | QString localMethodName = QString(element.value("local").toString()); 119 | 120 | _redisInterface->subscribeToEvent(remoteEventName, localMethodName); 121 | } 122 | 123 | // Publish events. 124 | QListIterator publishedEventsIter = publishedEvents().toList(); 125 | while(publishedEventsIter.hasNext()) 126 | { 127 | QVariantMap element = publishedEventsIter.next().toMap(); 128 | 129 | QString localSignalName = element.value("local").toString(); 130 | QString remoteEventName = element.value("remote").toString(); 131 | 132 | _redisInterface->publishEvent(localSignalName, remoteEventName); 133 | } 134 | 135 | // Subscribe to properties. 136 | QListIterator subscribedPropertiesIter = subscribedProperties().toList(); 137 | while(subscribedPropertiesIter.hasNext()) 138 | { 139 | QVariantMap element = subscribedPropertiesIter.next().toMap(); 140 | 141 | QString remotePropertyName = element.value("remote").toString(); 142 | QString localPropertyName = element.value("local").toString(); 143 | 144 | _redisInterface->subscribeToProperty(remotePropertyName, localPropertyName); 145 | } 146 | 147 | // Publish properties. 148 | QListIterator publishedPropertiesIter = publishedProperties().toList(); 149 | while(publishedPropertiesIter.hasNext()) 150 | { 151 | QVariantMap element = publishedPropertiesIter.next().toMap(); 152 | 153 | QString remotePropertyName = element.value("remote").toString(); 154 | QString localPropertyName = element.value("local").toString(); 155 | 156 | _redisInterface->publishProperty(localPropertyName, remotePropertyName); 157 | } 158 | } 159 | 160 | QString QMLRedisInterface::serverUrl() const 161 | { 162 | return _serverUrl; 163 | } 164 | 165 | void QMLRedisInterface::set(const QString &key, const QVariant &value) 166 | { 167 | if(this->isComponentComplete()) 168 | _redisInterface->set(key, value); 169 | } 170 | 171 | QVariant QMLRedisInterface::publishedProperties() const 172 | { 173 | return _publishedProperties; 174 | } 175 | 176 | QVariant QMLRedisInterface::subscribedProperties() const 177 | { 178 | return _subscribedProperties; 179 | } 180 | 181 | QVariant QMLRedisInterface::publishedEvents() const 182 | { 183 | return _publishedEvents; 184 | } 185 | 186 | void QMLRedisInterface::setSubscribedEvents(const QVariant &value) 187 | { 188 | if(_subscribedEvents != value) 189 | { 190 | _subscribedEvents = value; 191 | emit subscribedEventsChanged(value); 192 | } 193 | } 194 | 195 | void QMLRedisInterface::setPublishedProperties(const QVariant &value) 196 | { 197 | if(_publishedProperties != value) 198 | { 199 | _publishedProperties = value; 200 | emit publishedPropertiesChanged(value); 201 | } 202 | } 203 | 204 | void QMLRedisInterface::setSubscribedProperties(const QVariant& value) 205 | { 206 | if(_subscribedProperties != value) 207 | { 208 | _subscribedProperties = value; 209 | emit subscribedPropertiesChanged(value); 210 | } 211 | } 212 | 213 | void QMLRedisInterface::setPublishedEvents(const QVariant& value) 214 | { 215 | if(_publishedEvents != value) 216 | { 217 | _publishedEvents = value; 218 | emit publishedEventsChanged(value); 219 | } 220 | } 221 | 222 | void QMLRedisInterface::setServerUrl(const QString& value) 223 | { 224 | if(_serverUrl != value) 225 | { 226 | _serverUrl = value; 227 | emit serverUrlChanged(value); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /QMLRedisInterface.h: -------------------------------------------------------------------------------- 1 | #ifndef QMLREDISINTERFACE_H 2 | #define QMLREDISINTERFACE_H 3 | 4 | #include 5 | #include 6 | #include "RedisInterface.h" 7 | 8 | class QMLRedisInterface : public QQuickItem 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString serverUrl READ serverUrl WRITE setServerUrl NOTIFY serverUrlChanged ) 12 | Q_PROPERTY(QVariant subscribedProperties READ subscribedProperties WRITE setSubscribedProperties NOTIFY subscribedPropertiesChanged) 13 | Q_PROPERTY(QVariant publishedProperties READ publishedProperties WRITE setPublishedProperties NOTIFY publishedPropertiesChanged ) 14 | Q_PROPERTY(QVariant subscribedEvents READ subscribedEvents WRITE setSubscribedEvents NOTIFY subscribedEventsChanged ) 15 | Q_PROPERTY(QVariant publishedEvents READ publishedEvents WRITE setPublishedEvents NOTIFY publishedEventsChanged ) 16 | 17 | public: 18 | 19 | explicit QMLRedisInterface(QQuickItem *parent = 0); 20 | 21 | QString serverUrl() const; 22 | QVariant subscribedProperties() const; 23 | QVariant publishedProperties() const; 24 | QVariant subscribedEvents() const; 25 | QVariant publishedEvents() const; 26 | 27 | Q_INVOKABLE QVariant get(const QString& key) const; 28 | Q_INVOKABLE void get(const QString& key, QJSValue callback) const; 29 | Q_INVOKABLE bool subscribeToEvent(const QString& remoteEventName, const QString& localMethodName); 30 | 31 | Q_INVOKABLE void init(); 32 | 33 | signals: 34 | 35 | void serverUrlChanged(const QString& value); 36 | void subscribedPropertiesChanged(const QVariant& value); 37 | void publishedPropertiesChanged(const QVariant& value); 38 | void subscribedEventsChanged(const QVariant& value); 39 | void publishedEventsChanged(const QVariant& value); 40 | 41 | public slots: 42 | 43 | void set(const QString& key, const QVariant& value); 44 | 45 | void setServerUrl(const QString& value); 46 | void setSubscribedProperties(const QVariant& value); 47 | void setPublishedProperties(const QVariant& value); 48 | void setSubscribedEvents(const QVariant& value); 49 | void setPublishedEvents(const QVariant& value); 50 | 51 | private: 52 | 53 | QString _serverUrl; 54 | QVariant _subscribedProperties; 55 | QVariant _publishedProperties; 56 | QVariant _subscribedEvents; 57 | QVariant _publishedEvents; 58 | 59 | RedisInterface* _redisInterface; 60 | }; 61 | 62 | QML_DECLARE_TYPE(QMLRedisInterface) 63 | 64 | #endif // QMLREDISINTERFACE_H 65 | -------------------------------------------------------------------------------- /RedisInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "RedisInterface.h" 2 | 3 | /*! 4 | \mainclass 5 | \class RedisInterface 6 | \inmodule RedisInterface 7 | \brief Provides a common interface for QObject-based objects to communicate via Redis + HTTP. 8 | 9 | Redis is a networked, in-memory, key-value data store with an inbuilt publish/subscribe messaging system. The RedisInterface class 10 | enables binding of Redis values to/from Qt properties, and Redis events to/from Qt methods/signals/slots. 11 | 12 | When a \c{RedisInterface} object is instantiated, any Qt properties of the parent class defined using the \c{Q_PROPERTY} macro are available 13 | to be synchronised (in either direction) with corresponding values on the Redis server. Similarly, any Qt \c{signals} can be set to automatically 14 | \c{PUBLISH} to Redis events, and any \c{slots}/\c{signals}/ordinary methods can be set up to receive Redis events via \c{SUBSCRIBE}/\c{PSUBSCRIBE}. 15 | 16 | In addition to event/property binding, the \c{RedisInterface} supports a nominal set of 'once-off' commands such as \c{GET},\c{SET}, and \c{PUBLISH}. 17 | This set of commands will be expanded in the future as required. 18 | 19 | A QML wrapper is provided by the QMLRedisInterface class. 20 | 21 | \sa QMLRedisInterface 22 | */ 23 | 24 | /*! 25 | * \brief Constructor. Enables this object to interact with the Redis (webdis) server at \a{serverUrl} 26 | * and map Redis events/properties to \c{signals}/\c{slots}/properties on the given \c{QObject}-based \a{parent} (and vice versa). 27 | */ 28 | RedisInterface::RedisInterface(QString serverUrl, QObject *parent) : 29 | QObject(parent), 30 | _serverUrl(serverUrl), 31 | _networkInterface(new QNetworkAccessManager(this)) 32 | { 33 | // Make sure Redis server URL ends with a slash. 34 | if(!_serverUrl.endsWith("/")) 35 | _serverUrl.append("/"); 36 | 37 | // Fail hard if no parent is given. 38 | if(parent == NULL) 39 | throw std::runtime_error("RedisInterface::RedisInterface(): RedisInterface constructor must be passed a non-null QObject-based parent!"); 40 | } 41 | 42 | /*! 43 | * \brief Inspects the meta-object of the given \a{object} and returns the method with the given \a{signature}. 44 | * Returns an invalid \l{QMetaMethod} if the method could not be found. 45 | */ 46 | QMetaMethod RedisInterface::getMethod(QObject *object, QString signature) 47 | { 48 | if(object) 49 | return object->metaObject()->method(object->metaObject()->indexOfMethod(object->metaObject()->normalizedSignature(signature.toStdString().c_str()))); 50 | else 51 | std::cerr << "RedisInterface::getMethod(): Can't find method on NULL!"; 52 | 53 | return QMetaMethod(); 54 | } 55 | 56 | /*! 57 | * \brief Inspects the meta-object of the given \a{object} and returns the \c{signal} with the given \a{signature}. 58 | * Returns an invalid \l{QMetaMethod} if the method could not be found, or if the method is not a \c{signal}. 59 | */ 60 | QMetaMethod RedisInterface::getSignal(QObject *object, QString signature) 61 | { 62 | if(object) 63 | return object->metaObject()->method(object->metaObject()->indexOfSignal(object->metaObject()->normalizedSignature(signature.toStdString().c_str()))); 64 | else 65 | std::cerr << "RedisInterface::getSignal(): Can't find signal on NULL!"; 66 | 67 | return QMetaMethod(); 68 | } 69 | 70 | /*! 71 | * \brief Inspects the meta-object of the given \a{object} and returns the \c{signal} with the given \a{signature}. 72 | * Returns an invalid \l{QMetaMethod} if the method could not be found, or if the method is not a \c{slot}. 73 | */ 74 | QMetaMethod RedisInterface::getSlot(QObject *object, QString signature) 75 | { 76 | if(object) 77 | return object->metaObject()->method(object->metaObject()->indexOfSlot(object->metaObject()->normalizedSignature(signature.toStdString().c_str()))); 78 | else 79 | std::cerr << "RedisInterface::getSlot(): Can't find slot on NULL!"; 80 | 81 | return QMetaMethod(); 82 | } 83 | 84 | /*! 85 | * \brief Inspects the meta-object of the given \a{object} and returns the \c{Q_PROPERTY} with the given \a{propertyName}. 86 | * Returns an invalid \l{QMetaProperty} if the property could not be found. 87 | */ 88 | QMetaProperty RedisInterface::getProperty(QObject *object, QString propertyName) 89 | { 90 | if(object) 91 | return object->metaObject()->property(object->metaObject()->indexOfProperty(propertyName.toStdString().c_str())); 92 | else 93 | std::cerr << "RedisInterface::getProperty(): Can't find property on NULL!"; 94 | 95 | return QMetaProperty(); 96 | } 97 | 98 | /*! 99 | * \brief Adds a subscription to the Redis event \a{remoteEventName}, which will cause \a{localMethodName} (if valid) to be invoked 100 | * each time \a{remoteEventName} occurs. 101 | */ 102 | bool RedisInterface::subscribeToEvent(QString remoteEventName, QString localMethodName) 103 | { 104 | // Append parentheses if missing. 105 | if(!localMethodName.endsWith("()")) 106 | localMethodName += "()"; 107 | 108 | // Locate target method in parent's meta-object. 109 | QMetaMethod localMethod = RedisInterface::getMethod(parent(), localMethodName); 110 | 111 | if(localMethod.isValid()) 112 | { 113 | // Set remoteEventName to call localMethod each time it is triggered. 114 | addEventSubscription(remoteEventName, parent(), localMethod); 115 | _subscribedEvents.insert(remoteEventName, localMethod); 116 | 117 | return true; 118 | } 119 | else 120 | { 121 | std::cerr << "RedisInterface::subscribeToEvent(): Target method " << localMethodName.toStdString() << " invalid or not found!" << std::endl; 122 | return false; 123 | } 124 | } 125 | 126 | /*! 127 | * \brief Adds a publication of \a{localSignalName}, which (if valid) will cause a \a{remoteEventName} event to be sent to Redis 128 | * each time the local signal is emitted is emitted. 129 | */ 130 | void RedisInterface::publishEvent(QString localSignalName, QString remoteEventName) 131 | { 132 | // Append parentheses if missing. 133 | if(!localSignalName.endsWith("()")) 134 | localSignalName.append("()"); 135 | 136 | // Locate the source signal in parent's meta-obeject. 137 | QMetaMethod localSignal = RedisInterface::getSignal(parent(), localSignalName); 138 | 139 | if(localSignal.isValid()) 140 | { 141 | qDebug() << "[RedisInterface] Connecting local signal" << localSignalName << "to remote event" << remoteEventName; 142 | 143 | // Set handlePublishedEvent() to be called each time localSignalName is emitted.. 144 | QMetaMethod publishSlot = RedisInterface::getSlot(this, "handlePublishedEvent()"); 145 | connect(parent(), localSignal, this, publishSlot); 146 | 147 | _publishedEvents.insert(localSignalName, remoteEventName); 148 | } 149 | else 150 | { 151 | std::cerr << "RedisInterface::publishEvent(): Source signal " << localSignalName.toStdString() << " invalid or not found!" << std::endl; 152 | } 153 | } 154 | 155 | /*! 156 | * \brief Adds a subscription to the Redis property \a{remotePropertyName}, which will cause the value of \a{localPropertyName} to be 157 | * automatically updated each time the remote property changes. 158 | */ 159 | void RedisInterface::subscribeToProperty(QString remotePropertyName, QString localPropertyName) 160 | { 161 | qDebug() << "[RedisInterface] Mapping remote property" << remotePropertyName << "to local property" << localPropertyName; 162 | 163 | // Set handleSubscribedPropertyUpdate() to be called each time the '_changed' event for remotePropertyName is received. 164 | QMetaMethod propertyUpdateSlot = RedisInterface::getSlot(this, "handleSubscribedPropertyUpdate()"); 165 | addEventSubscription(remotePropertyName + "_changed", this, propertyUpdateSlot); 166 | 167 | _subscribedProperties.insert(remotePropertyName, RedisInterface::getProperty(parent(), localPropertyName)); 168 | } 169 | 170 | /*! 171 | * \brief Adds a publication of the local property \a{localPropertyName}, which will cause the Redis property \a{remotePropertyName} to be 172 | * automatically updated each time the local property changes. In addition, a "remotePropertyName_changed" Redis event will be generated to notify 173 | * any subscribers to the updated property. 174 | */ 175 | void RedisInterface::publishProperty(QString localPropertyName, QString remotePropertyName) 176 | { 177 | // Locate the local property on the parent's meta-object. 178 | QMetaProperty property = RedisInterface::getProperty(parent(), localPropertyName); 179 | 180 | if(property.isValid() && property.hasNotifySignal()) 181 | { 182 | // Set the property's NOTIFY signal to call handlePublishedPropertyUpdate() automatically. 183 | QMetaMethod notifySignal = property.notifySignal(); 184 | QMetaMethod setSlot = RedisInterface::getSlot(this, "handlePublishedPropertyUpdate()"); 185 | 186 | qDebug() << "[RedisInterface] Mapping local property" << localPropertyName << "to remote property" << remotePropertyName << "(via notify signal" << notifySignal.name() + "())"; 187 | connect(parent(), notifySignal, this, setSlot); 188 | _publishedProperties.insert(localPropertyName, remotePropertyName); 189 | } 190 | else 191 | { 192 | std::cerr << "[RedisInterface] Error: Property " << localPropertyName.toStdString() << " is invalid or has no NOTIFY signal!" << std::endl; 193 | } 194 | } 195 | 196 | /*! 197 | * \brief Subscribes to the Redis event \a{remoteEventName}, connecting it to the \a{targetMethod} method belonging to \a{targetObject}. 198 | */ 199 | void RedisInterface::addEventSubscription(QString remoteEventName, QObject* targetObject, QMetaMethod targetMethod) 200 | { 201 | if(targetObject != NULL && targetMethod.isValid() && targetMethod.enclosingMetaObject() == targetObject->metaObject()) 202 | { 203 | // Use pattern subscribe for remote events containing wildcards. 204 | QString subscribeCommand = remoteEventName.contains("*") ? "PSUBSCRIBE" : "SUBSCRIBE"; 205 | 206 | qDebug() << "[RedisInterface] Connecting remote event" << remoteEventName << "to local method" << targetMethod.name() << "using" << subscribeCommand; 207 | 208 | // Make the subscription request via Redis (over HTTP via webdis). 209 | QNetworkReply* reply = _networkInterface->get(QNetworkRequest(QUrl(_serverUrl + subscribeCommand + "/" + remoteEventName))); 210 | QMetaMethod readyRead = RedisInterface::getSignal(reply, "readyRead()"); 211 | connect(reply, readyRead, targetObject, targetMethod); 212 | connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); 213 | } 214 | else 215 | { 216 | if(targetObject == NULL) 217 | { 218 | std::cerr << "[RedisInterface] addEventSubscription(): targetObject must not be NULL!" << std::endl; 219 | } 220 | else 221 | { 222 | if(!targetMethod.isValid()) 223 | std::cerr << "[RedisInterface] addEventSubscription(): targetMethod is invalid!" << std::endl; 224 | 225 | if(targetMethod.enclosingMetaObject() != targetObject->metaObject()) 226 | std::cerr << "[RedisInterface] addEventSubscription(): targetMethod does not belong to targetObject!" << std::endl; 227 | } 228 | } 229 | } 230 | 231 | /*! 232 | * \brief Handles an incoming Redis event, invoking any corresponding methods/signals/slots that have been bound to the event. 233 | */ 234 | void RedisInterface::handleSubscribedEvent() 235 | { 236 | // Retrieve the network reply. 237 | QNetworkReply* reply = qobject_cast(sender()); 238 | 239 | try { 240 | QJsonObject doc = QJsonDocument::fromJson(reply->readAll()).object(); 241 | 242 | // Subscription message format is {"SUBSCRIPTION":["message/pmessage","message_name","message_value"]}. 243 | QJsonArray data = doc.value(doc.keys().at(0)).toArray(); 244 | QString eventType = data.at(0).toString(); 245 | 246 | // Invoke the method if the event is a proper message (as opposed to the initial subscription reply). 247 | if(eventType == "message" || eventType == "pmessage") 248 | { 249 | QString eventName = data.at(1).toString(); 250 | _subscribedEvents.value(eventName).invoke(parent()); 251 | } 252 | } 253 | catch(std::exception& e) 254 | { 255 | if(reply) 256 | std::cerr << "RedisInterface::receiveRemoteEvent(): Error: " << reply->errorString().toStdString() << std::endl; 257 | else 258 | std::cerr << "RedisInterface::receiveRemoteEvent(): Error: Network reply is NULL!" << std::endl; 259 | } 260 | } 261 | 262 | /*! 263 | * \brief Handles a published event (signal) being emitted, in turn publishing any Redis events that have been bound to the event. 264 | */ 265 | void RedisInterface::handlePublishedEvent() 266 | { 267 | // Retrieve the signal sender. 268 | QString senderSignal = parent()->metaObject()->method(senderSignalIndex()).methodSignature(); 269 | 270 | qDebug() << "[RedisInterface] Publishing" << senderSignal << "--->" << _publishedEvents.value(senderSignal); 271 | publish(_publishedEvents.value(senderSignal), senderSignal); 272 | } 273 | 274 | /*! 275 | * \brief Handles an incoming 'changed' event for a subscribed property. Sets the corresponding local property to the new value. 276 | */ 277 | void RedisInterface::handleSubscribedPropertyUpdate() 278 | { 279 | // Retrieve the network reply. 280 | QNetworkReply* reply = qobject_cast(sender()); 281 | 282 | try 283 | { 284 | // Parse the JSON response. 285 | QJsonObject doc = QJsonDocument::fromJson(reply->readAll()).object(); 286 | 287 | // Subscription message format is {"SUBSCRIPTION":["message/pmessage","message_name","message_value"]}. 288 | QJsonArray data = doc.value(doc.keys().at(0)).toArray(); 289 | QString eventType = data.at(0).toString(); 290 | 291 | // Invoke the method if the event is a proper message (as opposed to the initial subscription reply). 292 | if(eventType == "message" || eventType == "pmessage") 293 | { 294 | QString eventName = data.at(1).toString(); 295 | 296 | if(eventName.endsWith("_changed")) 297 | { 298 | QString propertyName = eventName.left(eventName.length() - 8); 299 | QVariant value = data.at(2).toVariant(); 300 | qDebug() << "[RedisInterface] Remote property" << propertyName << "changed to" << value; 301 | _subscribedProperties.value(propertyName).write(parent(), value); 302 | } 303 | else 304 | { 305 | std::cerr << "[RedisInterface] Error: Unexpected property changed message format: " << eventName.toStdString() << std::endl; 306 | } 307 | } 308 | } 309 | catch(std::exception& e) 310 | { 311 | std::cerr << "[RedisInterface] handleSubscribedPropertyUpdate(): Error: " << e.what(); 312 | } 313 | } 314 | 315 | /*! 316 | * \brief Handles a published property update, in turn updating the corresponding Redis value. 317 | */ 318 | void RedisInterface::handlePublishedPropertyUpdate() 319 | { 320 | // Retrieve the name of the NOTIFY signal that triggered this slot. 321 | QString senderSignal = parent()->metaObject()->method(senderSignalIndex()).name(); 322 | 323 | if(senderSignal.endsWith("Changed")) 324 | { 325 | QString localPropertyName = senderSignal.left(senderSignal.length() - QString("Changed").length()); 326 | QVariant newValue = parent()->property(localPropertyName.toStdString().c_str()); 327 | 328 | // Update the Redis value. 329 | set(_publishedProperties.value(localPropertyName), newValue); 330 | } 331 | else 332 | { 333 | std::cerr << "[RedisInterface] handlePublishedPropertyUpdate(): Unexpected sender signal " << senderSignal.toStdString() << std::endl; 334 | } 335 | } 336 | 337 | /*! 338 | * \brief Handles an asynchronous GET request response, calling the appropriate JavaScript callback. 339 | */ 340 | void RedisInterface::handleGetRequestResponse_JavaScript() 341 | { 342 | // Retrieve the network reply. 343 | QNetworkReply* reply = qobject_cast(sender()); 344 | 345 | try 346 | { 347 | // Parse the JSON response. 348 | QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); 349 | 350 | // Find the JavaScriptCallback we tacked on earlier, retrieve the callback, and call it with the value we requested. 351 | QJSValue callback = dynamic_cast(reply->userData(0))->callback; 352 | QJSValue value = callback.engine()->toScriptValue(doc.object().value("GET").toVariant()); 353 | callback.call(QJSValueList() << value); 354 | 355 | reply->deleteLater(); 356 | } 357 | catch(std::exception& e) 358 | { 359 | if(reply == NULL) 360 | std::cerr << "[RedisInterface] handleGetRequestResponse_JavaScript(): Network reply is NULL!" << std::endl; 361 | else 362 | std::cerr << "[RedisInterface] handleGetRequestResponse_JavaScript(): Error: " << e.what() << std::endl; 363 | } 364 | } 365 | 366 | void RedisInterface::handleGetRequestResponse_MetaMethod() 367 | { 368 | // Retrieve the network reply. 369 | QNetworkReply* reply = qobject_cast(sender()); 370 | 371 | try 372 | { 373 | // Parse the JSON response. 374 | QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); 375 | 376 | // Find the MetaMethodCallback we tacked on earlier and call it with the value we requested. 377 | MetaMethodCallback* metaMethodCallback = dynamic_cast(reply->userData(0)); 378 | QVariant value = doc.object().value("GET").toVariant(); 379 | metaMethodCallback->callback.invoke(metaMethodCallback->requester, QGenericArgument("key", &metaMethodCallback->key), QGenericArgument("value", value.data())); 380 | 381 | reply->deleteLater(); 382 | } 383 | catch(std::exception& e) 384 | { 385 | if(reply == NULL) 386 | std::cerr << "[RedisInterface] handleGetRequestResponse_MetaMethod(): Network reply is NULL!" << std::endl; 387 | else 388 | std::cerr << "[RedisInterface] handleGetRequestResponse_MetaMethod(): Error: " << e.what() << std::endl; 389 | } 390 | } 391 | 392 | /*! 393 | * \brief SETs the Redis property with the given \a{key} to the given \a{value}. 394 | */ 395 | void RedisInterface::set(QString key, const QVariant &value) 396 | { 397 | QNetworkReply* setReply = _networkInterface->get(QNetworkRequest(QUrl(_serverUrl + "SET/" + key + "/" + value.toString()))); 398 | publish(QString(key + "_changed"), value); 399 | 400 | connect(setReply, SIGNAL(finished()), setReply, SLOT(deleteLater())); 401 | } 402 | 403 | /*! 404 | * \brief Performs a synchronous GET request for the Redis value with the given \a{key}. This method blocks the calling thread (which, in QML, is the main thread), therefore 405 | * it is highly recommended that you use one of the asynchronous overloads instead. 406 | */ 407 | QVariant RedisInterface::get(QString key) const 408 | { 409 | // Perform the HTTP GET request for the key. 410 | QNetworkReply* reply = _networkInterface->get(QNetworkRequest(QUrl(_serverUrl + "GET/" + key))); 411 | 412 | // Spin up an event loop and wait for the response. 413 | QEventLoop waitLoop; 414 | connect(reply, SIGNAL(finished()), &waitLoop, SLOT(quit())); 415 | waitLoop.exec(); 416 | 417 | // Parse the response and return the requested value to the caller. 418 | QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); 419 | if(doc.isObject()) 420 | return doc.object().value("GET").toVariant(); 421 | else 422 | std::cerr << "[RedisInterface] Synchronous get(): Bad response from server!" << std::endl; 423 | 424 | return ""; 425 | } 426 | 427 | /*! 428 | * \brief Performs an asynchronous GET request for the Redis value with the given \a{key}. When a response is received, the given JavaScript \a{callback} 429 | * will be invoked. 430 | */ 431 | void RedisInterface::get(QString key, QJSValue callback) const 432 | { 433 | qDebug() << "[RedisInterface] Performing asynchronus GET request for" << key << "with callback" << callback.toString(); 434 | 435 | // Perform the GET request. 436 | QNetworkReply* reply = _networkInterface->get(QNetworkRequest(QUrl(_serverUrl + "GET/" + key))); 437 | connect(reply, SIGNAL(readyRead()), this, SLOT(handleGetRequestResponse_JavaScript())); 438 | connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); 439 | 440 | // Tack the callback onto the reply as a UserData object. This will be unwrapped in handleGetRequestResponse_JavaScript(). 441 | reply->setUserData(0, new JavaScriptCallback(callback)); 442 | } 443 | 444 | /*! 445 | * \brief Performs an asynchronous GET request for the Redis value with the given \a{key}. When a response is received, the given C++ method \a{callback} 446 | * will be invoked. 447 | */ 448 | void RedisInterface::get(QString key, QMetaMethod callback) const 449 | { 450 | qDebug() << "[RedisInterface] Performing asynchronus GET request for " << key << "with callback" << callback.name(); 451 | 452 | // Perform the GET request. 453 | QNetworkReply* reply = _networkInterface->get(QNetworkRequest(QUrl(_serverUrl + "GET/" + key))); 454 | connect(reply, SIGNAL(readyRead()), this, SLOT(handleGetRequestResponse_MetaMethod())); 455 | connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); 456 | 457 | // Tack the callback onto the reply as a UserData object. this will be unwrapped in handleGetRequestResponse_MetaMethod(). 458 | reply->setUserData(0, new MetaMethodCallback(parent(), key, callback)); 459 | } 460 | 461 | /*! 462 | * \brief Performs a single-shot PUBLISH command via Redis. 463 | */ 464 | void RedisInterface::publish(QString remoteEventName, QVariant value) 465 | { 466 | // qDebug() << "[RedisInterface] Publishing event" << remoteEventName << "via Redis, with value" << value.toString(); 467 | QNetworkReply* reply = _networkInterface->get(QNetworkRequest(QUrl(_serverUrl + "PUBLISH/" + remoteEventName + "/" + value.toString()))); 468 | connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); 469 | } 470 | -------------------------------------------------------------------------------- /RedisInterface.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISINTERFACE_H 2 | #define REDISINTERFACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | class RedisInterface : public QObject 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | 28 | /** Constructor. Requires a valid QObject-based parent to map events to/from. */ 29 | RedisInterface(QString serverUrl, QObject* parent); 30 | 31 | /** Convenience methods for retrieving methods/signals/slots/properties from a given QObject's meta-object. */ 32 | static QMetaMethod getMethod(QObject* object, QString signature); 33 | static QMetaMethod getSignal(QObject* object, QString signature); 34 | static QMetaMethod getSlot(QObject* object, QString signature); 35 | static QMetaProperty getProperty(QObject* object, QString propertyName); 36 | 37 | public slots: 38 | 39 | /** Subscribes to the given Redis event, causing localMethodName to be called automatically. */ 40 | bool subscribeToEvent(QString remoteEventName, QString localMethodName); 41 | 42 | /** Publishes the given local signal, generating the given Redis event automatically. */ 43 | void publishEvent(QString localSignalName, QString remoteEventName); 44 | 45 | /** Subscribes to the given Redis property, causing localPropertyName to be updated automatically. */ 46 | void subscribeToProperty(QString remotePropertyName, QString localPropertyName); 47 | 48 | /** Publishes the given local property, causing remotePropertyName to be updated automatically on Redis. */ 49 | void publishProperty(QString localPropertyName, QString remotePropertyName); 50 | 51 | /** SETs the given Redis property to the given value. */ 52 | void set(QString key, const QVariant& value); 53 | 54 | /** Performs a synchronous (thread-blocking) GET request for the given Redis key. */ 55 | QVariant get(QString key) const; 56 | 57 | /** Performs an asynchronous GET request, calling the given JavaScript callback upon completion. */ 58 | void get(QString key, QJSValue callback) const; 59 | 60 | /** Performs an asynchronous GET request, calling the given C++ method upon completion. */ 61 | void get(QString key, QMetaMethod callback) const; 62 | 63 | /** Performs a single-shot PUBLISH event to Redis, with the given event name and value. */ 64 | void publish(QString remoteEventName, QVariant value); 65 | 66 | private slots: 67 | 68 | /** Adds a subscription to the given remote event, hooking it up to the given method belonging to the given QObject subclass. */ 69 | void addEventSubscription(QString remoteEventName, QObject *targetObject, QMetaMethod targetMethod); 70 | 71 | /** Private handler slots to catch remote and local events. */ 72 | void handleSubscribedEvent(); 73 | void handlePublishedEvent(); 74 | void handleSubscribedPropertyUpdate(); 75 | void handlePublishedPropertyUpdate(); 76 | void handleGetRequestResponse_JavaScript(); 77 | void handleGetRequestResponse_MetaMethod(); 78 | 79 | private: 80 | 81 | /** Packages up a JavaScript callback such that it can be tacked onto a QNetworkReply for later invocation. */ 82 | class JavaScriptCallback : public QObjectUserData 83 | { 84 | public: 85 | JavaScriptCallback(QJSValue callback) : QObjectUserData(), callback(callback) {} 86 | QJSValue callback; 87 | }; 88 | 89 | /** Packages up a QMetaMethod callback such that it can be tacked onto a QNetworkReply for later invocation. */ 90 | class MetaMethodCallback: public QObjectUserData 91 | { 92 | public: 93 | MetaMethodCallback(QObject* requesterObj, QString requestedKey, QMetaMethod callbackMethod) : 94 | QObjectUserData(), 95 | requester(requesterObj), 96 | key(requestedKey), 97 | callback(callbackMethod) 98 | {} 99 | 100 | QObject* requester; 101 | QString key; 102 | QMetaMethod callback; 103 | }; 104 | 105 | /** URL of the Redis HTTP (webdis) server (eg. "http://localhost:7379/") */ 106 | QString _serverUrl; 107 | 108 | /** Object responsible for making network requests to Redis. */ 109 | QNetworkAccessManager* _networkInterface; 110 | 111 | /** Mapping of Redis events to local methods on the parent object. */ 112 | QMap _subscribedEvents; 113 | 114 | /** Mapping of parent signals to Redis event names. */ 115 | QMap _publishedEvents; 116 | 117 | /** Mapping of Redis properties to local Q_PROPERTYs on the parent object. */ 118 | QMap _subscribedProperties; 119 | 120 | /** Mapping of parent property names to Redis properties. */ 121 | QMap _publishedProperties; 122 | }; 123 | 124 | #endif // REDISINTERFACE_H 125 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "QMLRedisInterface.h" 4 | #include "CppRedisTest.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QGuiApplication app(argc, argv); 9 | 10 | qmlRegisterType("Redis", 1, 0, "RedisInterface"); 11 | 12 | CppRedisTest test; 13 | 14 | QQmlApplicationEngine engine; 15 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 16 | 17 | return app.exec(); 18 | } 19 | -------------------------------------------------------------------------------- /main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Window 2.2 3 | import Redis 1.0 4 | 5 | Window { 6 | visible: true 7 | width: 640 8 | height: 480 9 | title: qsTr("Hello World") 10 | 11 | MainForm { 12 | id: form 13 | anchors.fill: parent 14 | 15 | btnPublishEvent1.onClicked: redis.publishedSignal1() 16 | btnPublishEvent2.onClicked: redis.publishedSignal2() 17 | btnGetAsync.onClicked: { 18 | redis.get("get:async", function(value){ 19 | redis.asynchronousGetVariable = value; 20 | }) 21 | } 22 | btnGetSync.onClicked: { 23 | redis.synchronousGetVariable = redis.get("get:sync"); 24 | } 25 | btnSetRemoteDouble.onClicked: { 26 | redis.set("remote:published:double", Math.random()); 27 | } 28 | btnSetRemoteString.onClicked: { 29 | redis.set("remote:published:string", Math.random().toString(36).substring(7)); 30 | } 31 | btnClearAll.onClicked: { 32 | redis.localPublishedDouble = 0.0; 33 | redis.localPublishedString = "."; 34 | 35 | redis.localSubscribedDouble = 0.0; 36 | redis.localSubscribedString = ""; 37 | redis.synchronousGetVariable = ""; 38 | redis.asynchronousGetVariable = ""; 39 | } 40 | } 41 | 42 | RedisInterface { 43 | id: redis 44 | serverUrl: "http://localhost:7379/" 45 | width: 800 46 | height: 600 47 | 48 | property double localSubscribedDouble 49 | property string localSubscribedString 50 | property double localPublishedDouble: 0.00 51 | property string localPublishedString: "." 52 | property string asynchronousGetVariable: "" 53 | property string synchronousGetVariable: "" 54 | 55 | signal publishedSignal1 56 | signal publishedSignal2 57 | signal localSignal 58 | onLocalSignal: handleLocalSignal(); 59 | 60 | function localMethod() { 61 | console.log("(QML) localMethod() called"); 62 | } 63 | 64 | function localWildcard() { 65 | console.log("(QML) localWildcard() called"); 66 | } 67 | 68 | function handleLocalSignal() { 69 | console.log("(QML) localSignal() emitted and handled by handleLocalSignal()"); 70 | } 71 | 72 | Component.onCompleted: init(); 73 | 74 | subscribedProperties: [ 75 | { remote: "remote:published:double", local: "localSubscribedDouble" }, 76 | { remote: "remote:published:string", local: "localSubscribedString" } 77 | ] 78 | 79 | publishedProperties: [ 80 | { local: "localPublishedDouble", remote: "remote:subscribed:double" }, 81 | { local: "localPublishedString", remote: "remote:subscribed:string" } 82 | ] 83 | 84 | subscribedEvents: [ 85 | { remote: "remote:signal:trigger", local: "localSignal" }, 86 | { remote: "remote:method:trigger", local: "localMethod" }, 87 | { remote: "remote:wildcard*", local: "localWildcard" } 88 | ] 89 | 90 | publishedEvents: [ 91 | { local: "publishedSignal1", remote: "remote:subscribed:event1" }, 92 | { local: "publishedSignal2", remote: "remote:subscribed:event2" } 93 | ] 94 | 95 | Text { 96 | anchors.centerIn: parent 97 | text: "remote:published:double : " + redis.localSubscribedDouble.toFixed(20) + "\n" + 98 | "remote:published:string : \"" + redis.localSubscribedString + "\"\n" + 99 | "localPublishedDouble : " + redis.localPublishedDouble.toFixed(2) + "\n" + 100 | "localPublishedString : \"" + redis.localPublishedString + "\"\n" + 101 | "get:async : \"" + redis.asynchronousGetVariable + "\"\n" + 102 | "get:sync : \"" + redis.synchronousGetVariable + "\"" 103 | } 104 | 105 | Timer { 106 | running: true 107 | repeat: true 108 | interval: 16 109 | onTriggered: { 110 | redis.localPublishedDouble += 0.1; 111 | redis.localPublishedString += "."; 112 | 113 | if(redis.localPublishedString.length > 10) 114 | redis.localPublishedString = "."; 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | MainForm.ui.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /qt-redis.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick 4 | CONFIG += c++11 5 | 6 | SOURCES += main.cpp \ 7 | CppRedisTest.cpp \ 8 | QMLRedisInterface.cpp \ 9 | RedisInterface.cpp 10 | 11 | RESOURCES += qml.qrc 12 | 13 | # Additional import path used to resolve QML modules in Qt Creator's code model 14 | QML_IMPORT_PATH = 15 | 16 | # Additional import path used to resolve QML modules just for Qt Quick Designer 17 | QML_DESIGNER_IMPORT_PATH = 18 | 19 | # The following define makes your compiler emit warnings if you use 20 | # any feature of Qt which as been marked deprecated (the exact warnings 21 | # depend on your compiler). Please consult the documentation of the 22 | # deprecated API in order to know how to port your code away from it. 23 | DEFINES += QT_DEPRECATED_WARNINGS 24 | 25 | # You can also make your code fail to compile if you use deprecated APIs. 26 | # In order to do so, uncomment the following line. 27 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 28 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 29 | 30 | # Default rules for deployment. 31 | qnx: target.path = /tmp/$${TARGET}/bin 32 | else: unix:!android: target.path = /opt/$${TARGET}/bin 33 | !isEmpty(target.path): INSTALLS += target 34 | 35 | HEADERS += \ 36 | CppRedisTest.h \ 37 | QMLRedisInterface.h \ 38 | RedisInterface.h 39 | 40 | -------------------------------------------------------------------------------- /qt-redis.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {ff9f6df2-0d61-488d-a04e-65b6398accdc} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | true 45 | 0 46 | 8 47 | true 48 | 1 49 | true 50 | true 51 | true 52 | false 53 | 54 | 55 | 56 | ProjectExplorer.Project.PluginSettings 57 | 58 | 59 | 60 | ProjectExplorer.Project.Target.0 61 | 62 | Desktop Qt 5.7.1 GCC 64bit 63 | Desktop Qt 5.7.1 GCC 64bit 64 | qt.57.gcc_64_kit 65 | 0 66 | 0 67 | 0 68 | 69 | /home/rohan/Development/personal/build-qt-redis-Desktop_Qt_5_7_1_GCC_64bit-Debug 70 | 71 | 72 | true 73 | qmake 74 | 75 | QtProjectManager.QMakeBuildStep 76 | true 77 | 78 | false 79 | false 80 | false 81 | 82 | 83 | true 84 | Make 85 | 86 | Qt4ProjectManager.MakeStep 87 | 88 | -w 89 | -r 90 | 91 | false 92 | 93 | 94 | 95 | 2 96 | Build 97 | 98 | ProjectExplorer.BuildSteps.Build 99 | 100 | 101 | 102 | true 103 | Make 104 | 105 | Qt4ProjectManager.MakeStep 106 | 107 | -w 108 | -r 109 | 110 | true 111 | clean 112 | 113 | 114 | 1 115 | Clean 116 | 117 | ProjectExplorer.BuildSteps.Clean 118 | 119 | 2 120 | false 121 | 122 | Debug 123 | 124 | Qt4ProjectManager.Qt4BuildConfiguration 125 | 2 126 | true 127 | 128 | 129 | /home/rohan/Development/personal/build-qt-redis-Desktop_Qt_5_7_1_GCC_64bit-Release 130 | 131 | 132 | true 133 | qmake 134 | 135 | QtProjectManager.QMakeBuildStep 136 | false 137 | 138 | false 139 | false 140 | false 141 | 142 | 143 | true 144 | Make 145 | 146 | Qt4ProjectManager.MakeStep 147 | 148 | -w 149 | -r 150 | 151 | false 152 | 153 | 154 | 155 | 2 156 | Build 157 | 158 | ProjectExplorer.BuildSteps.Build 159 | 160 | 161 | 162 | true 163 | Make 164 | 165 | Qt4ProjectManager.MakeStep 166 | 167 | -w 168 | -r 169 | 170 | true 171 | clean 172 | 173 | 174 | 1 175 | Clean 176 | 177 | ProjectExplorer.BuildSteps.Clean 178 | 179 | 2 180 | false 181 | 182 | Release 183 | 184 | Qt4ProjectManager.Qt4BuildConfiguration 185 | 0 186 | true 187 | 188 | 189 | /home/rohan/Development/personal/build-qt-redis-Desktop_Qt_5_7_1_GCC_64bit-Profile 190 | 191 | 192 | true 193 | qmake 194 | 195 | QtProjectManager.QMakeBuildStep 196 | true 197 | 198 | false 199 | true 200 | false 201 | 202 | 203 | true 204 | Make 205 | 206 | Qt4ProjectManager.MakeStep 207 | 208 | -w 209 | -r 210 | 211 | false 212 | 213 | 214 | 215 | 2 216 | Build 217 | 218 | ProjectExplorer.BuildSteps.Build 219 | 220 | 221 | 222 | true 223 | Make 224 | 225 | Qt4ProjectManager.MakeStep 226 | 227 | -w 228 | -r 229 | 230 | true 231 | clean 232 | 233 | 234 | 1 235 | Clean 236 | 237 | ProjectExplorer.BuildSteps.Clean 238 | 239 | 2 240 | false 241 | 242 | Profile 243 | 244 | Qt4ProjectManager.Qt4BuildConfiguration 245 | 0 246 | true 247 | 248 | 3 249 | 250 | 251 | 0 252 | Deploy 253 | 254 | ProjectExplorer.BuildSteps.Deploy 255 | 256 | 1 257 | Deploy locally 258 | 259 | ProjectExplorer.DefaultDeployConfiguration 260 | 261 | 1 262 | 263 | 264 | false 265 | false 266 | 1000 267 | 268 | true 269 | 270 | false 271 | false 272 | false 273 | false 274 | true 275 | 0.01 276 | 10 277 | true 278 | 1 279 | 25 280 | 281 | 1 282 | true 283 | false 284 | true 285 | valgrind 286 | 287 | 0 288 | 1 289 | 2 290 | 3 291 | 4 292 | 5 293 | 6 294 | 7 295 | 8 296 | 9 297 | 10 298 | 11 299 | 12 300 | 13 301 | 14 302 | 303 | -1 304 | 305 | qt-redis 306 | 307 | Qt4ProjectManager.Qt4RunConfiguration:/home/rohan/Development/personal/qt-redis-qt56/qt-redis.pro 308 | true 309 | 310 | qt-redis.pro 311 | false 312 | 313 | 314 | 3768 315 | false 316 | true 317 | false 318 | false 319 | true 320 | 321 | 1 322 | 323 | 324 | 325 | ProjectExplorer.Project.TargetCount 326 | 1 327 | 328 | 329 | ProjectExplorer.Project.Updater.FileVersion 330 | 18 331 | 332 | 333 | Version 334 | 18 335 | 336 | 337 | --------------------------------------------------------------------------------