D-Busは最初にSailfish OSでサポートされていたという事実にもかかわらず、ターミナルまたはアプリケーションからのみ管理できます(既に組み込まれている場合)。 そのため、Sailfish OS用のD-Busシステム用のビジュアルクライアントを作成するというアイデアが生まれました。これにより、システムに登録されたサービスを表示し、グラフィカルインターフェイスを使用してサービスと対話できます。 つまり、Sailfish OS用のD-FeetまたはQt D-Bus Viewerの類似物を作成します。
システム内のアプリケーションは、メソッド、シグナル、プロパティで記述される1つ以上のインターフェイスを実装する独自のサービス(またはサービス)を作成できます。 アプリケーションは、D-Busに登録されたサービスを介して他のアプリケーションと対話することもできます。 たとえば、 net.connmanサービスはSailfish OSに登録されており、 / net / connman / technology / bluetoothでnet.connman.Technologyインターフェースを実装しています。 このインターフェイスには、 SetProperty()メソッドも含まれています 。 次のように呼び出すことにより-SetProperty( "Powered"、true) -デバイスでBluetoothを有効にできます。
実際、アプリケーションの機能は、アナログに対しても同じことを繰り返します。 つまり アプリケーションは、D-Bus(セッションとシステムの両方)に登録されたサービスのリストの表示、各サービス、可能なパスのリストの表示、各パスのインターフェースのリスト、およびメソッド、プロパティ、信号のリストを表示する各インターフェースの表示を許可する必要があります。 さらに、アプリケーションはこれらの同じメソッドの呼び出しとプロパティの読み取り/変更も許可する必要があります。
Sailfish SDKには、D-Busと対話するための2つのオプションがあります。 まず、 Nemo QML D-Busプラグインを使用すると、QMLコードから直接D-Busと対話できます。 第二に、それはQt D-Busです-D-Busと対話するためのC ++クラスを提供する標準のQtメカニズムです。 2つの違いは、前者はかなり使いやすく、後者はより多くのオプションを提供することです。 この記事で説明するアプリケーションでは、両方の方法について説明します。
サービス一覧
D-Busに登録されているサービスのリストを取得するには、 DBusInterface要素を使用します。
DBusInterface { id: dbusList service: 'org.freedesktop.DBus' path: '/org/freedesktop/DBus' iface: 'org.freedesktop.DBus' bus: DBus.SessionBus }
そして、上記のインターフェースのListNamesメソッドが呼び出されます:
dbusList.typedCall('ListNames', undefined, function(result) { sessionServices = result.filter(function(value) {return value[0] !== ':'}).sort(); }, function() { pageStack.push(Qt.resolvedUrl("FailedToRecieveServicesDialog.qml")); });
メソッドの呼び出しが成功すると、サービスのリストが入力されます。 同時に、「:1.44」などの形式の名前のサービスを表示しないようにフィルタリングが行われます。 メソッドの呼び出し時にエラーが発生した場合、ユーザーには対応するダイアログにエラーメッセージが表示されます。 ページ自体は次のようになります。
サービスパスのリスト
リストからサービスをクリックすると、このサービスのすべての可能なパスのリストを含むページに移動します。 これを行うため、およびインターフェースのリストを取得するために、 org.freedesktop.DBus.IntrospectableインターフェースとそのIntrospectメソッドが使用されます。 このメソッドは、1つのパスのインターフェイスおよびネストされたサービスパスに関する情報をxmlの形式で返します。 ただし、この目的のためには、可能なすべてのサービスパスのリストを取得する必要があります。 言い換えると、ルートパス( "/")でIntrospectメソッドを呼び出してから、ネストされたすべてのパス(メソッド応答で説明されている)で再帰的に呼び出す必要があります。 このプロセスは再帰的であり、メソッド応答はxml形式で取得されるため、QMLコードのみを使用してこのような形式を実装することはできませんでした( XmlListModel要素は単に必要な機能を表していない)。
したがって、C ++でパスのリストを実装することが決定されました。 次のようになります。
QStringList DBusServiceInspector::getPathsList(QString serviceName, bool isSystemBus) { this->serviceName = serviceName; dDusConnection = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus(); return extractPaths(introspectService("/"), "/"); } QString DBusServiceInspector::introspectService(QString path) { QDBusInterface interface(serviceName, path, "org.freedesktop.DBus.Introspectable", dDusConnection); QDBusReply<QString> xmlReply = interface.call("Introspect"); if (xmlReply.isValid()) return xmlReply.value(); return ""; } QStringList DBusServiceInspector::extractPaths(QString xml, QString pathPrefix) { QXmlStreamReader xmlReader(xml); QStringList pathsList; while (!xmlReader.atEnd() && !xmlReader.hasError()) { QXmlStreamReader::TokenType token = xmlReader.readNext(); if (token == QXmlStreamReader::StartDocument) continue; if (token == QXmlStreamReader::StartElement) { if (xmlReader.name() == "interface") { QXmlStreamAttributes attributes = xmlReader.attributes(); if (attributes.hasAttribute("name") && attributes.value("name") != "org.freedesktop.DBus.Introspectable" && attributes.value("name") != "org.freedesktop.DBus.Peer") if (!pathsList.contains(pathPrefix)) pathsList.append(pathPrefix); } else if (xmlReader.name() == "node") { QXmlStreamAttributes attributes = xmlReader.attributes(); if (attributes.hasAttribute("name") && attributes.value("name") != pathPrefix) { QString path = attributes.value("name").toString(); if (path.at(0) == '/' || pathPrefix.at(pathPrefix.length() - 1) == '/') { path = pathPrefix + path; } else { path = pathPrefix + "/" + path; } pathsList.append(extractPaths(introspectService(path), path)); } } } } return pathsList; }
このコードは、上記の再帰アルゴリズムを実装しています。 org.freedesktop.DBus.Introspectableとorg.freedesktop.DBus.Peerの 2つのインターフェースのみからアクセスできるパスが結果から削除されることに注意してください 。 これは、ユーザーにとって本当に役立つインターフェースが利用可能なパスのみを表示するために行われます。
パスリストページは次のとおりです。
インターフェースリスト
パスをクリックすると、次のページが開き、選択したパスに対してこのサービスによって実装されているインターフェースのリストが表示されます。 このリストとパスのリストは、 Introspectメソッドを使用して取得したxmlを使用してコンパイルされますが、特定のパスに対して1回呼び出すだけで再帰は行われません。 この機能はNemo QMLプラグインD-Busを使用して作成できますが、C ++で実装することにしました。
QStringList DBusServiceInspector::getInterfacesList(QString serviceName, QString path, bool isSystemBus) { this->serviceName = serviceName; dDusConnection = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus(); QXmlStreamReader xmlReader(introspectService(path)); QStringList interfacesList; while(!xmlReader.atEnd() && !xmlReader.hasError()) { QXmlStreamReader::TokenType token = xmlReader.readNext(); if (token == QXmlStreamReader::StartElement) { if (xmlReader.name() == "interface") { QXmlStreamAttributes attributes = xmlReader.attributes(); if (attributes.hasAttribute("name")) interfacesList.append(attributes.value("name").toString()); } } } return interfacesList; }
リスト自体は次のようになります。
インターフェイスをクリックすると、別のページが開き、このインターフェイスのメソッドとプロパティのシグナルが表示されます。
これらのパラメーターはすべて、 Introspectメソッドを呼び出した結果として取得されたxml解析を使用して取得されます。 ただし、ここでは、作業を簡素化するために、別のクラスInterfaceMemberを割り当てました。これは、本質的に、インターフェイスの特定のメンバーのすべてのパラメーターを格納する構造です。 これは、そのようなオブジェクトを非ビジュアル要素としてQMLコードで簡単に表現できるようにするために行われました。
メソッドの呼び出しとインターフェイスプロパティの変更
アプリケーションの最後の2ページは、インターフェイスプロパティを変更し、インターフェイスメソッドを呼び出すためのページです。 最初は非常に簡単に実装され、次のようになります。
プロパティが読み取り専用の場合、変更できません。 プロパティも書き込み可能な場合、ページに新しい値を入力するためのフィールドは変更可能であり、新しい値をそこに入力できます。 プロパティ値の読み取りと書き込みは、QDBusInterfaceクラスのproperty()およびsetProperty() メソッドを使用して行われます。 org.freedesktop.DBus.PropertiesインターフェースのGet()およびSet メソッドを使用して実装することもできます。
メソッド呼び出しページは次のとおりです。
インターフェイスメソッド自体の呼び出しも、QDBusInterfaceクラスのcall()またはcallWithArgumentList() メソッドを使用して簡単に実装できます。
ただし、ここでは、メソッド引数をQMLから取得したものからD-Bus自体が理解できるものに変換するのが困難でした。 この機能を実装するために、Qt D-Bus Viewerに実装された既製のソリューションを採用することにしました。 このプロジェクトのソースコードはGitHubで表示できます。
おわりに
このアプリケーションは、Sailfish OSプラットフォームのアプリケーションストア-Jolla HarbourでVisual D-Busという名前で公開され、そこからダウンロードできます。 プロジェクトのソースコードはGitHubにあります 。
投稿者:Denis Laure