プロジェクト

全般

プロフィール

発表ネタ JJUG CCC 2017 Spring向け

日本Javaユーザーグループ(JJUG)主催のJJUG CCC 2017春に向けた準備と発表後のフォローページです。

テーマ検討

  • 18年間継続したJava読書会BOF(コミュニティ)の運営の話
    → これは懇親会時のLTにしよう・・・
  • JavaFXでちょっとしたデスクトップツール作成
    ガジェット風のアプリ(アナログ時計、カレンダー、画像表示、など)の作り方・コマンドラインオプションと設定ファイル・デプロイまで含めて
  • JavaFXのアプリケーション開発環境とツール
    Scene Builder、ScenecView、インストーラー作成(RPM、MSI)あたりか

目玉はないなぁ・・・

テーマ

締め切り前に公募(CfP)で登録したはずなのですが、なぜか登録できていなくて、ぼそっとTwitterで呟いたら拾ってもらえました。
(JJUG CCCの公募登録はGithubを活用して作られているのですが、ちょっと難易度が高いようです:自分だけかも)

タイトル

「JavaFXでデスクトップガジェット風プログラムを作る」

登録内容

デスクトップの脇で動くちょっとしたプログラム、例えば時計、カレンダーなどいわゆるデスクトップガジェットのようなプログラムをJavaFXで作成します。

ガジェット風なプログラムでは、表示領域を極力少なくするためウィンドウ枠がなく背景を透明にした表示とすることが一般的です。その場合、OS(ウィンドウシステム)の機能でのウィンドウの位置・大きさの変更、終了といった操作ができないため、ドラッグ&ドロップ操作などで位置や大きさの変更、終了を行う必要が生じます。その操作は2in1
PCなどのタッチパネルに対応しているとなおいいです。

設定ファイルやコマンドラインオプションでの制御の他、次回起動時には最後の位置や大きさを継続できるよう、前回の状態を覚えておくことも必要です。
JavaはデフォルトではCPUとメモリを大食いする傾向がありますので、ガジェット風なプログラムを複数動かしてもCPUやメモリを喰わないように省リソース設定をしておくこともお作法として必要です。そのためにはデプロイ方法を含めて対応することが必要となります。

これらを踏まえて、JavaFXでガジェット風のプログラムを作る方法を紹介します。

カテゴリ

Java SE

難易度

初心者

簡単な自己紹介

Javaは1996年(1.0)から触っています。GUIはApplet(AWT)、Netscape
IFC、Swing、そしてJavaFXを個人と時折仕事でも使ってきました。
1998年よりJava読書会に参加、1999年よりJava読書会BOF代表を務めています。

発表フォローアップ

発表後に補足する情報です。

発表スライド

https://www.slideshare.net/torutk/jjugccc2017spring-m6-javafx

スライド資料補足

開発ツール

開発ツールのURL

Linuxディストリビューションに含まれるOpenJDKの場合

  • OpenJDKにはJavaFXが含まれていませんが、OpenJFXを追加で入れると利用できるようになります。ただし、ディストリビューションによってOpenJFXが用意されているもの、されていないものがあります。
    → Fedora:なし、Ubuntu:あり

CPU使用率の低減

資料に記載していませんが、アニメーションを使用した際のCPU使用率を低減する方法を以下に書いています。

JavaFXコミュニティ

日本のJavaFXコミュニティ&メーリングリストです。JavaFXユーザーグループでは過去(数年前になりますが)何回かJavaFX勉強かいを開催していました。

発表の方針

ちょっとしたプログラムであれば、自分で作って自分で使えると楽しいですよ。
プログラマーであれば、人の作ったプログラムを動かすだけでなく、自分でプログラムを作ってうごかしたいですよね。
不満があっても、自分で作っているなら改善できるよね。

作って動かして楽しいのはやはりGUIがあるプログラムでしょう。
JavaFX は、エフェクト、アニメーション、2D/3D、サウンド、動画、Webなどが揃って今時のGUIプログラムを作るのに便利な機能が揃っています。
また、JavaFXで作ったGUIプログラムであれば、Windows、Linux、Mac OSなどで動きます。

スライド

