QtQuickでNEMに触れてみる

Advent Calendar 2018 3日目です。NEMとQtです。

NEMBERをどうやったら増やせるのかをなんとなく考えているkihakuです。
ざっくり2つの方法があるのかなぁ、と思っています。

1つはNEMを意識して使わせる、1つはNEMを意識させずに使わせる、です。
前者は、NEMだからこそ、みたいなもので、アプリやプラットフォームを作る人や提供する人が「○○したいから、NEMが最適だ」みないな判断で使うケースです。
後者は、NEMBERと自分でも認識しないケースで、前者のアプリやプラットフォームを使う人です。なんか使ってたら、実はそれってNEMだった、みたいなケースですね。

今回は前者の「NEMを意識して使わせる」をやります。
どうやってNEMを採用してもらうかですが、手っ取り早い手段の1つが、設計者が使いやすいようになっていること、です。
これはNEMのウリの1つだと思うのですが、公式/有志問わずいろいろなSDK、ライブラリが提供されています。(といいつつ他のblockchainは知らないですが。。。)
https://nem.io/developers/#panel-w599b86f61d6d1-0-0-2

というわけで、今回は、QtでNEMに触ってみようと思います。
本業でここ1ヶ月くらい触る機会があったので、Qtにしてみました。QtとNEMのクロスドメインです。
1．ノードの確認
2．アドレス情報の確認(残高確認など)
3．アドレスのトランザクションの確認(取引履歴など)
送金は、時間の都合上、諦めました。(機会があれば次回に)

#NEMとは(for Qt User)
いわゆる仮想通貨・暗号通貨の1つです。
が、個人的には”仮想通貨”って思うよりも、デジタルデータを扱うためのオープンプラットフォームというほうがしっくりきます。(ここ大事)
広域的な特徴は他の仮想通貨等が1つ、これに加えて、公証(アポスティーユ認証)や、複数人署名(Multisig)があります。
次のバージョン(Cutapalt)では、アグリゲートトランザクション(複数のトランザクションをまとめて処理)なども目玉ですね。
日本のコミュニティやNEMBER(ざっくりNEMユーザー)の活動が活発です。

#Qtとは(For NEMBER)
マルチプラットフォーム対応のフレームワークです。
日本では採用事例が少ないようですが、Skypeだったり、メルセデスのコックピットディスプレイに採用されていたりします。
ツールチェーンを使用することでWindows、iOS、Android、Linux等なんでもござれです。

有償版と無償版がありますが、無償版はオープンソース(基本的にGPL\LPGL)なので要注意です。
(企業内で使うなら、あまり気にしなくていいので使いやすいかもです)

Qtは特にUIフレームワークが強く、QtQuick(QML)と呼ばれるXML＋JavaScriptが使えるのが特徴です。ただ、JavaScriptは、最近のフロントエンド界隈のNode.jsをベースとしたライブラリは基本使えないと思ったほうが良いです。言語としてのシンプルなjs(?)をイメージしておくといいでしょう。
QtWidgetというUIフレームワークもありますが、今から触るならQtQuick一択だと思います。
いわゆるコントロール層やモデル層はC++になります。

#NEMにどうやって触るか
まず触ろうとするレイヤですが、タイトルの通りQtQuickを対象とします。
ネイティブアプリとして考えるなら、本来はC++のほうが筋だと思います。が、人がやらないところをやる。

というわけで、QtQuickからはNEMのNIS APIでHTTPリクエスト投げます。
NEM NIS API公式：https://nemproject.github.io/
日本語訳：http://i-yusuke.com/entry/nem-nis-api-document-japanese/

QtQuick QMLtype一覧：https://doc.qt.io/qt-5.11/qmltypes.html

#0．アプリケーションの大枠
さっそくですが、今回のアプリケーションの大まかなつくりは、以下になります。
・メニューは左側(Window幅の左から4割)
・ボディが残り、ボディにはトランザクションの結果のJSONをそのまま出力する

Windowに対して、Rectangleで仕切ります。
お作法のようなものですが、Windows全体->各パーツとRectangleで区切ることが多いようです。
これだけだと真っ黒のWindowが表示されるだけです(背景黒は個人的な趣味です)。

尚、QtQuickやQtそのものの環境構築手順や、ビルド/実行手順はここでは記載しません。
(が、癖があるのに、ネットに情報が少ないので、何かあればお気軽にTwitterのDMででも、聞いてください。)

```qml:main.qml
import QtQuick 2.11
import QtQuick.Controls 2.2
import QtQuick.Window 2.11

Window {
    id:_root
    visible: true
    width: 1280
    height: 800
    title: qsTr("NEM By Qt")

    Rectangle{
        id:_bg
        anchors.fill:parent
        color:"black"

        Rectangle{
            id:_leftMenu
            anchors.margins: 8
            anchors.top:parent.top
            anchors.left:parent.left
            anchors.bottom:parent.bottom
            width:parent.width * 0.3
            color:"black"
        }
        Rectangle{
            id: _body
            anchors.top:parent.top
            anchors.left:_leftMenu.right
            anchors.right:parent.right
            anchors.bottom:parent.bottom
            width:parent.width - _leftMenu.width
            color:"black"
        }
    }
}
```
`id`：オブジェクトにユニークな名前です。このidを指定してアクセスすることが多いです。
`anchors`：配置の相対位置を決める。例えば`anchors.top:parent.top`であれば、対象のオブジェクトの上部を親オブジェクトの上部とバインドする、感じです。`anchors.fill`は全ての辺を一致させるプロパティです。

