目次
OpenCVで格ゲーの動画を解析したい連載の7回目です。
前回、平均背景法について学びました。
今回はJavaFX+Kotlinで輪郭にラベルを付けるGUIアプリケーションを作成しようと思います。
JavaFXを使うとxml形式でGUIを記述できます(通称FXML)
また、IntelliJにはグラフィカルにFXMLを編集できるツールが付属しているので便利🤘
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-98.png)
最小構成の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); }
実行するとこんな感じのウィンドウが表示される。
簡単で良いですねぇ😀
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-90.png)
FXMLでGUIを作成してみる
src/main/resources
にFXMLファイルを作成する
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-87.png)
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に切り替えるとグラフィカルに編集できる。
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-89-1024x518.png)
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ファイルを書き換えるだけで画面を作っていける💯
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-99.png)
プロパティとのバインド
Controllerから画面上の要素にアクセスしたい。
FXMLからScene Builderを開いて、右ペインのfx:id
に適当な名前を入力する
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-91.png)
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にもあってこんな感じでハマるので注意。
概ね出来てきた。
輪郭にラベルを付けるツールが概ね出来てきた。
後は保存処理を作る。
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-92-1024x689.png)
保存処理を作る
java(kotlin)にはphpのserialize()
みたいな雑にオブジェクトを保存できる関数は無いっぽい。
なんでkotlinx.serializationを使ってみる。
READMEを参考にbuild.gradle.kts
に追記して再ビルドする。
以下のエラーが発生した。
Unresolved reference: KotlinCompilerVersion
以下を追記しないといけないらしい。
import org.jetbrains.kotlin.config.KotlinCompilerVersion
ビルドはできるが、IntelliJが補完してくれない。(No suggestions
になる)
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-93.png)
Reimport All Gradle Projects
をすれば直った。
以下の様にshift
を2回押すと表示される窓でreimport
で検索すると出てくる。
つーかこれ前もハマったやつじゃねーか!
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-94.png)
コードはリポジトリのREADMEを見ていただくとして、輪郭とラベルの情報をjsonに保存する事ができた。
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-95.png)
複数のエントリポイントを持ちたい
輪郭にラベルを付けるツールはだいたい出来たので、また別の処理を書いていく。
なので、同じプロジェクトで複数のエントリーポイント(メインクラス)を持ちたい。gradle.build.kts
のmainClassName
をmainClass
というパラメータで上書きできる様にする。
application { mainClassName = if (project.hasProperty("mainClass")) project.properties["mainClass"] as String else "jp..(中略)..MyApp" }
IntelliJの実行構成を新たに作成し、上記のGUIアプリとは異なるメインクラスを-PmainClass
に指定する。
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-97.png)
こうする事で、実行構成を切り替える事で、処理をまるごと切り替える事ができる。
![](http://t-kuni.sub.jp/wp-content/uploads/2020/05/image-100.png)
ちなみにここで結構ハマったポイントがあって
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件のフィードバック