今回は、JavaFXでスライドショーを実行しようと計画しています。
といっても、Javaコードをガシガシ作るのは大変なので、Scene Builderでスライド1枚を1つのFXMLで記述し、FXMLファイルを順次切り替えて表示する方法で実現します。

FXMLでスライドを作る際のメモ

  • スライドは全画面表示としますが、プロジェクタの機種によって画面解像度が異なるので、画面レイアウトをピクセル単位で設定するとずれてしまいます。
    そこで、BorderPaneを使ってCENTERにスライドの主要コンテンツを配置します。すると、画面を大きくしたときにコンテンツを最大限大きく表示してくれます。
  • ImageView はResizeできない部品なので、BorderPaneのCENTERに配置して画面を大きくしても画像は大きくなりません。
    そこで、Resize可能な独自のImageView風な部品を用意しました。
  • スライドのタイトルはBorderPaneのTOPにLabelで配置します。LabelのmaxWidth プロパティを、MAX_VALUEに設定すると、画面左右の幅一杯に広がるLabelとなります。
    文字の大きさは、Fontプロパティでは単位がpixelしか選択できないので、DPIの大きさが違うディスプレイに表示すると文字の実寸サイズが大きく異なることがあります。そこで、Style プロパティでCSS("-fx-font-size: 3em")のようにemを単位に指定します。
    Labelの文字を中央配置するには、alignmentプロパティをCENTERに設定します(デフォルトはCENTER_LEFT)。
  • スライドのコンテンツは箇条書きの文字が多いので、Labelで複数行編集を使って複数行の文字を記載します。
    Wrap Textのプロパティを有効にすると、ラベルの横幅が短いときに折り返し(改行)されます。ただし、ラベルの縦幅が改行された分必要になります。
    → BorderPaneのCENTERに配置
    → AnchorPaneで上下にAnchorを設置
  • CSSファイルを用意すると楽です。

CSSファイルサンプル

.label {
    -fx-font-family: "Meiryo";
}

.label.heading {
    -fx-font-size: 6em;
    -fx-alignment: center;
}

.label.body {
    -fx-font-size: 4em;
}
.label.code {
    -fx-font-size: 3.5em;
    -fx-font-family: "Consolas";
}

JavaFXのスライドをPDF化する模索

JavaFXでプレゼンテーションを実施した場合、そのコンテンツをWebにアップするために、PDFやPPTなどの文書に変換する必要があります。

SVG出力

まず、SVG出力を調べてみました。

印刷

印刷機能を追加して、印刷先をPDFとする方法を調べてみました。まずは印刷ボタンを付け、最小限のコードを実装しました。(エラー処理は省いています)

        PrinterJob job = PrinterJob.createPrinterJob();
        Window topWindow = node.getScene().getWindow();
        job.showPrintDialog(topWindow); 
        job.printPage(node);
        job.endJob();   

印刷ボタンを押すと、印刷ダイアログが表示され、[Microsoft Print to PDF]を選択、印刷を横に指定して印刷するとPDFファイルを指定するファイルダイアログが表示されます。ファイルを指定すると、PDFに出力されます。

ファイルを開くと、左90度に回転した状態で表示されました。しかし、画面の右端・下端が欠けてしまいました。

印刷範囲にきっちり収めるには、印刷範囲を算出し、印刷したいNodeの大きさが印刷範囲に収まるよう座標変換のScaleを設定します。

        PrinterJob job = PrinterJob.createPrinterJob();
        Window topWindow = node.getScene().getWindow();
        job.showPrintDialog(topWindow);
        Printer printer = job.getPrinter();
        PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.EQUAL);
        double scaleX = pageLayout.getPrintableWidth() / node.getWidth();
        double scaleY = pageLayout.getPrintableHeight() / node.getHeight();
        Scale scale = new Scale(scaleX, scaleY);
        node.getTransforms().add(scale);
        job.printPage(node);
        node.getTransforms().remove(scale);
        job.endJob();            

まだ表示が欠けていたので、ノードの大きさではなく、トップレベルウィンドウ(Stage)の大きさをベースに印刷範囲へスケールさせ、それでも欠けることがあったので、0.9を掛けて調整しました。

        double scaleX = pageLayout.getPrintableWidth() / topWindow.getWidth() * 0.9;
        double scaleY = pageLayout.getPrintableHeight() / topWindow.getHeight() * 0.9;

