QtRemoteObjectsを使用したオブジェクトのプロセス間レプリケーション

2014年10月7日、QtモジュールQtRemoteObjectsのソースがパブリックドメインに登場しました。 このモジュールは、Ford Motor Company(著者Brett Stottlemyer)の腸内で作成されました。 私の意見では、このことは非常に有望です。 このモジュールにより、たとえば、ネットワークを介してオブジェクト間で信号を送信できます。 ただし、モジュールの機能はこれに限定されません。 より正確には、モジュールの本質は、オブジェクトがプロセス間で「複製」されるため、以前の名前であるレプリカで記述されます。











プロセス間通信/リモートプロシージャコールの他の方法と定性的に区別するQtRemoteObjectsの重要な考え方は、他のプロセスでQtオブジェクトを完全に複製するという考え方です。 これは、ソースオブジェクトのプロパティに対するすべての変更が、レプリカオブジェクトに(信号による通知とともに)反映されることを意味します。 ソースオブジェクトから放出される信号は、各レプリカオブジェクトでも放出されます。 プロパティを設定したり、レプリカオブジェクトのスロットを呼び出したりすることもできます。この場合、リクエストはそれらを処理するソースオブジェクトに送信され、信号またはプロパティを変更することで変更が他のレプリカオブジェクトに反映されます。 その結果、すべてのオブジェクト(ソースオブジェクトを含む)が同期されます。 同時に、プロセス間通信の複雑さはすべてQtRemoteObjects内に隠されています。



モジュールの組み立てと取り付け



1. Qt 5のバージョンのプライベートヘッダーファイルをインストールします


システムで、 qtbase5-private-devパッケージをインストールすることにより実行



2.モジュールソースを取得する


git clone gitorious.org/qtplayground/qtremoteobjects.git



3.モジュールを組み立ててインストールする


シャドウビルドを使用してQtCreatorでプロジェクトを組み立てました。 その結果、必要なすべてのファイルがbuild-qtremoteobjects-qt5_3_0-Releaseディレクトリに表示されました。 その後、このディレクトリにインストールインストールを行うと、モジュールヘッダーファイル、ライブラリ、およびrepファイルのコードジェネレーターが対応するディレクトリにインストールされます。 インストールされていない唯一のものは、qtremoteobjects / mkspecs / featuresディレクトリの内容です。/usr/ lib / x86_64-linux-gnu / qt5 / mkspecs / featuresディレクトリにコピーする必要があります。



make installに問題がある場合は、すべてを「手動で」実行できます。libQt5RemoteObjects.so(dll)ライブラリをQtライブラリディレクトリに、/ include / QtRemoteObjectsディレクトリをQt includeディレクトリにそれぞれコピーします。 また、「手動」モードでは、バイナリをbin / repcビルドディレクトリからQtバイナリディレクトリにコピーする必要があります(私の場合、/ usr / lib / x86_64-linux-gnu / qt5 / bin / repcです)。



モジュールの使用



オブジェクト(ソースとオブジェクト)の存在-レプリカは、クライアント/サーバーアーキテクチャのアイデアに触発されています。 したがって、モジュールの操作を示すために、クライアントとサーバーの2つのプロジェクトを作成します。 サーバー上のソースオブジェクトは、テキストが送信されるパラメーターで信号を送信し、レプリカオブジェクトの信号に接続されているクライアントのスロットでは、送信されたテキストが処理されます。

この例は非常に単純ですが、このような例ではモジュールを簡単に表示できます。 結果のアプリケーションの2つの写真と以下のコード。



サーバー



お客様





クライアントプロジェクトとサーバープロジェクト


1つの共通ディレクトリ(たとえば、remoteobj)と2つのサブディレクトリ(クライアントとサーバー)があり、2つのアプリケーションのプロジェクトが配置されます。 両方のアプリケーションのプロジェクトファイルで、モジュールを接続する必要があります。

QT += remoteobjects
      
      





.Repファイル


拡張子が.repのファイルは、プロセス間通信に使用されるオブジェクトのインターフェースを記述するために使用されます。 クライアントおよびサーバープロジェクトディレクトリの親ディレクトリに、次の内容のmessageSender.repテキストファイルを作成します。

 #include <QtCore> class MessageSender { SIGNAL(sendMessage(const QString &message)); };
      
      





