WebCFaceの機能紹介・チュートリアルです。
このチュートリアルはWebUIでデータを可視化する・WebUIからプログラムを操作するという部分をメインにしたものです。 プロセス間通信に重点をおいたチュートリアルは 1-2. Tutorial (Communication)
このチュートリアルでは C++ (Meson または CMake)、または Python (Python3.6以上が必要です) を使用します。
WebCFaceのインストール
READMEにしたがって webcface, webcface-webui, webcface-tools をインストールしましょう。
C++の場合はインストールしたwebcfaceパッケージにクライアントライブラリも含まれています。
Server
WebCFaceを使用するときはserverを常時立ち上げておく必要があります。 次のように WebCFace Desktop アプリから、またはコマンドラインからの2つの方法があります。
WebCFace Desktop
スタートメニュー(Windows)、 アプリケーションメニュー(Ubuntu)、 Launchpadまたはアプリケーションフォルダ(Mac) に 「WebCFace Desktop」のアイコン が表示されているはずなので、それを起動してください。
それを起動すると、WebCFaceのメイン画面(WebUI)が起動すると同時に、バックグラウンドでサーバーが起動します。
コマンドライン
ターミナルを開いて webcface-server
コマンドを実行するとサーバーが起動します。 デフォルトでは7530番ポートを開きクライアントの接続を待ちます。 追加で指定できるコマンドラインオプションなど、詳細は 2-1. Server
serverは起動したまま、起動時に表示されるurl (http://IPアドレス:7530/index.html) をブラウザで開くと、 WebCFaceのメイン画面(WebUI)が開きます。 使用しているターミナルによっては、Ctrl(Command)+クリックなどで開くことができるかもしれません。
WebUI
WebCFaceのメイン画面です。 WebCFaceにクライアントが接続すると、WebUI右上のMenuに表示されます。 Menuから見たいデータを選ぶことで小さいウィンドウのようなものが現れデータを見ることができます。
ウィンドウの表示状態などは自動的にブラウザ(LocalStorage)に保存され、次回アクセスしたときに復元されます。
詳細な使い方については 2-2. WebUI
Setup
ここからWebCFaceを使ったプログラムを書いていきます。
まずはプロジェクトを作成しWebCFaceライブラリを使えるようにします。
- Note
- C++でMesonやCMakeを使わない場合、pkg-configを使ったり手動でコンパイル・リンクすることも可能です。 詳細な説明は 3-1. Setup WebCFace Library
C++ (Meson) 適当にディレクトリを作ります
mkdir webcface-tutorial
cd webcface-tutorial
以下のようにWebCFaceの初期化 + 簡単な Hello World を書きます
- main.cc
#include <iostream>
int main() {
wcli.waitConnection();
std::cout << "Hello, World!" << std::endl;
}
サーバーに接続するクライアント。
Definition client.h:18
- Note
- Clientの初期化はグローバル変数でもローカル変数 (main() の最初) でもどちらでも構いませんが、 このチュートリアルではグローバル変数にします。
- meson.build
project('webcface-tutorial', 'cpp',
default_options: ['cpp_std=c++17'],
)
webcface_dep = dependency('webcface')
executable('tutorial', 'main.cc',
dependencies: webcface_dep,
)
ビルドして、実行します
meson setup build # ← 初回のみ
meson compile -C build
./build/tutorial
実行すると、Hello, World!
と出力されると同時に、 WebUI の右上のメニューの中にClientで指定した名前「tutorial」が現れるはずです。
- Note
- このチュートリアルでは以降ビルドと実行の手順は省略しますが、 同様に
meson compile
(またはninja) でビルドして ./build/tutorial
を実行してください。
C++ (CMake) 適当にディレクトリを作ります
mkdir webcface-tutorial
cd webcface-tutorial
以下のようにWebCFaceの初期化 + 簡単な Hello World を書きます
- main.cc
#include <iostream>
int main() {
wcli.waitConnection();
std::cout << "Hello, World!" << std::endl;
}
- Note
- Clientの初期化はグローバル変数でもローカル変数 (main() の最初) でもどちらでも構いませんが、 このチュートリアルではグローバル変数にします。
- CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(webcface-tutorial)
find_package(webcface 2.0 CONFIG REQUIRED)
add_executable(tutorial main.cc)
target_link_libraries(tutorial PRIVATE webcface::webcface)
ビルドして、実行します
cmake -B build # ← 初回のみ
cmake --build build
./build/tutorial
実行すると、Hello, World!
と出力されると同時に、 WebUI の右上のメニューの中にClientで指定した名前「tutorial」が現れるはずです。
- Note
- このチュートリアルでは以降ビルドと実行の手順は省略しますが、 同様に
cmake --build
(またはmakeやninjaなどでも可) でビルドして ./build/tutorial
を実行してください。
Python PythonでWebCFaceを使うには、クライアントライブラリをインストールする必要があります。 venv内でもグローバルにインストールしても構いません。
以下のようにWebCFaceの初期化 + 簡単な Hello World を書きます
- main.py
from webcface import Client
wcli = Client("tutorial")
wcli.wait_connection()
print("Hello, World!")
- Note
- Clientの初期化はグローバル変数でもローカル変数 (main() やクラスの初期化など) でもどちらでも構いませんが、 このチュートリアルではグローバル変数にします。
実行します 実行すると、Hello, World!
と出力されると同時に、 WebUI の右上のメニューの中にClientで指定した名前「tutorial」が現れるはずです。
Log
コンソールに出力していた Hello, World!
を、WebCFaceにも送信してみましょう。
C++
- main.cc
#include <iostream>
std::ostream &logger = wcli.loggerOStream();
int main() {
wcli.waitConnection();
logger << "Hello, World!" << std::endl;
wcli.sync();
}
これを実行すると、コンソール (std::coutではなくstd::cerrと同じ標準エラー出力ですが) には今まで通り Hello, World!
と表示されます。
それに加えて、WebUI右上のメニューから「tutorial」を開き「Logs」をクリックすると、ログを表示する画面が開きこちらからも Hello, World!
を確認できるはずです。
std::wostream を使うこともできます。 また、コンソールに表示せずWebCFaceにログの文字列を送信する関数もあります。 詳細は 5-5. Log
Python
- main.py
from webcface import Client
import sys
wcli = Client("tutorial")
sys.stdout = wcli.logging_io
wcli.wait_connection()
print("Hello, World!")
wcli.sync();
これを実行すると、コンソール (stdoutではなくstderrですが) には今まで通り Hello, World!
と表示されます。
それに加えて、WebUI右上のメニューから「tutorial」を開き「Logs」をクリックすると、ログを表示する画面が開きこちらからも Hello, World!
を確認できるはずです。
pythonのloggingモジュールを使いたい場合はLoggerの出力先として使用できるHandlerも用意しています。 また、コンソールに表示せずWebCFaceにログの文字列を送信する関数もあります。 詳細は 5-5. Log
Value, Text
数値や文字列のデータを送信すると、それをリアルタイムに表示することができます。
C++
- main.cc
#include <iostream>
#include <thread>
std::ostream &logger = wcli.loggerOStream();
int main() {
wcli.waitConnection();
logger << "Hello, World!" << std::endl;
int i = 0;
while(true){
i++;
wcli.value("hoge") = i;
if(i % 2 == 0){
wcli.text("fuga") = "even";
}else{
wcli.text("fuga") = "odd";
}
wcli.sync();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
これを実行し、WebUI右上のメニューから「tutorial」を開き「hoge」をクリックするとグラフが表示され、リアルタイムにtestの値(ここでは1秒ごとに1ずつ増える値)を確認できます。
また、「fuga」をクリックすると文字列で送信したデータもリアルタイムに確認することができます。
Value, Textについては 5-1. Value, 5-2. Text に詳細なドキュメントがあります。
Python
- main.py
from webcface import Client
import sys
import time
wcli = Client("tutorial")
sys.stdout = wcli.logging_io
wcli.wait_connection()
print("Hello, World!")
i = 0
while True:
i += 1
wcli.value("hoge").set(i)
if i % 2 == 0:
wcli.text("fuga").set("even")
else:
wcli.text("fuga").set("odd")
wcli.sync()
time.sleep(1)
これを実行し、WebUI右上のメニューから「tutorial」を開き「hoge」をクリックするとグラフが表示され、リアルタイムにtestの値(ここでは1秒ごとに1ずつ増える値)を確認できます。
また、「Text Variables」をクリックすると文字列で送信したデータもリアルタイムに確認することができます。
Value, Textについては 5-1. Value, 5-2. Text に詳細なドキュメントがあります。
Func
プログラムからWebUIに情報を送信するだけでなく、WebUIからプログラムを操作することも可能です。
C++
- 引数なしの関数hogeを作り、これをクライアントに登録します。
int hoge() {
logger << "Function hoge started" << std::endl;
return 42;
}
int main() {
wcli.func("hoge").set(hoge);
}
これを実行し、WebUI右上のメニューから「tutorial」を開き「Functions」をクリックすると hoge() を実行するボタンが現れると思います。 「Run」をクリックすると実行され、「Function hoge started」のログが追加されます。 また、画面右下に関数の戻り値の42が表示されています。
- Note
- Runを押してから「Function hoge started」が表示されるまでに1秒程度ラグがあると思います。 これは関数を実際に実行する処理が wcli.sync() (このチュートリアルでは1秒に1回呼んでいる) の中で行われているためです。
- こんどは引数がある関数を作ってみます。
int fuga(int a, const std::string &b) {
logger << "Function fuga(" << a << ", " << b << ") started" << std::endl;
return a;
}
int main() {
wcli.func("hoge").set(hoge);
wcli.func("fuga").set(fuga).setArgs({
});
}
引数の情報を表す。
Definition arg.h:30
std::optional< ValAdaptor > init() const
デフォルト値を取得する。
Definition func_info.cc:62
const std::vector< ValAdaptor > & option() const
引数の選択肢を取得する。
Definition func_info.cc:85
setArgs() はそれぞれの引数の名前やオプション(初期値、最小値、最大値、選択肢など)を指定することができます。 (指定しなくてもよいです。)
実行すると fuga() の引数を入力する欄と実行するボタンが現れると思います。 hogeの場合と同様、引数を入力して「Run」をクリックすると実行されます。
Funcについては 5-3. Func に詳細なドキュメントがあります。
Python
- 引数なしの関数hogeを作り、これをクライアントに登録します。
from webcface import Client
import sys
import time
wcli = Client("tutorial")
@wcli.func("hoge")
def hoge() -> int:
print("Function hoge started")
return 42
sys.stdout = wcli.logging_io
wcli.wait_connection()
- Note
@wcli.func("hoge")
はデコレータです(糖衣構文というPythonの構文です)。 この書き方に馴染みがないなら def hoge() -> int:
print("Function hoge started")
return 42
wcli.func("hoge").set(hoge)
と書いて使うこともできます(がこの場合は@を使ったほうがかんたんに書けます)。
これを実行し、WebUI右上のメニューから「tutorial」を開き「Functions」をクリックすると hoge() を実行するボタンが現れると思います。 「Run」をクリックすると実行され、「Function hoge started」のログが追加されます。 また、画面右下に関数の戻り値の42が表示されています。
- Note
- Runを押してから「Function hoge started」が表示されるまでに1秒程度ラグがあると思います。 これは関数を実際に実行する処理が wcli.sync() (このチュートリアルでは1秒に1回呼んでいる) の中で行われているためです。
- また、send側では呼び出された関数の実行が完了するまで wcli.sync() は完了しません。 メインループをブロックせず別スレッドで関数を呼び出したい場合は set() の代わりに setAsync() が使えます。
- こんどは引数がある関数を作ってみます。
from webcface import Client, Arg
import sys
import time
wcli = Client("tutorial")
@wcli.func("hoge")
def hoge() -> int:
print("Function hoge started")
return 42
@wcli.func("fuga", args=[
Arg(init=100),
Arg(option=["foo", "bar", "baz"]),
])
def fuga(a: int, b: str) -> int:
print(f"Function fuga({a}, {b}) started")
return a
sys.stdout = wcli.logging_io
wcli.wait_connection()
args ではそれぞれの引数の名前やオプション(初期値、最小値、最大値、選択肢など)を指定することができます。 (指定しなくてもよいです。)
実行すると fuga() の引数を入力する欄と実行するボタンが現れると思います。 hogeの場合と同様、引数を入力して「Run」をクリックすると実行されます。
Funcについては 5-3. Func に詳細なドキュメントがあります。
View
Funcに登録した関数は一覧表示させることしかできませんが、 Viewではテキストや入力欄を任意に並べて表示させることができます。
C++
int main() {
while(true){
v << "Hello, world!" << std::endl;
v << "i = " << i << std::endl;
logger <<
"str = " << ref_str.
asString() << std::endl;
});
v << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
TemporalViewComponent & bind(const InputRef &ref) &
変更した値を格納するInputRefを設定
Definition component_view.cc:302
Viewの送受信データを表すクラス
Definition view.h:26
const View & sync() const
Viewの内容をclientに反映し送信可能にする
Definition view.cc:31
TemporalViewComponent textInput(std::string_view text="")
Definition components.h:631
View view(std::string_view field="") const
Definition field.cc:74
これを実行し、WebUI右上のメニューから「tutorial」を開き「sample」をクリックすると、 画像のようにプログラムで指定したとおりの画面が表示されます。 「hoge」ボタンをクリックすると関数hoge (Function hoge started) と表示し、 「print」ボタンをクリックするとその左の入力欄に入力した文字列がログに表示されます。
上のプログラム例のように、 テキストの表示はstd::ostream (std::cout など) と同じようにviewに文字列や数値などを出力するような書き方でできます。 ボタンにはFuncと同様関数や関数オブジェクト(ラムダ式など)を登録できます。 またtextInputなどを使って入力欄を表示させることもできます。 詳細は 5-4. View を参照
Python
from webcface import Client, Arg, view_components, InputRef
while True:
with wcli.view("sample") as v:
v.add("Hello, world!")
v.add("i = ", i)
v.add(view_components.button("hoge", hoge))
ref_str = InputRef()
v.add(view_components.text_input("str", bind=ref_str))
v.add(
view_components.button(
"print",
lambda: print(f"str = {str(ref_str.get())}"),
)
)
v.add("\n")
wcli.sync()
time.sleep(1)
これを実行し、WebUI右上のメニューから「tutorial」を開き「sample」をクリックすると、 画像のようにプログラムで指定したとおりの画面が表示されます。 「hoge」ボタンをクリックすると関数hoge (Function hoge started) と表示し、 「print」ボタンをクリックするとその左の入力欄に入力した文字列がログに表示されます。
上のプログラム例のように、 テキストの表示はadd()に文字列や数値などを渡すことでできます。 ボタンにはFuncと同様関数や関数オブジェクト(ラムダ式など)を登録できます。 またtextInputなどを使って入力欄を表示させることもできます。 詳細は 5-4. View を参照
おわりに
以上で 1-1. Tutorial (Visualizing) は終わりです。 次ページ (1-2. Tutorial (Communication)) にはプロセス間通信に重点をおいたチュートリアルがあります。
ここで紹介していない機能として
もWebUIからアクセスすることのできる機能としてあるので、見てみてください。
また、チュートリアルでは紹介していないコマンドラインツールとして
もあります。