デモプログラム

  1. TinyGadget プログラム
  2. ImageGadget プログラム
  3. EarthGadget プログラム

リポジトリ

https://github.com/torutk/jjugccc2017spring-javafx

TinyGadget プログラム

ウィンドウ枠なし、透明ウィンドウのサンプルプログラム

TinyGadget-1.png (TinyGadgetの画面)

シーングラフ

StackPane
  +-- Circle
  +-- Text

ウィンドウ枠の非表示と背景透明化

ドラッグ操作でウィンドウの移動

ホイール操作でウィンドウの大きさ変更

タッチ操作を考慮しない処理

ScrollEvent のハンドラで、ホイールの回転方向を getDeltaY で取得し、ウィンドウの大きさを回転方向に応じて変更します。

    // ホイール操作でウィンドウの大きさを変更
    scene.setOnScroll(event -> zoom(event.getDeltaY() > 0 ? 1.1 : 0.9, stage));
    private void zoom(double factor, Stage stage) {
        stage.setWidth(stage.getWidth() * factor);
        stage.setHeight(stage.getHeight() * factor);
    }
タッチ操作を考慮する処理(1)
        // ホイール操作でウィンドウの大きさを変更
        scene.setOnScroll(event -> {
            if (event.getTouchCount() != 0 || event.isInertia()) {
                return;
            }
            zoom(event.getDeltaY() > 0 ? 1.1 : 0.9, stage);
        });

タッチ操作では、ウィンドウを上下左右いずれかの方向にドラッグしたときにScrollEventが発生します。
タッチ操作によるScrollEventのときは、ウィンドウの大きさを変更しないようにします。そのため、getTouchCountメソッドでタッチ点数を取得、0以外であればタッチ操作と判断してスキップします。また、タッチ操作のドラッグ時に発生する慣性イベントもスキップします。

タッチ操作を考慮する処理(2)

Java Advent Calendar 2015 において @skrb さんが解説している方法です。

ウィンドウの大きさに応じて Circle の大きさを変更

Circle は、isResizableメソッドがfalseを返すので、レイアウト機能でのサイズ変更を受けません。
そこで、ルートノード(StackPane)の幅・高さのうち小さい方の値を1/2して半径とするようにバインディングを設定します。

    circle.radiusProperty().bind(Bindings.min(root.widthProperty(), root.heightProperty()).divide(2));

circleの半径はradiusProperty()で取得できるので、この半径と、ルートノード(root)のwidthProperty()またはheightProperty()のうち小さい値の1/2とをバインディングします。
2つのプロパティのうち小さい方を取得するBindings.minと、プロパティの値を1/2するdivideを使用しています。

StackPaneは、ノードの中心位置を画面中心に置くようにレイアウトするので、circleのcenterXとcenterYは明示的に制御する必要はありません。

ただし、このバインディングを設定すると、StackPaneの大きさがCircleではなくTextで決まるようになり、初期起動時は次のようになってしまいます。

stackpane_circle_bind-1.png (Circleの半径をStackPaneの幅・高さにバインドした後)

回避方法は、Scene または Stage に明示的にサイズを指定することです。

-         Scene scene = new Scene(root);
+         Scene scene = new Scene(root, 320, 200);

または

        stage.setWidth(320);
        stage.setHeight(200);

コンテキストメニューの表示

ContextMenuを生成し、ルートノードのsetOnContextMenuRequestedメソッドで可視化(show)します。

        ContextMenu popup = createContextMenu(stage);
        root.setOnContextMenuRequested(event -> {
            popup.show(stage, event.getScreenX(), event.getScreenY());
        });

