目次
OpenCVで格ゲーの動画を解析したい連載の7回目です。
前回、平均背景法について学びました。
今回はJavaFX+Kotlinで輪郭にラベルを付けるGUIアプリケーションを作成しようと思います。
JavaFXを使うとxml形式でGUIを記述できます(通称FXML)
また、IntelliJにはグラフィカルにFXMLを編集できるツールが付属しているので便利🤘

最小構成のJavaFxアプリケーションを作成する
さっそく作っていく。
コードは以下の1ファイルだけで良い。
ちなみにStageはアプリケーションが表示される場所の事。例えばウィンドウなど。Sceneはページに該当し、複数のNodeで構成される。Nodeはテキストやボタンといった要素の事。
package jp.t_kuni.kotlin_app_skelton.views
import javafx.application.Application
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.text.Font
import javafx.scene.text.Text
import javafx.stage.Stage
class MyApp: Application() {
override fun start(stage: Stage?) {
val text = Text(10.0, 40.0, "Hello World!")
text.setFont(Font(40.0))
val scene = Scene(Group(text))
stage!!.setTitle("Welcome to JavaFX!")
stage.setScene(scene)
stage.sizeToScene()
stage.show()
}
}
fun main(args: String) {
Application.launch(args);
}
実行するとこんな感じのウィンドウが表示される。
簡単で良いですねぇ😀

FXMLでGUIを作成してみる
src/main/resourcesにFXMLファイルを作成する

FXMLの内容はこんな感じ。
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="jp.t_kuni.kotlin_app_skelton.views.Controller"
prefHeight="400.0" prefWidth="600.0">
</AnchorPane>
FXMLファイルを開いて、左下にあるタブをScene Builderに切り替えるとグラフィカルに編集できる。

FXMLファイルに対応するControllerクラスを用意する。
Controllerクラスにはボタンが押下されたときの処理などを記述していく。
package jp.t_kuni.kotlin_app_skelton.views
import javafx.fxml.Initializable
import java.net.URL
import java.util.*
class Controller: Initializable {
override fun initialize(p0: URL?, p1: ResourceBundle?) {
}
}
エントリーポイントとなるメインクラスがこんな感じ。FXMLLoaderでFXMLファイルを読み込んでSceneに登録している。
package jp.t_kuni.kotlin_app_skelton.views
import javafx.application.Application
import javafx.fxml.FXMLLoader
import javafx.scene.Scene
import javafx.scene.layout.Pane
import javafx.stage.Stage
class MyApp: Application() {
override fun start(stage: Stage?) {
val url = javaClass.getResource("/app.fxml");
val loader = FXMLLoader(url)
val root = loader.load<Any>() as Pane
val controller: Controller = loader.getController()
val scene = Scene(root)
stage!!.setTitle("Welcome to JavaFX!")
stage.setScene(scene)
stage.sizeToScene()
stage.show()
}
}
fun main(args: String) {
Application.launch(args);
}
実行すると空のウィンドウが表示される。
後はFXMLファイルを書き換えるだけで画面を作っていける💯

プロパティとのバインド
Controllerから画面上の要素にアクセスしたい。
FXMLからScene Builderを開いて、右ペインのfx:idに適当な名前を入力する

Controllerに以下の様に記述する事で、画面上の要素にアクセスできる。
変数名は前述のfx:idに合わせる必要がある。
class Controller : Initializable {
@FXML
private var imageView = ImageView()
なお、ImageView()でインスタンスを代入しているのはnull代入を避けたかったため。
nullを許容するならprivate var imageView: ImageView? = nullでも可。
OpenCVのMatクラスをレンダリングしたい
以下の様にjavafx.scene.imageに変換した後、ImageView.imageに代入すればOK
fun mat2image(mat: Mat): Image {
val buffer = MatOfByte()
Imgcodecs.imencode(".png", mat, buffer);
return Image(ByteArrayInputStream(buffer.toArray()));
}
イベントを取得しようとしてハマった
マウス座標を取得しようとしてマウス移動イベントを取得しようとしたが以下のエラーが発生した。
Caused by: javafx.fxml.LoadException: Error resolving onMouseMoved='#onMouseMove', either the event handler is not in the Namespace or there is an error in the script
引数の型がjavafx.scene.input.MouseEventではなくjava.awt.event.MouseEventになっていた。
javafxと同名のクラスがswingにもあってこんな感じでハマるので注意。
概ね出来てきた。
輪郭にラベルを付けるツールが概ね出来てきた。
後は保存処理を作る。

保存処理を作る
java(kotlin)にはphpのserialize()みたいな雑にオブジェクトを保存できる関数は無いっぽい。
なんでkotlinx.serializationを使ってみる。
READMEを参考にbuild.gradle.ktsに追記して再ビルドする。
以下のエラーが発生した。
Unresolved reference: KotlinCompilerVersion
以下を追記しないといけないらしい。
import org.jetbrains.kotlin.config.KotlinCompilerVersion
ビルドはできるが、IntelliJが補完してくれない。(No suggestionsになる)

Reimport All Gradle Projectsをすれば直った。
以下の様にshiftを2回押すと表示される窓でreimportで検索すると出てくる。
つーかこれ前もハマったやつじゃねーか!

コードはリポジトリのREADMEを見ていただくとして、輪郭とラベルの情報をjsonに保存する事ができた。

複数のエントリポイントを持ちたい
輪郭にラベルを付けるツールはだいたい出来たので、また別の処理を書いていく。
なので、同じプロジェクトで複数のエントリーポイント(メインクラス)を持ちたい。gradle.build.ktsのmainClassNameをmainClassというパラメータで上書きできる様にする。
application {
mainClassName = if (project.hasProperty("mainClass")) project.properties["mainClass"] as String else "jp..(中略)..MyApp"
}
IntelliJの実行構成を新たに作成し、上記のGUIアプリとは異なるメインクラスを-PmainClassに指定する。

こうする事で、実行構成を切り替える事で、処理をまるごと切り替える事ができる。

ちなみにここで結構ハマったポイントがあって
kotlinでコンパイルされたクラスのclasspath名は、末尾にKtを付けないといけないらしいっす。
例えばAppクラスならAppKtと指定する必要がある。Ktを付け忘れるとクラスが見つからなくて以下のエラーが出ます。
エラー: メイン・クラスjp.t_kuni...を検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: jp.t_kuni...
今日はここまで!
Webエンジニアをやっています
UX/UIデザインからプログラミング、DB設計、SEO、インフラ構築など幅広く対応してます
PHP/PHPUnit/Laravel/Vue/Nuxt/Docker/Terraform
ご連絡はTwitterのDMまで。

「格ゲーの動画を解析したい 〜JavaFXでGUIツールを作る〜 7フレーム目」への1件のフィードバック