Help us understand the problem. What is going on with this article?

ThingWorx ウィジェット開発 その2 アナログ時計を作る 後編

はじめに

この記事は、「ThingWorx ウィジェット開発 その2 アナログ時計を作る 前編」からの続きです。

目次

さて、前回までで ThingWorx マッシュアプビルダーが必要とするウィジェットの機能を作りこんで来ました。今回はいよいよ、ThingWorx のランタイムが使うウィジェットの機能を作っていきます。ThingWorx ランタイムが使う機能というのはすなわち、エンドユーザーが直接触れる機能になります。

第七歩:ランタイム用の JavaScript を書く

今回はまず最初に最終形コードの全体を見通してみましょう。ランタイムの際に読み込まれる JavaScript ファイルは、analog_clock.runtime.js になります。

analog_clock.runtime.js
TW.Runtime.Widgets.analog_clock= function () {

    var shortArm;
    var longArm;
    var hour;
    var minute;
    var second;
    var shortArmRotate;
    var longArmRotate;
    var currentDate;

    this.renderHtml = function () {
        return  '<div class="widget-content widget-analog_clock">' +
                    '<img class="aClock" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_Back.svg" />' +
                    '<img class="aClock aClock_shortArm" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_ShortArm.svg" />' +
                    '<img class="aClock aClock_longArm" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_LongArm.svg" />' +
                    '<img class="aClock" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_Center.svg" />' +
                '</div>';
    };

    this.afterRender = function () {
        shortArm = this.jqElement.find('.aClock_shortArm');
        longArm = this.jqElement.find('.aClock_longArm');
    };

    this.updateProperty = function (updatePropertyInfo) {

        if (updatePropertyInfo.TargetProperty === 'date') {

            currentDate = new Date(updatePropertyInfo.SinglePropertyValue);
            hour = currentDate.getHours();
            minute = currentDate.getMinutes();
            second = currentDate.getSeconds();

            if (hour > 11) {
                hour = hour - 12;
            }
            if (minute === '60') {
                minute = 0;
            }

            shortArmRotate = (hour * 30) + (minute * 0.5);
            longArmRotate = (minute * 6) + (second * 0.1);

            shortArm.css('transform', 'rotate(' + shortArmRotate + 'deg)');
            longArm.css('transform', 'rotate(' + longArmRotate + 'deg)');

        }
    };
};

コードを一つづつ見ていきましょう。ウィジェットがランタイムに初期化された際、最初に呼ばれるのが renderHtml()になります。renderHtml()の内部では、img タグで時計の背景画像や可動部分である短針・長針などを指定しています。

renderHtml()の呼び出しが終了して img 要素が DOM に追加されると、続いてafterRender()関数が呼ばれます。ここでは、ローカル変数shortArmlongArmに、DOM 上の要素 ID を代入しています。

ここは少し解説が必要です。renderHtml()内で定義している img タグは id 属性を持たず、class 属性を持っています。DOM 上の要素を変数に代入するならば、class ではなく id で一意に指定した方が良いような気がしますよね? ところが、ThingWorx ではウィジェット内部で id を元にした DOM 要素を取得することができません。

通常、マッシュアップには同じウィジェットを複数配置できます。今回の例では、一つのマッシュアップに5つの Analog Clock ウィジェットを配置できます。もしも Analog Widget 内部で id を使っていたら、DOM ツリー上で id の衝突が起きます。このため、ThingWorx のマッシュアップでは id を利用しません。かわりに class を使って次のように指定します。

sample.js
// <div class="myClass" />
var myElement = this.jqElement.find('.myClass');

さて、最後にupdateProperties()関数ですが、この関数は、ソースデータとしてバインドしているプロパティに変更があった時に呼び出されるコールバック関数です。内部の処理は(行数の割には)簡単で、渡された DATETIME 型の値から短針と長針の「角度」を算出し、その角度に基づいて CSS プロパティに「回転」を指示しています。

第八歩:ランタイム用のスタイルシートを書く

さて、ランタイム用のスタイルシートも忘れずに編集しなければなりません。analog_clock.runtime.js ではとくになんの工夫をすることもなく img タグを並べただけなので、このままでは文字盤、短針、長針がそれぞればらばらに配置されてしまいます。そこで、ランタイム用に各 img タグにスタイルを指定します。

もう一度 HTML がどうなってたかおさらいしておきましょう。

analog_clock.runtime.js
        return  '<div class="widget-content widget-analog_clock">' +
                    '<img class="aClock" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_Back.svg" />' +
                    '<img class="aClock aClock_shortArm" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_ShortArm.svg" />' +
                    '<img class="aClock aClock_longArm" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_LongArm.svg" />' +
                    '<img class="aClock" src="../Common/extensions/AnalogClock/ui/analog_clock/image/AnalogClock_Center.svg" />' +
                '</div>';

ランタイム用のスタイルシートは、analog_clock.runtime.css ファイルで指定します。最終形はこうなります。

analog_clock.runtime.css
/* Place custom CSS styling for Analog Clock widget at runtime in this file */