次に、ボディのログ表示部分を作ります。
ただ垂れ流すだけの機能にします。
`_body`オブジェクト内を以下のように記述します。

```qml:main.qmlの_bodyオブジェクト
        Rectangle{
            id: _body
            anchors.top:parent.top
            anchors.left:_leftMenu.right
            anchors.right:parent.right
            anchors.bottom:parent.bottom
            width:parent.width - _leftMenu.width
            color:"black"

            Text{
                id: _text
                anchors.fill:parent
                anchors.centerIn: parent
                color:"white"
                font.pixelSize: 18
                font.family:"Meiryo UI"
                wrapMode: Text.Wrap
                function add(t){
                    text = t + text;
                }
            }
        }
```
`_body`と同じ領域にText(文字列表示)のオブジェクト設定しています。
`add(t)`で`t`に入力された文字列を既存の文字列の先頭に追加しています。新しいものが先に表示されます。(参考：[QML(Qt)のドラッグ＆ドロップはQt5で簡単に！](http://relog.xii.jp/mt5r/2012/08/qmlqtqt5.html))
`wrapMode`：行端での折り返しの設定です。

次に、左メニューに配置するボタンを作っておきます。
ファイル名(ここではRequestButton)がクラス名になります。
QMLにはButtonクラスも用意されていますが、個人的に使いづらいのでMouseAreaとRectangleで自作することが多いです。

```qml:RequestButton.qml
import QtQuick 2.11
import QtQuick.Controls 2.2

MouseArea {
    id:_root
    width:200
    height:40
    property alias label:_label.text

    Rectangle{
        id:_bg
        anchors.fill:parent
        color:"black"
        border.width:2
        border.color:"white"
    }
    Text{
        id:_label
        anchors.fill:parent
        anchors.centerIn: parent
        color:"white"
        font.pixelSize: 18
        font.family:"Meiryo UI"
        text: "default"
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }
}
```
`MouseArea`：ボタンにするためにはマウスイベントが必要です。マウスに反応するようにするために、この`MouseArea`のクラスとして継承します。`Rectangle->MouseArea`と構成してもよいですが、呼び出し元でMouseEventを定義しようとすると、`MouseArea->Rectangle`としておいたほうが楽ちんです。(参考：[QML(Qt)でボタンを作ろう！](http://relog.xii.jp/mt5r/2012/05/qmlqt-19.html))
`property`：新しい任意のプロパティ(メンバ)の追加。`alias`指定した場合には、このプロパティに指定する値は指定先のプロパティに設定されます。ここでは`RequestButton`の`label`に"ぼたんのらべる"と指定すると、`_label.text`にも"ぼたんのらべる"が設定され、Textとして表示されるようになります。

ざっくり、これで準備ができたので、NEMにアクセスしていきます。

#1．ノードの確認
HTTPリクエストを投げるにもノードが動いているか確認しないことには始まりません。
NEMにはMAINNET(本チャンのPublic Chain)、TESTNET(テスト用)、MIJINNET(Private Chain)の3種類があります。
公開するときには、MAINNETにしなければならないですが、とりあえず触るだけなら、TESTNETでいいでしょう。

NEMのノードは世界中にあります。
ここ([MAINNET](http://chain.nem.ninja/#/nodes/)、[TESTNET](http://bob.nem.ninja:8765/#/nodes/))で確認しましょう。今回は`http://150.95.145.157:7890/`を使わせていただきます。

ノードの確認は`/heartbeat`を用います。
httpでGETリクエストを投げる関数を用意しておき、そこにURLとAPIを突っ込みます。

```qml:main.qml
import QtQuick 2.11
import QtQuick.Controls 2.2
import QtQuick.Window 2.11

Window {
    id:_root
    visible: true
    width: 1280
    height: 800
    title: qsTr("NEM By Qt")

    property string testneturl:"http://150.95.145.157:7890/"

    Rectangle{
        id:_bg
        anchors.fill:parent
        color:"black"

        Rectangle{
            id:_leftMenu
            anchors.margins: 8
            anchors.top:parent.top
            anchors.left:parent.left
            anchors.bottom:parent.bottom
            width:parent.width * 0.3
            color:"black"

            RequestButton{
                id:_heartbeat
                label:"heartbeat"
                width:parent.width-2
                anchors.top:parent.top
                anchors.horizontalCenter: parent.horizontalCenter

                onClicked: {
                    _root.getData("/heartbeat");
                }
            }
        }
        Rectangle{
            id: _body
            anchors.top:parent.top
            anchors.left:_leftMenu.right
            anchors.right:parent.right
            anchors.bottom:parent.bottom
            width:parent.width - _leftMenu.width
            color:"black"

            Text{
                id: _text
                anchors.fill:parent
                anchors.centerIn: parent
                color:"white"
                font.pixelSize: 18
                font.family:"Meiryo UI"
                wrapMode: Text.Wrap
                function add(t){
                    text = t + text;
                }
            }
        }
    }

    function getData(apipath) {
        var xmlhttp = new XMLHttpRequest();
        var url = _root.testneturl + apipath;
        var response = "";

        xmlhttp.onreadystatechange=function() {
            if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status == 200) {
                response = xmlhttp.responseText;
                print("response in :"+response); // log
                _text.add("api:" +apipath + "\n" + "response:"+response+"\n")
            }
        }
        xmlhttp.open("GET", url, true);
        xmlhttp.send();
    }

}
```
`function getData(apipath)`：本来であれば、Promiseなどを用いて実装したほうが良いと思いますが、私がまだ使ったことがありませんので、`xmlhttp.onreadystatechange`で直接テキスト出力しています。
`print()`：IDE側の出力ウインドウへの出力をさせています。デバッグは基本これでログ出力させます。

実行結果：
![image.png](https://qiita-image-store.s3.amazonaws.com/0/249932/90923236-34f1-e387-a46e-bdcf9b8c2f7c.png)

無事確認できました。

#2．アドレス情報の確認(残高確認など)
まず、アドレスの入力が必要になります。
アドレスもNIS APIで作れますが、手っ取り早く公式のNanoWalletで作っちゃいましょう。
[こちら](https://qiita.com/kihaku_dol/items/049c94731482b6f346c7#%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E4%BD%9C%E3%82%8B)を参考にアドレスを作り、TESTNET用のXEMを確保します。
頭文字が`T`のアドレスが生成されます。

`_body`直下の`_heartbeat`の記述の下に以下を追加します。

```qml:main.qmlのアドレス入力
            Rectangle{
                id:_addressinput
                width:parent.width-2
                height:30
                anchors.topMargin: 16
                anchors.top:_heartbeat.bottom
                anchors.horizontalCenter: parent.horizontalCenter
                color:"lightgray"
                border.width:1
                border.color: "lightgray"

                TextField{
                    id:_addresstext
                    anchors.fill:parent
                    placeholderText: qsTr("Enter address")
                    color:"black"
                    font.pixelSize: 12
                    font.family:"Meiryo UI"
                    selectByMouse:true
                }
            }
```
`_addresstext`：実際の入力用フィールド。`_addresstext.text`に入力した値が保持されます。

ついでに、アドレス情報取得用のボタンを追加します。

```qml:main.qmlのアドレス情報取得用のボタン
            RequestButton{
                id:_getad
                label:"Get address data"
                width:parent.width-2
                anchors.margins: 8
                anchors.top:_addressinput.bottom
                anchors.horizontalCenter: parent.horizontalCenter

                onClicked: {
                    _root.getData("/account/get?address="+_addresstext.text);
                }
            }`
```

実行結果：
![image.png](https://qiita-image-store.s3.amazonaws.com/0/249932/48cf2bb7-1a27-2373-5d6a-c0d4e0865489.png)

残高は、この中の`balance`の値を1,000,000で割った値が、XEMになります。ここでは、`balance`が8950000ですので、8.95XEMが残高になります。

#3．アドレスのトランザクションの確認(取引履歴など)
取引を含め様々なリクエストをトランザクションといいます。
転送(いわゆる送金)や設定の変更などなど、いろいろなトランザクションがあります。
送信のみ、受信のみ、未承認(ブロックチェーンに刻まれる前段階)などでも取得できます。
ここでは、指定のアドレスのすべてのトランザクションを表示します。

`_body`直下の`_getad`の記述の下に以下を追加します。

```qml:main.qmlのトランザクション確認部
            RequestButton{
                id:_gettx
                label:"Get tranxaction"
                width:parent.width-2
                anchors.margins: 8
                anchors.top:_getad.bottom
                anchors.horizontalCenter: parent.horizontalCenter

                onClicked: {
                    _root.getData("/account/transfers/all?address="+_addresstext.text);
                }
            }
```

heartbeat、account/get、account/transfersを実行した結果：
![image.png](https://qiita-image-store.s3.amazonaws.com/0/249932/5e7c098e-f893-713c-e047-7b1f12a01c81.png)

#まとめ
今回はQtQuick(QML)上のみで、NEMに触ってみました。
NEMBERにはネイティブアプリがXML＋JavaScriptでサクッと作れる感じ、QtユーザーにはNEMへのアクセスの第一歩が思ったより簡単と思えていただけたらよいです。

それぞれのドメイン(領域)内では、知識や経験等のオープン化があってもクロスする領域でオープンになっていることが少ないと感じています。
今後もこういった、あまり関係なさそうなクロスドメインで「異分野への第一歩のハードル」を下げることができたら、と思います。

---
この記事はnemlogにも転記しています。
転記先([QtQuickでNEMに触れてみる](https://nemlog.nem.social/blog/6910)))。