QlikView Extensionをつくってみる

QlikView Extensionの作り方について。日本語の紹介サイトがあまりなく、情報収集に困る方も多いであろうから参考になれば幸いである。

# 対象
本稿ではQlikView(v11以上)を扱っている。Qlik SenseもExtensionと呼ばれる技術があるらしいが、本稿では取り扱っていない。

# QlikView Extension
## 概説
QlikViewではビルトインのいくつかの[チャート](https://help.qlik.com/en-US/qlikview/April2019/Subsystems/Client/Content/QV_QlikView/Chart_types.htm)がある。しかしながら、定型的で表現には限界がある。Extensionを使うことで、CSSとJavaScriptにより自由な表現を行うことができる。

## 制約
WebViewモードでしか使えない。表現をWebブラウザーのレンダリングに頼っているためである。

## 活用例
### 
運送会社の案件で、ドライバーごとの移動を視覚化したいとの依頼があった。それに応じ移動経路を地図にプロットするExtensionを開発した。



# QlikView Extensionの作り方
## フォルダー構成
基本的な構成は次の通りとなる。

　エクステンション名/
　　├ Definition.xml
　　├ icon.png
　　├ Script.js
　　├ DynProperties.qvpp
　　├ css/
　　│　├ Definition.xml
　　│　├ a.css
　　│　└ b.css
　　└ js/
　　 　├ Definition.xml
　　 　├ c.js
　　 　└ d.js

css/やjs/といったサブフォルダーは設けず、単一のフォルダー(エクステンション名/)に全てまとめても良い。フォルダー毎にDefinition.xmlが必要となるため、サブフォルダーを作るのであれば同ファイルが必要となる。

## Definition.xml
### 概要
ファイル名は固定。その名の通りExtensionのプロパティ定義ファイルである。日本語を含むときはBOM付きでないと文字化けするかもしれない。次のリンクに設定項目は網羅される。

 - [QlikView extension definition file, QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-definition-file.htm)
 - [QlikView properties: dimensions, QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-properties-dimensions.htm)
 - [QlikView properties: measures, QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-properties-measures.htm)
 - [QlikView properties: caption, background and layout, QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-properties-caption-background-layout.htm)
 - [QlikView properties: text, QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-properties-text.htm)

### エクステンション名/Definition.xml
デフォルトのPageHeightは40。値を大きくすればロードするレコードの量が増える。

```xml:Definition.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ExtensionObject Path="エクステンション名"
                 Label="エクステンション日本語名"
                 Description="エクステンションの説明"
                 PageHeight="100"
                 Type="object">

  <Dimension Label="軸" Initial="" TargetName="軸" />
  <Measurement Label="メジャーA" Initial="" />
  <Measurement Label="メジャーB" Initial="" />

  <Initiate Name="Chart.Dimension.0.Field" Value="軸" />
  <Initiate Name="Chart.Expression.0.0.Definition" Value="メジャーA" />
  <Initiate Name="Chart.Expression.0.0.Definition" Value="メジャーB" />

  <Initiate Name="Chart.Dimension.0.SuppressNull" Value="1" /> 
  <Initiate Name="Chart.Dimension.0.Sort.PrimarySortOrder" Value="1" /> 
</ExtensionObject>
```

前述の通りプロパティはいろいろあるので、例えばExtensionの枠が鬱陶しいなと思ったら、このように追記すれば消せる。

```xml:Definition.xml(一部)
  <Initiate Name="Caption.Show" Value="0" />
  <Initiate Name="Caption.Icon.SendToExcel" Value="0" />
  <Initiate Name="Caption.Allow.Maximize" Value="0" />
  <Initiate Name="Caption.Allow.Minimize" Value="0" />
  <Initiate Name="Caption.Text" Value="エクステンション日本語名" />
  <Initiate Name="Layout.Allow.MoveSize" Value="0" />
  <Initiate Name="Layout.Border.Use" Value="0" />
```

### エクステンション名/サブフォルダー名/Definition.xml
これ以上の情報はいらない。このDefinition.xmlをサブフォルダー毎に格納すればよい。

```xml:Definition.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ExtensionObject Type="object">
</ExtensionObject>
```

## icon.png
### 概要
ファイル名は固定。本Extensionのアイコン。32 x 32px位でよいと思う。

## Script.js
### 概要
ファイル名は固定。initialのスクリプト。日本語を含むときはBOM付きでないと文字化けするかもしれない。

ネットに転がっているExtensionのコードを探したが、後述のパターンAかパターンBの書き方が多いように見受けられた。何を隠そうQv.AddExtensionの呼び出し順が異なるだけである。留意したいのは、Qv.AddExtensionのコールバックでthis.Dataオブジェクトにアクセスできるようになるということだ。

 - [QlikView Ajax JavaScript Library, QlikTech](https://help.qlik.com/en-US/qlikview-developer/12.0/APIs/js+API/symbols/Qv.html)


### エクステンション名/Script.js：パターンA
```javascript:Script.js(パターンA)
function ExtensionInit() {
  var _this = this;

  var cssFiles = [];
  var QV_LOAD_URL = Qva.Remote + (Qva.Remote.indexOf("?") >= 0 ? "&" : "?") + "public=only" + "&name=";
  cssFiles.push("Extensions/" + QV_LOAD_URL + "/css/a.css");
  cssFiles.push("Extensions/" + QV_LOAD_URL + "/css/b.css");
  for (var i = 0; i < cssFiles.length; i++) {
    Qva.LoadCSS(QV_LOAD_URL + cssFiles[i]);
  }

  var jsFiles = [];
  jsFiles.push("Extensions/" + QV_EXTENSION_NAME + "/js/c.js");
  jsFiles.push("Extensions/" + QV_EXTENSION_NAME + "/js/d.js");

  Qv.LoadExtensionScripts(jsFiles,
    function () {
      alert(Object.keys(_this.Data.Rows));
      subA();
      subB();

      function subA() {
        // サブルーチンA
      }

      function subB() {
        // サブルーチンB
      }
    });
}

var QV_EXTENSION_NAME = "エクステンション名";
Qv.AddExtension(QV_EXTENSION_NAME, ExtensionInit);
```
### エクステンション名/Script.js：パターンB
```javascript:Script.js(パターンB)
function ExtensionInit() {
  var QV_EXTENSION_NAME = "エクステンション名";

  var cssFiles = [];
  var QV_LOAD_URL = Qva.Remote + (Qva.Remote.indexOf("?") >= 0 ? "&" : "?") + "public=only" + "&name=";
  cssFiles.push("Extensions/" + QV_LOAD_URL + "/css/a.css");
  cssFiles.push("Extensions/" + QV_LOAD_URL + "/css/b.css");
  for (var i = 0; i < cssFiles.length; i++) {
    Qva.LoadCSS(QV_LOAD_URL + cssFiles[i]);
  }

  var jsFiles = [];
  jsFiles.push("Extensions/" + QV_EXTENSION_NAME + "/js/c.js");
  jsFiles.push("Extensions/" + QV_EXTENSION_NAME + "/js/d.js");

  Qv.LoadExtensionScripts(jsFiles,
    function () {
      Qv.AddExtension(QV_EXTENSION_NAME,
        function () {
          var _this = this;

          alert(Object.keys(_this.Data.Rows));
          subA();
          subB();

          function subA() {
            // サブルーチンA
          }

          function subB() {
            // サブルーチンB
          }
        });
    });
}

ExtensionInit();
```

### データへのアクセス
Rowsオブジェクトにアクセスするだけ。とてもかんたん。

```JavaScript:Script.js(一部)
alert(this.Data.Rows[0][0].text);
```

 - https://help.qlik.com/en-US/qlikview-developer/12.0/APIs/js+API/symbols/Qv.Document.Object.Data.html#Rows

### ページサイズを可変にする例
Definition.xmlに次の通り追加することで、プロパティダイアログにテキストボックスができる。

```xml:Definition.xml(一部)
<Text Label="ページサイズ" Type="text" Expression="500" />
```

Script.jsで同テキストボックスを参照するようにすれば、それに応じてページサイズを変更できる。

```JavaScript:Script.js(一部)
Data.SetPagesizeY(_this.Layout.Text0.text.toString());
```

## DynProperties.qvpp
### 概要
ファイル名は固定。DynProperties.qvppはプロパティダイアログの表示(見た目)を管理している。Extensionをインストールするか、当該Extensionの埋め込まれたドキュメントを開くと自動的に生成される。

## パッケージング
### 概要
qarファイルがExtensionのファイルとなる。実態はただのzipファイル。拡張子を除けばファイル名に制約はない。

 - [QlikView archive files (QAR), QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-qar-files.htm)

### 作り方
zipで固めて、拡張子をqarとするだけ。

　1. フォルダー エクステンション名/をzipで固める(例：エクステンション名.zip)
　2. 同ファイルをの拡張子をzipからqarへ変更する(例：エクステンション名.qar)

## インストール
qarファイルはQlikViewに関連付けられていることを前提にダブルクリックするだけ。インストールが上手く行けばQlikViewのダイアログが表示される。

再インストールの際は、QlikViewのドキュメントからも一度削除し、再配置すのが無難。

## アンインストール
次のフォルダから当該Extension名のフォルダを削除すればよい。

　%AppData%\Local\QlikTech\QlikView\Extensions\Objects\エクステンション名

 - [QlikView archive files (QAR), QlikTech](https://help.qlik.com/en-US/qlikview-developer/April2019/Subsystems/Extensions/Content/QV_Extensions/extensions-qar-files.htm)

# その他
## QVとQVAの名前空間について
提供されるAPIは、Qv.ExtensionやQva.AddExtensionといったように、QvとQvaの名前空間(?)が混在している。QlikView v10とv11以降の違いとも言われるが、Qvに集約されたコードを見た試しがない。

 - [Qv vs Qva, Qlik Community](https://community.qlik.com/t5/New-to-QlikView/Qv-vs-Qva/td-p/533658)

# 参考
GitHubにもいくつか例はあるし、それらを参考にできると思う。

 - [QlikView Extension tutorials, documentation and examples](http://www.qlikfix.com/2012/07/03/qlikview-extension-tutorials-documentation-and-examples/)