mkspecs / featuresディレクトリのファイルについて書いたことを覚えていますか? これらのファイルには、repファイルを処理するためのルールが記述されています。 アセンブリ中にそれらを処理するには、プロジェクトファイルに次の行を追加する必要があります。

顧客向け

 REPC_REPLICA += ../MessageSender.rep
      
      



サーバー用

 REPC_SOURCE += ../MessageSender.rep
      
      





ヘッダーは、ビルド中にMessageSender.repから生成されます

クライアント用-rep_MessageSender_replica.h
 #ifndef REP_MESSAGESENDER_REPLICA_H #define REP_MESSAGESENDER_REPLICA_H // This is an autogenerated file. // Do not edit this file, any changes made will be lost the next time it is generated. #include <QObject> #include <QVariantList> #include <QMetaProperty> #include <QRemoteObjectNode> #include <QRemoteObjectReplica> #include <QRemoteObjectPendingReply> #include <QtCore> class MessageSenderReplica : public QRemoteObjectReplica { Q_OBJECT Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "MessageSender") friend class QRemoteObjectNode; private: MessageSenderReplica() : QRemoteObjectReplica() {} void initialize() { QVariantList properties; properties.reserve(0); setProperties(properties); } public: virtual ~MessageSenderReplica() {} Q_SIGNALS: void sendMessage(const QString & message); }; #endif // REP_MESSAGESENDER_REPLICA_H
      
      







サーバー用-rep_MessageSender_source.h
 #ifndef REP_MESSAGESENDER_SOURCE_H #define REP_MESSAGESENDER_SOURCE_H // This is an autogenerated file. // Do not edit this file, any changes made will be lost the next time it is generated. #include <QObject> #include <QVariantList> #include <QMetaProperty> #include <QRemoteObjectNode> #include <qremoteobjectsource.h> #include <QtCore> class MessageSenderSource : public QObject { Q_OBJECT Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "MessageSender") friend class QRemoteObjectNode; public: MessageSenderSource(QObject *parent = Q_NULLPTR) : QObject(parent) { } public: virtual ~MessageSenderSource() {} Q_SIGNALS: void sendMessage(const QString & message); }; class MessageSenderSimpleSource : public QObject { Q_OBJECT Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "MessageSender") friend class QRemoteObjectNode; public: MessageSenderSimpleSource(QObject *parent = Q_NULLPTR) : QObject(parent) { } public: virtual ~MessageSenderSimpleSource() {} Q_SIGNALS: void sendMessage(const QString & message); }; template <class ObjectType> struct MessageSenderSourceAPI : public SourceApiMap { MessageSenderSourceAPI() { _properties[0] = 0; _signals[0] = 1; _signals[1] = qtro_signal_index<ObjectType>(&ObjectType::sendMessage, static_cast<void (QObject::*)(QString)>(0),signalArgCount+0,signalArgTypes[0]); _methods[0] = 0; } QString name() const Q_DECL_OVERRIDE { return QStringLiteral("MessageSender"); } int propertyCount() const Q_DECL_OVERRIDE { return _properties[0]; } int signalCount() const Q_DECL_OVERRIDE { return _signals[0]; } int methodCount() const Q_DECL_OVERRIDE { return _methods[0]; } int sourcePropertyIndex(int index) const Q_DECL_OVERRIDE { return _properties[index+1]; } int sourceSignalIndex(int index) const Q_DECL_OVERRIDE { return _signals[index+1]; } int sourceMethodIndex(int index) const Q_DECL_OVERRIDE { return _methods[index+1]; } int signalParameterCount(int index) const Q_DECL_OVERRIDE { return signalArgCount[index]; } int signalParameterType(int sigIndex, int paramIndex) const Q_DECL_OVERRIDE { return signalArgTypes[sigIndex][paramIndex]; } int methodParameterCount(int index) const Q_DECL_OVERRIDE { return methodArgCount[index]; } int methodParameterType(int methodIndex, int paramIndex) const Q_DECL_OVERRIDE { return methodArgTypes[methodIndex][paramIndex]; } int propertyIndexFromSignal(int index) const Q_DECL_OVERRIDE { Q_UNUSED(index); return -1; } const QByteArray signalSignature(int index) const Q_DECL_OVERRIDE { switch (index) { case 0: return QByteArrayLiteral("sendMessage(QString)"); } return QByteArrayLiteral(""); } const QByteArray methodSignature(int index) const Q_DECL_OVERRIDE { Q_UNUSED(index); return QByteArrayLiteral(""); } QMetaMethod::MethodType methodType(int) const Q_DECL_OVERRIDE { return QMetaMethod::Slot; } const QByteArray typeName(int index) const Q_DECL_OVERRIDE { Q_UNUSED(index); return QByteArrayLiteral(""); } int _properties[1]; int _signals[2]; int _methods[1]; int signalArgCount[1]; const int* signalArgTypes[1]; int methodArgCount[0]; const int* methodArgTypes[0]; }; #endif // REP_MESSAGESENDER_SOURCE_H
      
      