ContextMenu には、MenuItemを任意個数積むことができます。今回は、「終了」メニュー項目を1つ設けます。
「終了」メニュー項目が選択されると、ウィンドウを閉じる要求イベント(WINDOW_CLOSE_REQUEST)を発行します。これは、ウィンドウ枠(タイトルバー)の閉じるボタン(Windows OSなら[X]ボタン)を押したときと同じになります。
デフォルトの振る舞いではWINDOW_CLOSE_REQUESTイベントでウィンドウが閉じられます。

    private ContextMenu createContextMenu(Stage stage) {
        MenuItem exitItem = new MenuItem("終了");
        exitItem.setStyle("-fx-font-size: 2em");
        exitItem.setOnAction(event -> {
            stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));
        });        
        ContextMenu popup = new ContextMenu(exitItem);
        return popup;
    }

最初、WINDOW_CLOSE_REQUESTではなく、Platform.exit()を呼ぶようにしていました。
しかし、後でStageが閉じるときのイベント処理(setOnCloseRequest)が、Platform.exit()では反応しません。
そこで、上述の実装にしました。

タッチ操作(ピンチ)でウィンドウの大きさ変更

        scene.setOnZoom(event -> zoom(event.getZoomFactor(), stage));

ピンチ操作をすると、ジェスチャーイベントZoomが発生するので、これを捕まえて先に実装しているzoomメソッドを呼び出します。
ピンチアウト(拡大)かピンチイン(縮小)かは、イベントのgetZoomFactorで判別します。

プログラム終了時に状態の保存と次回起動時に状態の復元

Java Preferences APIを使って、ユーザー毎に状態を保存します。

終了時のタイミングとして、ウィンドウが閉じられたとき、あるいはJavaVMが終了するときのタイミングが取れますが、今回はウィンドウが閉じられたときをタイミングとします。

        stage.setOnCloseRequest(event -> saveStatus(stage));

状態は、キー/値 の形式で保存します(マップに似ています)。

    private Preferences prefs = Preferences.userNodeForPackage(this.getClass());
    private static final String KEY_STAGE_X = "stageX";
    private static final String KEY_STAGE_Y = "stageY";
    private static final String KEY_STAGE_WIDTH = "stageWidth";
    private static final String KEY_STAGE_HEIGHT = "stageHeight";

    private void saveStatus(Stage stage) {
        prefs.putInt(KEY_STAGE_X, (int) stage.getX());
        prefs.putInt(KEY_STAGE_Y, (int) stage.getY());
        prefs.putInt(KEY_STAGE_WIDTH, (int) stage.getWidth());
        prefs.putInt(KEY_STAGE_HEIGHT, (int) stage.getHeight());
    }

起動時に保存した状態があればそれを使用します。なければ、デフォルト値を使用します。

    private void loadStatus(Stage stage) {
        stage.setX(prefs.getInt(KEY_STAGE_X, 0));
        stage.setY(prefs.getInt(KEY_STAGE_Y, 0));
        stage.setWidth(prefs.getInt(KEY_STAGE_WIDTH, 320));
        stage.setHeight(prefs.getInt(KEY_STAGE_HEIGHT, 200));
    }

Windowsでは、保存した状態はレジストリ内に保持されます。

preferences_registry-1.png (Windows上でPreferences APIで保存したレジストリの例)

ネイティブパッケージング

JDKには、OS固有のインストーラーを作成するjavapackagerが含まれています。

javapackagerでWindows用にMSI形式インストーラを作成する場合、別途WiX Toolsetを使用します。

実行可能JARファイルと、JVMオプションを指定した起動コマンドを記述したスクリプトファイル(バッチファイル)を1つのファイルにまとめると便利です。

TinyGadget.src.bat
%echo off

set JAVA_OPT=-Xms8m -Xmx64m -Xss256k -XX:+UseSerialGC

java %JAVA_OPT% -jar "%~f0" %*

exit /b %ERRORLEVEL%

%~f0 は、バッチファイル自身のフルパスを展開します。

バッチファイルとJARファイルの結合(Windows)
C:\work\TinyGadget> copy /b TinyGadget.src.bat+dist\TinyGadget.jar dist\TinyGadget.bat

ImageGadget プログラム

ウィンドウ枠なしで、指定したフォルダ以下にある画像ファイルを一定間隔で順次表示するプログラム

ImageGadget-1.png (画像表示ガジェットの表示例)

