1.はじめに
ElixirのGUIライブラリ「Scenic」を使って、簡単なウィンドウアプリを作ってみます。
scenicのバージョンが0.10系から0.11系になり、過去に記述した記事がそのままではビルドできなくなりました。
本記事では、2023年時点で最新のVer.0.11系でビルドする手順を示します。
実行環境
下記の環境で試しています。
OS | Ubuntu Linux 22.04 LTS |
Elixir | Ver.1.15.5 |
$ uname -a
Linux hostname 6.2.0-37-generic #38~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 2 18:01:13 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.3 LTS"
$ elixir -v
Erlang/OTP 25 [erts-13.2.2.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Elixir 1.15.5 (compiled with Erlang/OTP 25)
2.インストール
Elixirのインストールは下記を参照願います。
Scenicのインストールは下記を参考に進めます。
Scenicのビルドに必要なライブラリをインストールします。
$ sudo apt install libglfw3 libglfw3-dev libglew-dev -y
scenicライブラリをインストールします。
$ mix archive.install hex scenic_new
3.まずは"Hello World"を試す
ひな形のプロジェクトを作成して、とりあえず実行します。
$ cd (ご自身のワーキングディレクトリに移動)
# scenicプロジェクトを作成
$ mix scenic.new hmihello
#プロジェクトのディレクトリに移動
$ cd hmihello
# 必要なライブラリを取得
$ mix deps.get
# 実行
$ mix scenic.run
#(`iex -S mix`でもOK)
[Ctrl-C]
の押下か、ウィンドウの閉じるボタンで終了できます。
4.テキストと図形を表示
(・・・省略・・・)
# ============================================================================
# setup
# --------------------------------------------------------
def init(scene, _param, _opts) do
# get the width and height of the viewport. This is to demonstrate creating
# a transparent full-screen rectangle to catch user input
{width, height} = scene.viewport.size
# show the version of scenic and the glfw driver
scenic_ver = Application.spec(:scenic, :vsn) |> to_string()
driver_ver = Application.spec(:scenic_driver_local, :vsn) |> to_string()
info = "scenic: v#{scenic_ver}\nscenic_driver_local: v#{driver_ver}"
graph =
Graph.build(font: :roboto, font_size: @text_size)
|> add_specs_to_graph([
# この4行をコメントアウト
# text_spec(info, translate: {20, 40}),
# text_spec(@note, translate: {20, 120}),
# rect_spec({width, height})
# ここから追記
# テキスト
text_spec("Hello World", t: {40, 40}),
# 図形・丸
# 直径 描画位置 塗りつぶし 輪郭
circle_spec(10, t: {20, 30}, fill: :blue, stroke: {2, :white}),
circle_spec(10, t: {220, 30}, fill: :yellow, stroke: {2, :red}),
# 図形・直線
# 対角左上 対角右下 太さ 色 先端形状
line_spec({{10, 50}, {250, 50}}, stroke: {4, :cyan}, cap: :round)
])
scene = push_graph(scene, graph)
{:ok, scene}
end
(・・・省略・・・)
書き換えたら、ビルド・実行します。
$ mix scenic.run
文字と、2つの丸、直線の図形が描画できました。
5.押しボタンの入力イベントでテキストと図形を書き換え
まずは描画内容を作ります
defmodule Hmihello.Scene.Home do
use Scenic.Scene
require Logger
alias Scenic.Graph
import Scenic.Primitives
import Scenic.Components # ←ここのコメントアウトを外します
# ↓ここは参照しないので、コメントアウトします
# @note """
# This is a very simple starter application.
# If you want a more full-on example, please start from:
# mix scenic.new.example
# """
@text_size 24
# ↓ウィンドウに描画する内容は、この関数にまとめます
defp graph_build() do
Graph.build(font: :roboto, font_size: @text_size)
|> add_specs_to_graph(
[
# テキスト
text_spec("Hello World", t: {40, 40}),
# 図形・丸
circle_spec(10, fill: :blue, stroke: {2, :white}, t: {20, 30}),
circle_spec(10, fill: :yellow, stroke: {2, :red}, t: {220, 30}),
# 図形・直線
line_spec({{10, 50}, {250, 50}}, stroke: {4, :cyan}, cap: :round),
# 押しボタン
button_spec("ON", id: :btn_on, t: {40, 90}, theme: :success),
button_spec("OFF", id: :btn_off, t: {120, 90}, theme: :danger),
# テキスト・ON/OFF表示
text_spec("---", t: {80, 80}, id: :event_text),
# 図形・ランプ・ON/OFF表示
circle_spec(10, id: :event_circle, fill: :grey, stroke: {2, :white}, t: {60, 70})
],
# 全体の描画位置をオフセットする
translate: {10, 10}
)
end
# ============================================================================
# setup
# --------------------------------------------------------
# ↓ここの関数はまるっと書き換えて、2行だけの内容にします
def init(scene, _param, _opts) do
scene = push_graph(scene, graph_build())
{:ok, scene}
end
# ***あとでここに関数を書き足します***
# ↓ここは変更なしです
def handle_input(event, _context, scene) do
Logger.info("Received event: #{inspect(event)}")
{:noreply, scene}
end
end
書き換えたら、ビルド・実行します。
$ mix scenic.run
先ほどの描画内容に対し、さらにON・OFFボタンと灰色のランプ、テキスト---
が描画できました。
この時点では、ボタンイベントの処理を書いていないので、ボタンをクリックしても変化はありません。
続いて、イベント処理を追加します。
先ほどのコード中に # ***あとでここに関数を書き足します***
と書かれた箇所に、関数を2つ追記します。
(・・・省略・・・)
def handle_event(event, _context, scene) do
Logger.debug("Received handle_event: #{inspect(event)}")
graph =
case event do
{:click, id} ->
# 押しボタンのクリックイベントでマッチさせる
graph_build()
# 押しボタンの動作による画面更新処理に渡す
|> on_click_button(id)
_ ->
# それ以外のときは書き換えなし
graph_build()
end
# 描画内容を更新する
scene =
scene
|> push_graph(graph)
{:noreply, scene}
end
def on_click_button(graph, id) do
case id do
# 押しボタン・ONが押された
:btn_on ->
graph
# テキストの表記を書き換え
|> Graph.modify(:event_text, &text(&1, "ON"))
# ランプの色を置き換え
|> Graph.modify(:event_circle, &update_opts(&1, fill: :lawn_green))
# 押しボタン・OFFが押された
:btn_off ->
graph
|> Graph.modify(:event_text, &text(&1, "OFF"))
|> Graph.modify(:event_circle, &update_opts(&1, fill: :grey))
_ ->
# それ以外のときは書き換えなし
raise "not implimentation"
end
end
(・・・省略・・・)
書き換えたら、ビルド・実行します。
$ mix scenic.run
押しボタンのON・OFFを押下するごとに、テキストとランプが書き換わります。
6.まとめ
Ver.0.10系とVer.0.11系における変更点の気づきです。
- マウスのカーソル移動、キーボードのキー入力単体のイベントが取れなくなった
-
push_graph
関数で画面の更新をするようになった - (ほか、調査中)