これらのファイルは、それぞれクライアントおよびサーバーの付属品のヘッダーファイルに含まれている必要があります。



お客様


最初に、サーバーよりも単純なので、クライアントを分析します。

クライアントクラスのメンバーを追加する必要があります。

 QRemoteObjectNode clientNode;
      
      





そして、初期化関数(またはコンストラクターで直接)に次のように記述します。

 clientNode = QRemoteObjectNode::createNodeConnectedToRegistry(); //  QRemoteObjectReplica *sender = m_client.acquire< MessageSenderReplica >(); //    connect(sender, SIGNAL(sendMessage(const QString &)), this, SLOT(appendMessage(const QString &))); //   
      
      





appendMessageスロットでは、受信した文字列がリストに追加されるだけなので、その説明をスキップしてクライアントの説明に移動します。



サーバー


生成されたヘッダーファイルにはインターフェイスのみがあるため、ソースオブジェクトによる有用な作業を実行するには、機能を追加する必要があります。 これを行うには、生成されたクラスから継承し、スロットを定義します。

 class MessageSender : public MessageSenderSource { Q_OBJECT public slots: void postMessage(const QString &message); };
      
      





このスロットの実装は、単に信号を発信します。

 void MessageSender::postMessage(const QString &message) { emit sendMessage(message); }
      
      







サーバークラスの次のメンバーを追加します。

 MessageSender *serverSender; //   QRemoteObjectNode registryHostNode; //  QRemoteObjectNode objectNode; // 
      
      







そして、初期化関数で(または直接コンストラクターで)書きます:

 connect(ui->sendButton, SIGNAL(clicked()), this, SLOT(startSendMessage())); //  registryHostNode = QRemoteObjectNode::createRegistryHostNode(); //   objectNode = QRemoteObjectNode::createHostNodeConnectedToRegistry(); //   serverSender = new MessageSender(); // - objectNode.enableRemoting(serverSender);//   
      
      







startSendMessage()スロットでは、ソースオブジェクトのスロットが呼び出されます。

 QString messageText = ui->messageTextEdit->text(); serverSender->postMessage(messageText);
      
      







次に、アプリケーションを実行します。最初にサーバー、次にクライアント。



ネットワーキング


この例では、同じホスト内のプロセス間通信について説明しました。 パラメーターなしでノードを作成する場合、相互作用はローカルであると見なされます。



ネットワークを介して対話するには、ノード作成コードを変更する必要があります

サーバー側

 objectNode = QRemoteObjectNode::createHostNode(QUrl("tcp://localhost:9999")); //registryHostNode  
      
      





クライアント側で

 clientNode = QRemoteObjectNode(); clientNode.connect(QUrl("tcp://localhost:9999"));
      
      







結論の代わりに


この記事では、プロパティの使用については説明しません。 プロパティとスロット(repファイルにも記述されています)の使用例は、モジュールに付属の例で見ることができます。



ソースサーバーとクライアントを使用してアーカイブにリンクします。

Qt Developers Days 2014 North Americaからのプレゼンテーション



All Articles