NetBeansでJavaFX FXMLアプリケーション雛形生成

  • [ファイル]メニュー > [新規] > [プロジェクト]で「新規JavaFXMLアプリケーション」ダイアログを開き
    プロジェクト名にImageGadget
    FXML名にImageGadgetView
    アプリケーションクラスの作成に、imagegadget.ImageGadgetApp
JavaFXアプリケーションの命名例

アプリケーション名を Alfa とした場合

  • FXMLファイル名: AlfaView.fxml
  • FXMLファイルに対応するコントローラ名: AlfaViewControl
  • Application継承クラス名: AlfaViewApp

としています。画面が複数(FXMLファイルとFXMLファイルに対応するコントローラがそれぞれ複数)あるときは、アプリケーション名にちなむ画面をメイン画面とし、上述の命名を使用します。メイン画面から呼び出されるサブ画面(FXMLファイルとそれに対応するコントローラ)はそれぞれ画面名にちなんだ命名をします。

Scene Builderでシーングラフ編集

NetBeansのJavaFX FXMLアプリケーション雛形生成で作成されるFXMLファイルには、次のシーングラフが定義されています。

AnchorPane
  +-- Button
  +-- Label

これを、次のように変更します。

StackPane
  +-- ImageView
Tips ルートノードを変更する場合の操作

ここで、Scene Builder上でAnchorPaneを削除操作すると、AnchorPane属性に定義されるコントローラクラス名の情報も削除されてしまいます。

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="imagegadget.ImageGadgetViewController">

そこで、Scene Builder上でAnchorPaneを選択し、右クリックでポップアップメニューから[Wrap in] > [StackPane]を選択します。
すると、シーングラフが次のようになります。

StackPane
  +-- AnchorPane
        +-- ...

AnchorPaneの属性に定義されていたコントローラクラス名はStackPaneの属性に設定(移動)されます。ここでAnchorPaneを削除します。

ImageViewの大きさを画面サイズに追従

ImageViewは、Resizableではないので、画面の大きさに追従させるコードを記述します。

  • ImageGadgetViewController.java
    public class ImageGadgetViewController implements Initializable {
    
        @FXML
        private Region root;
        @FXML
        private ImageView imageView;
    
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            imageView.fitWidthProperty().bind(root.widthProperty());
            imageView.fitHeightProperty().bind(root.heightProperty());
            imageView.setPreserveRatio(true);
        }    
    
    }
    

ウィンドウ枠非表示・透明化

先のTinyGadgetとほぼまったく一緒です。

ドラッグ&ドロップでディレクトリ指定

ドラッグ&ドロップで画像表示対象ディレクトリを指定します。
外部ツール(Windows OSならエクスプローラー)から、画像ファイルのあるディレクトリ(フォルダ)を、ドラッグ&ドロップでこのImageGadgetプログラムに渡します。
つまり、ドラッグ&ドロップのターゲット側イベント(ドロップ)を記述すればよいことになります。

ドラッグ&ドロップのターゲット側には、次の2つのイベントが順に発生します。

  1. DragEvent.DRAG_OVER
  2. DragEvent.DRAG_DROPPED

ドロップターゲットには、シーンあるいはノードがなることができます。
FXMLと対応づけられるコントローラークラスでは、シーンを直接は参照できないので、ルートノード(StackPane)をターゲットとします

上述の2つのイベントに対して、それぞれイベントハンドラメソッド handleDragOver と handleDragDropped を定義します。
アノテーション @FXML をメソッドに付けると、FXMLから紐づけることができます。

DragEvent.DRAG_OVER のイベント処理メソッド handleDragOver の記述

    @FXML
    private void handleDragOver(DragEvent event) {
        Dragboard dragboard = event.getDragboard();
        if (dragboard.hasFiles() && dragboard.getFiles().get(0).isDirectory()) {
            event.acceptTransferModes(TransferMode.ANY);
        }
        event.consume();
    }
  • ドロップで渡されるデータがファイルであり、かつそれがディレクトリであるときにドラッグ&ドロップが成立するようにします。
    ドラッグ&ドロップでは複数ファイルを渡すことができるため、イベントから取得するファイル情報はリストとなっています。ここでは、暫定的に最初の1つ目だけをチェックしそれがディレクトリであるかを判別しています。受け入れ対象となる場合は、イベントのacceptTransferModesを呼びます。
  • eventのconsume()を呼ぶことで、指定したNodeの後ろ(下)にあるNodeあるいはSceneにイベントが伝搬することを防ぎます。
    この例では、ここでconsumeしないと、ルートノード(StackPane)の後ろにあるSceneにDragOverイベントが伝搬してしまいます。

