Thymeleaf で isXxx プロパティを参照するとエラーになる
概要
Thymeleaf の変数式で ${aVariable.isXxx} とするとエラーになって困った時の記録です。
確認環境
- Spring Boot 1.4.2
- Spring 4.3.4
- Thymeleaf 2.1.5, 3.0.0
- Kotlin 1.0.5
解説
Kotlin で次のクラスを作成したとする。
data class User( val name: String, val age: Int, val address: Address, val isEnabled: Boolean)
Java ならば以下のようになる。
public class User { private final String name; private final int age; private final Address address; private final boolean isEnabled; public boolean isEnabled() { return isEnabled; } ... (setter/getter, toString, equals, hashCode メソッドと続くが省略) }
Thymeleaf では User オブジェクトの isEnabled プロパティを参照するために以下のように書く。
<span th:text="${user.isEnabled}">どうなるでしょう?</span>
これはエラーになる。isEnabled プロパティが見つからないと。 isEnabled ではなく enabled とすることで参照できる。
<span th:text="${user.enabled}">参照できる</span>
何故エラーになるのかと思い、デバッガで確認した。
org.springframework.expression.spel.support.ReflectivePropertyAccessor クラスの findGetterForProperty メソッドでは、 propertyName=isEnabled を IsEnabled に変換した後に findMethodForProperty メソッドを呼ぶ。
findMethodForProperty メソッドでは、prefix=is と methodSuffix=IsEnabled を結合して isIsEnabled とした上でメソッド名と equals で比較。
プロパティ名を enabled とすれば、isEnabled となるため参照できる。
Thymeleaf は内部的に SpEL (非SpringではOGNL) を使って式を解釈している。 上記は SpEL の一部に該当する。
bootRun で Spring Boot を起動した時に、IntelliJ IDEA でデバッガを使う
概要
Spring Boot と IntelliJ IDEA の組み合わせでデバッガを使う方法です。 Terminal から bootRun 起動する場合に限ります。
確認環境
- IntelliJ IDEA CE 2016.3
- Spring Boot 1.4.2
参考情報
- How do I debug a gradle/Spring-boot app in IntelliJ Idea ? https://github.com/Muhuru-Bay-Microgrid/muhuru-bay-dashboard - #3 by Herick_Oliveira - Old Forum - Gradle Forums
- configuration で Remote を選んでね、と書かれている。
- Spring Boot Reference Documentation
- 起動では --debug-jvm オプションを付ける。
解説
Run > Edit Configurations を選択する。
Configuration を新規追加。
Remote を選択。(Socket で接続する。)
任意の名前に設定し、他はデフォルトでOK。
Terminal から起動する際に --debug-jvm
を付けて bootRun を実行すると接続待ちになる。
作成した Configuration (例では、bootRun (debug)) でデバッグ実行を行うと、Socket が接続されて bootRun の起動が継続する。
以上で、デバッガを使用できる状態になる。
Thymeleaf snippet
概要
Thymeleaf の書き方を学習するために、チュートリアルの内容を一通り書いてみました。 その記録です。
確認環境
- IntelliJ IDEA COMMUNITY 2016.3
- Spring Boot 1.4.2
- Thymeleaf 2.1.5
参考情報
Thymeleaf の使い方。日本語。
内容
html
タグの xmlns:th="http://www.thymeleaf.org"
は IDEA のエラー対策です。
雑感
IntelliJ IDEA ULTIMATE を使えば Thymeleaf でも補完できるらしい。 COMMUNITY は Thymeleaf の補完だけでなく JavaScript の整形、補完もできないので、開発で使うなら ULTIMATE を買わないとダメだなと思う。
FaceRig + 恋声 + Skype で映像と音声を変換して通話する
概要
FaceRig + Live2D Module を使えばイケメン・美少女キャラクターに成り替わることができます。 恋声を使えばマイク入力の音声を変換して女声・男声に替えることができます。 これらと Skype を組み合わせれば、姿と声を変えて通話することができます。
FaceRig + Live 2D Module は合わせても 2000 円弱、恋声は無料。 いずれも Windows 用のソフトウェアです。 MacBook Pro で試したいと思い、VMware Fusion 8 で動いている Windows 10 に環境構築しました。
確認環境
- MacBook Pro (13-inch, Late 2016)
- VMware Fusion 8.5
- RAM 8GB
- Windows 10 Pro (x64)
参考情報
- Windows 仮想マシンで Apple iSight カメラを使用する
- VMware でカメラを使用する
- 中の人(二次元)になる方法【FaceRig × Live2D × Unity × OBS × AVVoiceChanger × 気合】 - Qiita
- FaceRig の使用方法
- http://ch.nicovideo.jp/marth/blomaga/ar337868
- 音声を Skype に出力する方法
解説
映像の設定、音声の設定、映像と音声を組み合わせて Skype に出力する手順で構築する。
仮想マシンのカメラを有効にする
- 仮想マシンをシャットダウン
- 設定からカメラを追加
以下、設定からカメラを追加する手順。
FaceRig をインストールする
- Windows 10 を起動
- Steam, The Ultimate Online Game Platform から Steam をダウンロードして、インストール
- Steam を起動して FaceRig と Live2D Module をインストール
- FaceRig を起動する
- LAUNCH しようとするとエラーが表示されるので、OPTIONS から設定を変更 (DX11 ではなく DX9 を使用)
- アドバンス UI に変更する
- 高度なトラッキングの設定から、カメラデバイスの指定を変更
- VMware Virtual USB Video Device
- アバターの設定を変更
- 自動調整を実行
- 頭ポーズの(早い)自動調整
- 表情の(早い)自動調整
以下、画像で手順を説明する。
Steam で FaceRig と Live2D Module をインストール。
FaceRig を起動。
アドバンス UI に変更。
カメラデバイスの指定。
アバター設定。
自動調整。
以上で映像の設定は完了。
仮想サウンドカードをインストールする
- NETDUETTO をダウンロードして、インストール
恋声 (無料ボイスチェンジャー) をインストール
- サービス終了のお知らせ をダウンロードして、インストール
- 恋声を起動
- koigoe(x64).exe を実行
- デバイス設定を変更
- 入力はマイク、出力は仮想サウンドカード
- 入力をマイクに設定
- 声質を調整
- お好みに
- 起動したままにする
デバイス設定を変更。
FaceRig のサウンド設定を変更
- 入力を仮想サウンドカードに変更
以上で音声の設定は完了。
Skype の設定
FaceRig の仮想デバイスに映像を出力させるには、ブロードキャストに切り替える。 リップシンクに切り替えると音声に合わせて口が動くようになる。
Skype の映像と音声の入力を変更する。
プログラム用のきれいな等幅フォント
2011.12.08: MacBook Air に環境構築。
2016.11.20: MacBook Pro 導入に伴って、再度インストールしました。内容は更新されています。
表題の件で何かないかと探したら、Ricty というフォントがあるそうな。早速入れてみました。
詳しいインストール方法は Ricty フォントのページに書かれています。
以下はインストール記録です。
前準備として、必要になるコマンドをインストールします。
Homebrew というパッケージ管理システムをインストールします。
インストール方法は Homebrew に書かれています。
```bash
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```
次に Homebrew を使って FontForge をインストールします。
```bash
$ brew install fontforge --use-clang
```
これで前準備は完了。次はフォントファイルと生成スクリプトを プログラミング用フォント Ricty 内のリンクを辿ってダウンロード。
まず、ダウンロードしたファイルを一つのディレクトリに展開します。展開したディレクトリで ls コマンドを実行した結果です。
```bash
$ ls
Inconsolata-Bold.ttf migu-1m-regular.ttf
Inconsolata-Regular.ttf mplus-TESTFLIGHT-060
ipag00303 ricty_discord_converter.pe
migu-1m-bold.ttf ricty_generator.sh
```
```bash
$ bash ricty_generator.sh Inconsolata-Regular.ttf Inconsolata-Bold.ttf migu-1m-regular.ttf migu-1m-bold.ttf
```
実行が完了すると、Ricty ディレクトリに以下のファイルが生成されます。
これらのファイルを Finder でダブルクリックして、フォントをインストールします。
プログラミング用フォント Ricty に合わせて以下のフォントもインストールします。
- Inconsolata-Regular.ttf
- Inconsolata-Bold.ttf
- migu-1m-regular.ttf
- migu-1m-bold.ttf
以上で完了です。
参考:
http://knagayama.net/blog/2011/07/27/generate-ricty-font/
http://d.hatena.ne.jp/gar_sue/20111112/1321131923
http://d.hatena.ne.jp/hiroe_orz17/20111113/1321168274
libGDX 2D UI Library - Widget (Button)
概要
libGDX の 2D UI Library のうち、Button 系 Widget についてまとめます。
目次
確認環境
- OS X El Capitan (10.11.6)
- Xcode 8.0
- Android Studio 2.2.2
- Multi-OS Engine Plugin 1.2.1
- Kotlin 1.0.4
- buildToolVersion 25.0.0
- compileSdkVersion 24
- libGDX 1.9.5-SNAPSHOT
参考情報
- Scene2d.ui · libgdx/libgdx Wiki · GitHub
- Scene2d UI 公式文書。
解説
Button
Table クラスのサブクラス。
設定項目 | 型 | 説明 |
---|---|---|
child | Actor | 子の Actor。 |
style | Button.ButtonStyle | 背景の Drawable (up, down, checked, over, checkedOver, disabled)。child のオフセット (unpressedOffset[X|Y], pressedOffset[X|Y], checkedOffset[X|Y])。 |
Skin |
状態遷移(一部)
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | child のサイズ | 0 |
実行例
up | checked (offsetX = 100, offsetY = 100) |
---|---|
サンプルコード
private val actor by lazy { val imageButton = ImageButton( SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.RED }), SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.BLUE }), SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GREEN }) ) Button(imageButton, Button.ButtonStyle().apply { up = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.YELLOW }) down = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.MAGENTA }) checked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.CYAN }) checkedOffsetX = 100f checkedOffsetY = 100f } ).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height } } override fun create() { ... stage.addActor(actor) }
ImageButton
Button クラスのサブクラス。
設定項目 | 型 | 説明 |
---|---|---|
style | ImageButton.ImageButtonStyle | imageUp, imageDown, imageChecked, imageOver, imageCheckedOver, imageDisabled。加えて、Button.ButtonStyle の項目。 |
Skin |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | image のサイズ | 0 |
実行例
up | checked |
---|---|
サンプルコード
private val actor by lazy { ImageButton( ImageButton.ImageButtonStyle().apply { up = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.YELLOW }) imageUp = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.RED }) down = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.MAGENTA }) imageDown = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.BLUE }) checked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.CYAN }) imageChecked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GREEN }) disabled = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GRAY }) imageDisabled = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.WHITE }) } ).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height isDisabled = false } } override fun create() { ... stage.addActor(actor) }
TextButton
Button クラスのサブクラス。
設定項目 | 型 | 説明 |
---|---|---|
text | String | ボタンテキスト。改行文字 ('\n') で改行される。 |
style | TextButton.TextButtonStyle | font, fontColor, downFontColor, checkedFontColor, overFontColor, checkedOverFontColor, disabledFontColor。加えて、Button.ButtonStyle の項目 |
Skin | ||
label.* | Label の設定項目。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | text が収まるサイズを算出 | 0 |
実行例
up | checked |
---|---|
サンプルコード
private val actor by lazy { TextButton("OK\n(Enter)", TextButton.TextButtonStyle().apply { font = BitmapFont() fontColor = Color.RED downFontColor = Color.BLUE checkedFontColor = Color.GREEN disabledFontColor = Color.GRAY up = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.YELLOW }) down = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.MAGENTA }) checked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.CYAN }) disabled = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.WHITE }) } ).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height label.setFontScale(5f, 10f) isDisabled = false } } override fun create() { ... stage.addActor(actor) }
CheckBox
TextButton クラスのサブクラス。
設定項目 | 型 | 説明 |
---|---|---|
text | String | ボタンテキスト。改行文字 ('\n') で改行される。 |
style | CheckBox.CheckBoxStyle | Drawable (checkboxOff, checkboxOn, checkboxOffDisabled, checkboxOnDisabled, checkboxOver)。 加えて、TextButton.TextButtonStyle の項目。 |
Skin | ||
label.* | Label の設定項目。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | text と checkbox (Drawable) が収まるサイズを算出 | 0 |
実行例
up | checked |
---|---|
サンプルコード
private val actor by lazy { CheckBox("On", CheckBox.CheckBoxStyle().apply { font = BitmapFont() checkboxOff = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.RED }) checkboxOn = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GREEN }) } ).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height label.setFontScale(5f, 10f) } } override fun create() { ... stage.addActor(actor) }
libGDX Scene2d の基礎
概要
libGDX で 2D UI Widget (Button, Label, CheckBox, etc.) を使用するための基礎となる Stage, Viewport, Layout についてまとめます。 (ゲームライブラリとしてではなく、マルチプラットフォームの GUI ライブラリとしての使用を模索中です。)
確認環境
- OS X El Capitan (10.11.6)
- Xcode 8.0
- Android Studio 2.2.2
- Multi-OS Engine Plugin 1.2.1
- Kotlin 1.0.4
- buildToolVersion 25.0.0
- compileSdkVersion 24
- libGDX 1.9.5-SNAPSHOT
参考情報
- Scene2d · libgdx/libgdx Wiki · GitHub
- Scene2d 公式文書。
- Viewports · libgdx/libgdx Wiki · GitHub
- Viewport 公式文書。
- Scene2d.ui · libgdx/libgdx Wiki · GitHub
- Scene2d UI 公式文書。
- libGDXの基礎5 Scene2Dを使う - Qiita
- 概要。
解説
Scene2d は基本的な 2D scene graph の機能を提供する。 Scene2d により以下が提供される。
- アクター (actor)
- 2D scene graph のノード。位置 (position)、サイズ (rectangular size)、原点 (origin)、スケール (scale)、回転 (rotation)、色 (color) を持つ。
- グループ (group)
- アクターのグループ。
- 描画 (drawing)
- 回転 (rotation)、スケール (scale) を考慮した描画を行う。
- 当たり判定 (hit detection)
- 回転 (rotation)、スケール (scale) を考慮した当たり判定を行う。
- イベント (event)
- 各アクターに入力イベントを分配する。
- アクション (action)
- 各アクターを時間に応じて変化させる。
Scene2d を基礎とした UI Widget が用意されている。 UI Widget 関連のクラスを図に示す。
Stage
Stage は 複数の Actor を子に持ち、Actor の root 要素になる。 Stage は InputProcessor であり、入力イベントを各 Actor に分配する。 Viewport により Stage を Screen に投影する方法を指定する。
基本的な使い方は以下のとおり。
class MyGdxGame : ApplicationAdapter() { private val stage by lazy { Stage() } override fun create() { Gdx.input.inputProcessor = stage Gdx.graphics.isContinuousRendering = false stage.addActor(Image(Texture("badlogic.jpg"))) } override fun resize(width: Int, height: Int) { stage.viewport.update(width, height, true) } override fun render() { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) stage.act(Gdx.graphics.deltaTime) stage.draw() } override fun dispose() { stage.dispose() } }
stage プロパティは var lateinit stage
でも良いが、stage プロパティを変更不可としたいため val stage by lazy
を使用している。
by lazy
を使用することにより、最初の get 呼び出しでインスタンスが生成されるようになる。
Viewport
Viewport により Stage を Screen に投影する方法を指定する。 Viewport のクラス階層を以下に示す。
各 Viewport における、Screen サイズ (単位はピクセル) と Stage サイズ (単位はユニット) の関係を以下に示す。 Stage を構成する単位 (Stage の論理的な画素) を 1 ユニットとする。
- ScreenViewport
- ScalingViewport
- Stage サイズを指定し、Screen に合わせて Scale (拡大/縮小) して表示する。Stage サイズは指定したサイズ。
- Scale 方法は以下のとおり。
- FitViewport
- ScalingViewport の fit と同じ。
- FillViewport
- ScalingViewport の fill と同じ。
- StretchViewport
- ScalingViewport の stretch と同じ。
- ExtendViewport
- Stage サイズを指定し、Screen に合わせて拡大/縮小して表示する。拡大/縮小に合わせて Stage サイズを変更する。
- FitViewport と同様に拡大/縮小した後に、Stage < Screen となっている辺の Stage サイズを変更する。
- 最大サイズが指定されていない場合は、Stage = Screen となるように Stage サイズを変更する。
- 最大サイズが指定されている場合は、指定した最大サイズに Stage サイズを変更する。
言葉では分かりづらいため図示すると以下のようになる。 赤は Screen サイズ (単位はピクセル)。 黒は Stage サイズ (単位はユニット)。
ScreenViewport (unitsPerPixcel = 1) | ScreenViewport (unitsPerPixcel = 2) |
---|---|
ScalingViewport (none, 640, 480) | ScalingViewport (fit, 640, 480) |
---|---|
ScalingViewport (fill, 640, 480) | ScalingViewport (stretch, 640, 480) |
ExtendViewport (640, 480) | ExtendViewport (640, 480, 800, 480) | ExtendViewport (640, 480, 800, 640) |
---|---|---|
表示例を得るために以下のソースコードを使用した。
class MyGdxGame : ApplicationAdapter() { companion object { private val TAG = MyGdxGame::class.java.name } private val stage by lazy { Stage(ScreenViewport()) /* Stage(ScreenViewport().apply { unitsPerPixel = 2f }) */ //Stage(ScalingViewport(Scaling.none, 640f, 480f)) //Stage(ScalingViewport(Scaling.fit, 640f, 480f)) //Stage(FitViewport(640f, 480f)) //Stage(ScalingViewport(Scaling.fill, 640f, 480f)) //Stage(FillViewport(640f, 480f)) //Stage(ScalingViewport(Scaling.stretch, 640f, 480f)) //Stage(StretchViewport(640f, 480f)) //Stage(ExtendViewport(640f, 480f)) //Stage(ExtendViewport(640f, 480f, 800f, 480f)) //Stage(ExtendViewport(640f, 480f, 800f, 640f)) } override fun create() { Gdx.app.logLevel = Application.LOG_DEBUG Gdx.app.debug(TAG, "create") Gdx.graphics.isContinuousRendering = false stage.addActor(object : Actor() { override fun draw(batch: Batch, parentAlpha: Float) { batch.end() ShapeRenderer().run { projectionMatrix = batch.projectionMatrix begin(ShapeRenderer.ShapeType.Filled) color = Color.RED rect(0f, 0f, stage.width, stage.height) Gdx.app.debug(TAG, "${stage.width} ${stage.height}") end() } batch.begin() } }) stage.addActor(Image(Texture("badlogic.jpg"))) } override fun resize(width: Int, height: Int) { Gdx.app.debug(TAG, "resize $width $height") stage.viewport.update(width, height, true) } override fun render() { Gdx.app.debug(TAG, "render") Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) stage.draw() } override fun dispose() { Gdx.app.debug(TAG, "dispose") stage.dispose() } }
レイアウト
UI Widget は自身のサイズと位置を決定しない。 親の UI Widget が子の UI Widget のサイズと位置を決定する。 UI Widget は親がサイズと位置を決定するためのヒントとなる最小サイズ、推奨サイズ、最大サイズを提供する。
UI Widget が描画 (draw) されるとき、最初に validate
メソッドが呼ばれる。
UI Widget のレイアウトが invalid のとき、描画の前に layout
メソッドが呼ばれる。
描画においては、layout
によりキャッシュされたレイアウト情報 (サイズと位置) を用いる。
invalidate
, invalidateHierarchy
メソッドを呼ぶことで、レイアウトを invalid にできる。
UI Widget の状態が変化してキャッシュされたレイアウト情報を再計算する場合には invalidate
メソッドを呼ぶ。
但し、最小サイズ、推奨サイズ、最大サイズに影響がない場合に限る。
サイズが変化せず、親 Widget が影響を受けない場合に使用する。
UI Widget の状態が変化して最小サイズ、推奨サイズ、最大サイズに影響が及ぶ場合には invalidateHierarcy
メソッドを呼ぶ。
親 Widget のレイアウトに影響が及ぶため、root までの全ての親 Widget で invalidate
メソッドが呼ばれる。