.widget-analog_clock {
    position: relative;
}

.aClock {
    position: absolute;
    left: 0;
    right: 0;
    max-width: 100%;
    height: auto;
    transition: 0.5s;
}

とてもシンプルなスタイルですね。基本的には、aClock クラスに対してポジションを absolute で指定したのち、左0ピクセル、上0ピクセルから可能な限り領域を埋めるように描画する指示を与えているだけです。

第九歩:ビルドする

さて、マッシュアップ用の JavaScript とスタイルシート、そしてランタイム用の JavaScript とスタイルシートの準備ができました。念の為、これまで編集してきたファイルを確認しましょう。

  • マッシュアップ用ファイル
    • analog_clock.ide.js
    • analog_clock.ide.css
  • ランタイム用ファイル
    • analog_clock.runtime.js
    • analog_clock.runtime.css

これらのファイルをエクステンションとして ZIP ファイルにまとめるには、Eclipse で「ビルド」と呼ばれる操作をします。ビルド自体は簡単なのですが、その前に若干のビルド用の設定をしなければなりません。

Eclipse の Package Explore で configfiles フォルダにある metadata.xml ファイルをダブルクリックします。そうすると、編集画面が立ち上がります。

image.png

Entities → ExtensionPackages → ExtensionPackage とドリルダウンしていくと、いくつかの設定可能項目が見つかると思いますが、編集しなければならない項目を説明します。

minimumThingWorxVersion
このウィジェットがインポートできる ThingWorx のバージョンを指定します。ここに指定したバージョン以降の ThingWorx でこのウィジェットが利用可能になります。

name
マッシュアップビルダー上で表示されるこのウィジェットの名前になります。

packageVersion
今回のビルドによって生成されるエクステンションのバージョンです。これは注意が必要で、エクステンションのインポート時には、すでに同じエクステンションがインストール済みでないか ThingWorx サーバーがチェックしています。もしも packageVersion = 1.0.0 のエクステンションを過去にインポートしており、今回コードの修正に伴って新しいエクステンションを再度インポートしようとした時に、新しいエクステンションの packageVersion が 1.0.0 かそれ未満だと、ThingWorx サーバーによってインポートが拒否されます。

通常は、エクステンションに更新が加わるたびに packageVersion の番号を上げていきます。

Vendor
このエクステンション(ウィジェット)を作った組織もしくは個人の識別子を指定します。特にこだわる必要はありません。

さて、すべての設定項目を適切に埋めたら、他に未セーブ状態のファイルがないことを確認して、いよいよビルドを実行します。

Package Explorer 上で build-extension.xml というファイルを見つけてください。build-extension.xml ファイルの名前の上で右クリックをした後、"Run As" → "1 Ant Build" を選択します。

image.png

そうすると、ビルド処理が流れ、右側のコンソールに下図のようなメッセージが出てきます。

image.png

赤い文字が表示されたりしてびっくりしますが、"BUILD SUCCESSFULL" という文字が表示されていればビルドは成功しています。実際のエクステンションファイルは、build/disributions フォルダの下に、AnalogClock.zip というファイルとして生成されています。

image.png

第十歩:ThingWorxにウィジェットをインポートする

直前のステップで生成した AnalogClock.zip ファイルはエクステンションそのもので、このまま ThingWorx にエクステンションとしてインポートできます。

コンポーザーの右肩にある "Import / Export" メニューから、実際にエクステンションをインポートしてみましょう。Import ダイアログが表示されたら、"Import Option" には "Extension" を選択し、ファイルには先ほど作成した AnalogClock.zip を指定します。

image.png

緑色の "Import" ボタンをクリックすると、インポート処理が実行されます。最終的に "Import successful." と表示されれば、AnalogClock エクステンションは無事にインポートされています。

image.png

ためしにマッシュアップビルダーを開いてみると、ウィジェット一覧に "Analog Clock" が追加されていることがわかります。

image.png

なお、現状では Analog Clock ウィジェットにサムネールが表示されていません。これは analog_clock.ide.js ファイルで定義している、

analog_clock.ide.js
    this.widgetIconUrl = function() {
        return  "'../Common/extensions/AnalogClock/ui/analog_clock/default_widget_icon.ide.png'";
    };

widgetIconUrl() で返しているパスにファイルが存在しないからです。指定されているファイルパスにアイコンを準備すると、ウィジェット一覧上でサムネールが表示されるようになります。

また、Analog Clock ウィジェットはレスポンシブルウィジェットとして登録されています。これも analog_clock.ide.js ファイルでsupportsAutoResizeを指定しているので、マッシュアップビルダーが「Analog Clock ウィジェットはレスポンシブルに対応している」と判断しているんですね。

「そして、新しい曲が始まるのです」

さて、時計ウィジェットが完成しました。

image.png

次回 →「ThingWorx ウィジェット開発 その3 マッシュアップへの組み込み」では、マッシュアップビルダーの操作を中心に、Analog Clock ウィジェットを実際に動作させる方法をご紹介します。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away