DragEvent.DRAG_DROPPED のイベント処理メソッド handleDragDropped の記述

    @FXML
    private void handleDragDropped(DragEvent event) {
        Dragboard dragboard = event.getDragboard();
        if (dragboard.hasFiles() && dragboard.getFiles().get(0).isDirectory()) {
            model = new ImageGadgetViewModel(Paths.get(dragboard.getFiles().get(0).toURI()));                
            event.setDropCompleted(true);
        } else {
            event.setDropCompleted(false);
        }
        event.consume();
    }
  • ドロップで渡されるデータがファイルであり、かつそれがディレクトリであるときにドロップ処理を実施します。
  • ドロップで渡されたディレクトリのパスで新しくImageGadgetViewModelを生成します。

Scene Builderで、StackPaneのイベントに上述2つのメソッドを設定します。

scenebuilder_stackpane_drag-1.png (ドラッグ&ドロップのイベント紐づけ)

参照資料

ドキュメントは次が参考となります。

調査メモ

HiDPI

  • Windows 10 240DPI(13.3"ディスプレイ 2560×1440)上でJavaFXプログラムを動かす
    ⇒ HiDPI対応しているので、1706×960 のStageを表示すると画面一杯に表示される
    既に、ピクセル(px)指定が仮想ピクセル(DIP: Device Independet Pixel)に対応していました
    -Dprism.allowhidpi=falseを指定すると、ピクセルが物理デバイス(2560×1440)に合う
  • 文字の大きさをpixelで指定した場合
    -Dprism.allowhidpi=falseを指定すると、文字の画面上の寸法が小さくなりました
  • 文字の大きさを、単位をemで、FXMLのstyle属性で指定した場合
    • JDK 8u131で実行
      CSS Style形式で-fx-font-size: 3emのように指定したところ、-Dprism.allowhidpi=falseを指定してもしなくても画面上の寸法は同じ(変化なし)でした
    • JDK 9 EA build167で実行
      CSS Style形式で-fx-font-size: 3emのように指定したところ、-Dprism.allowhidpi=falseを指定したら画面上の寸法は小さくなりました

ヒープサイズ

JVMヒープサイズ(デフォルト)

  • 64bit JVMの初期ヒープは、上限なく物理メモリの1/64の模様
    16GB搭載機: 256MB、32GB搭載機: 512MB

CPU使用

  • パラレルGCは、CPU数(スレッディング型CPUではスレッディング総数)を使用

Preferences API

  • キーと値の組でデータを保存(key-value map)
  • 値は、String/int/long/float/double/boolean/byte[] の読み書きがサポート(専用メソッドが提供)
  • XMLファイルからのインポート可能
  • 保存タイミングについて要検討

JARとバッチ(シェルスクリプト)の一体化

詰まったことメモ

Platform.exitで終了すると setOnCloseRequest リスナーが発動しない

×ボタンではなく、メニューからPlatform.exitを呼んで終了すると setOnCloseRequest リスナーが発動しない

  • 通常、[×]ボタンを押してウィンドウを閉じます(プロセスを終了)。この場合、Stage#setOnCloseRequestリスナーが呼ばれます。
  • ウィンドウの枠を消した場合(StageStyle.TRANSPARENT)、コンテキストメニュー等からPlatform#exitを呼び出してプロセスを終了させる方法があります。この場合、Stage#setOnCloseRequestリスナーは呼ばれません。
    また、Stage#closeメソッドを呼んでも同様setOnCloseRequestリスナーは呼ばれません。

そこで、Platform.exitではなく、Stage#fireEventでWINDOWS_CLOSE_REQUESTイベントを発行します。

        exitItem.setOnAction(event -> {
            stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));
        });

Stage#fireEventで、WINDOW_CLOSE_REQUESTを発生させると、Stageのデフォルトの振る舞いであるプロセスを終了する処理が実行されます。
ウィンドウ枠を表示した際に[X]ボタン(閉じるボタン)を押したときと同じ動きです。

コントローラ内でウィンドウが表示(可視化)されたイベントを拾う

Stageの参照が利用できるApplication派生クラスでは次のようにウィンドウが表示されたイベントを拾えます。

primaryStage.setOnShown(this::nextImage);

コントローラでは、Stageの参照が基本利用できないので、他の方法がないか要調査。

  • initializeメソッド内で記述したい

操作メモ

ネイティブインストーラ作成手順

  • NetBeans IDEの左側ペインでTinyGadgetプロジェクトを右クリック、[プロパティ]を選択
  • 「プロジェクト・プロパティ」ダイアログの左側ペインで[デプロイメント]を選択、右側ペインで[ネイティブ・パッケージングの有効化]にチェックを付ける
  • NetBeans IDEの左側ペインでTinyGadgetプロジェクトを右クリック、[パッケージとして] > [MSIインストーラ]を選択

しばらくすると、MSI形式のインストーラが作成される

プレゼン環境

JJUG CCCの会場で用意されるプロジェクタ

事前のアナウンスでは、VGAコネクタ、解像度は1024×768とのことです。
会場によってプロジェクタ機種が異なるので、固有の差異があると思われます。

持参PC(TOSHIBA dynabook KIRA L93/39M)は micro HDMI出力端子のため、別途micro HDMI→VGA変換器(KanaaN)を持参しています。
昨年までの実績からは、

  1. 画面の左右と下が表示欠落する
  2. 画面の左右が表示欠落する

といった状況が発生しています。

自宅での模擬環境

XGA(4:3)ディスプレイへの表示が欠ける

プロジェクタ(XGA)を模擬した液晶ディスプレイの表示が欠ける、縦につぶれる

自宅にはプロジェクタがないので、液晶ディスプレイ(EIZO FlexScan S2000)のVGA端子に接続し、液晶ディスプレイ側で解像度を1024×768に設定しています。
すると、やはり上下左右が表示欠損してしまいます。

  • 液晶ディスプレイの設定メニュー > ピクチャー調整 > 解像度 で値を1600×1200から1024×768に変更
  • 液晶ディスプレイの設定メニュー > ピクチャー調整 > クロック で値を1600から1125に変更
    → 左右の表示欠損がほぼなくなるが、上下は表示欠損が解消せず

問題解決

NetBeans の設定

プレゼンテーションで表示するNetBeansのフォントを大きくします。

NetBeansの全体のフォントの大きさを指定する

起動時のコマンドラインオプション --fontsize N で、NetBeansのフォントの大きさを指定します。メニューや各ペインの文字の大きさを指定することができます。

Windows OSの場合、NetBeansのショートカットを作成し、次のように設定します。

"C:\Program Files\NetBeans 8.2\bin\netbeans64.exe" --userdir d:\work\netbeans\forPresentaion --fontsize 28

NetBeans のエディタ

エディタ部分は、キー操作(ショートカット)で文字の大きさを調性できます。

NetBeansキーマップ使用時

テキストを拡大 Alt + マウスホイール(上)
テキストを縮小 Alt + マウスホイール(下)

Emacsキーマップを使用するときは、ショートカットが未割当なので、適宜割り当てておくと便利でしょう。

TinyGadget-1.png 表示 - TinyGadgetの画面 (159 KB) 高橋 徹, 2017/05/08 00:19

preferences_registry-1.png 表示 - Windows上でPreferences APIで保存したレジストリの例 (35.1 KB) 高橋 徹, 2017/05/08 06:48

stackpane_circle_bind-1.png 表示 - Circleの半径をStackPaneの幅・高さにバインドした後 (12.4 KB) 高橋 徹, 2017/05/08 23:54

scenebuilder_stackpane_drag-1.png 表示 - ドラッグ&ドロップのイベント紐づけ (168 KB) 高橋 徹, 2017/05/11 23:20

ImageGadget-1.png 表示 - 画像表示ガジェットの表示例 (263 KB) 高橋 徹, 2017/05/11 23:33

クリップボードから画像を追加 (サイズの上限: 1 GB)