2014年01月04日(土) [長年日記]
■ [java] JavaFXでFXMLを読み込む
JavaFXを使ってみる。公式ドキュメントは
かな。日本語の情報はJavaFX 2で始めるGUI開発の連載くらいしか見当たらないのでこれを順番に読んでみる。この連載は目次が見当たらず不便。連載の第1回ではFXMLを読み込む例が説明されているのでそれを試してみる。
メインクラス
JavaFXにはjavafx.application.Applicationクラスが用意されている。ひとまずはこれのサブクラスでmain()を実装すればよさそう。
package hello; import java.io.IOException; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class Hello extends Application { @Override public void start(Stage primaryStage) throws IOException { Parent root = FXMLLoader.load(getClass().getResource("Hello.fxml")); primaryStage.setScene(new Scene(root)); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
- main()ではApplication.launch()を呼び出す。
- そうするとApplication#start()が呼ばれるようなので、ここでメインウィンドウを作成する。
- StageがSwingのJFrameみたいなものなのかな。
- StageにはSceneを設定している。
- SceneにはParentを設定している。
- ParentはFXMLLoader.load()を使ってFXMLから作れるみたい。
FXML
メインクラスでロードしているFXMLは次のような感じ。
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.AnchorPane?> <AnchorPane id="AnchorPane" prefHeight="200" prefWidth="300"> <children> <Label text="ハロー" /> </children> </AnchorPane>
- ルート要素をAnchorPaneにしている。このクラスがParentのサブクラスだからFXMLLoader.load()の戻り値をParent型の変数に代入できるみたい。
- AnchorPaneの大きさを指定していて、それがウィンドウの大きさに影響する。
- FXMLの要素にはimportしたクラス名を指定できる。
実行結果
Helloを実行すると次のようなウィンドウが表示された。
2014年01月05日(日) [長年日記]
■ [java] JavaFXでイベントハンドリング
JavaFX 2で始めるGUI開発 第2回 シーングラフとFXMLを読んだ。コントローラクラスでGUIからのイベントを受け取る方法が説明されていたので試す。
FXML
掛算を行う画面を作ってみた。画面とそのFXMLは次の通り。
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.HBox?> <AnchorPane id="AnchorPane" prefHeight="200.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="hello.HelloController"> <children> <HBox alignment="CENTER" spacing="10.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <children> <TextField fx:id="leftField" onKeyReleased="#handleKey" prefWidth="50.0" /> <Label text="×" /> <TextField fx:id="rightField" onKeyReleased="#handleKey" prefWidth="50.0" /> <Label text="=" /> <Label fx:id="resultLabel" text="" /> </children> </HBox> </children> </AnchorPane>
- xmlnsのURLが前回と違うのは、Scene Builderがこう書き換えてしまうから。
- ルート要素のAnchorPaneタグのfx:controller属性でコントローラとするクラスの名前を指定している。
- コントロールを横一列に並べるのにHBoxを使ってみた。
- 入力を受け付けるTextFieldタグと結果出力用のLabelタグのfx:id属性でそれぞれのフィールドに名前を付けている。
- TextFiledタグのonKeyReleased属性で、キーボードのキーが離されたイベントを処理するコントローラクラスのメソッド名を指定している。
コントローラクラス
FXMLで指定したコントローラクラスのコードは次の通り。
package hello; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; public class HelloController { @FXML private TextField leftField; @FXML private TextField rightField; @FXML private Label resultLabel; @FXML public void handleKey(KeyEvent event) { try { double left = Double.parseDouble(leftField.getText()); double right = Double.parseDouble(rightField.getText()); resultLabel.setText(String.valueOf(left * right)); } catch (NumberFormatException | NullPointerException ex) { resultLabel.setText(""); } } }
- FXMLで名前を付けた要素それぞれに対し、同名のメンバ変数を用意している。@FXMLアノテーションを付けることで、FXMLで定義した要素のオブジェクトを自動的に参照するようになるみたい。
- FXMLで指定したキーreleaseイベントのハンドラメソッドを用意している。これにも@FXMLアノテーションを付ける。メソッドの引数がKeyEventになるということはNode#onKeyReleasedの型から分かるのだと思う。
メインクラス
メインクラスは前回と同じでよい。以上で掛算画面が動作するようになった。
2014年01月07日(火) [長年日記]
■ [vim] Vim 7.4.135がmakeのディレクトリ変更を検出しない
Vimの最新のソースをMercurialから取ってきてコンパイルしてみたのだけれど、:make したときにエラー箇所にジャンプしなくなっていることに気づいた。
Vimにはmakeコマンドが出力する「Enter directory」といった文字列を解釈してよきに計らってくれる機能があるんだけど、これが機能していないのが原因みたい。古いバージョンの7.2.411だとちゃんとジャンプするのでerrorformatオプションの内容を比べてみたところ、古いバージョンだと
%D%*\a: Entering directory `%f'
という設定だった所が
%D%*\a: Entering directory [`']%f'
に変わっていた。試しにerrorformatオプションの該当箇所を古いバージョンの設定に変更してみると正しくジャンプするようになった。
Vimの最近の変更を調べてみたら、issueに
というのがあって、
でそれへの対応が入っている感じだった。この変更でerrorformatオプションの設定が変わっていて、手元の環境だとこのせいで正しく動かなくなってしまうみたい。「[`']」という正規表現(なんだと思う)が正しく解釈できていないのかな?
ひとまず、件の変更を元に戻す修正を手元のVimのソースに対して行ってコンパイルし直したら、エラー箇所にちゃんとジャンプするようになった。
2014年01月08日(水) [長年日記]
■ [java] JavaFXでプログラム終了処理
JavaFX 2で始めるGUI開発 第3回 コントロール その1とJavaFX 2で始めるGUI開発 第4回 コントロール その2を読んだ。基本的なコントロールが色々紹介されている。Swingとほぼ同じかなという感想。
気になったのは、「JavaFXのアプリケーションを終了させるためには、javafx.application.Platformクラスのexitメソッドをコールします。」という所。PlatformクラスというのはSwingUtilitiesみたいなクラスだった。そのexitメソッドは
If this method is called after the Application start method is called, then the JavaFX launcher will call the Application stop method and terminate the JavaFX application thread.
ということで、System.exit()を呼ぶのとは違いアプリケーションの終了時処理を行う余地があるということかな。試してみたくなったので、終了するだけのアプリケーションを作ってみる。
できあがり
メニューバーは黒いのか。丸角なのはイマイチ。
- ファイルメニューの終了を選ぶと終了する。
- 閉じるボタンなどでウィンドウを閉じようとしても終了しない。
と動作する。以下ソースコード。
メインクラス
package exit; import java.io.IOException; import javafx.application.Application; import javafx.event.EventHandler; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import javafx.stage.WindowEvent; public class Exiter extends Application { @Override public void start(Stage primaryStage) throws IOException { Parent root = FXMLLoader.load(getClass().getResource("Exiter.fxml")); primaryStage.setScene(new Scene(root)); primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent t) { System.out.println("handle " + t.getEventType()); t.consume(); } }); primaryStage.setTitle("Exiter"); primaryStage.show(); } @Override public void stop() { System.out.println("stop Exiter"); } public static void main(String[] args) { launch(args); } }
- ウィンドウを閉じる動作では終了しないようにするため、Window#setOnCloseRequest()でイベントハンドラを設定し、その中でEvent#consume()を呼ぶようにしてみた。
- Application#stop()をオーバーライドしてこのメソッドが呼び出されていることを確認。
コントローラクラス
package exit; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.fxml.FXML; public class ExiterController { @FXML public void handleExit(ActionEvent event) { Platform.exit(); } }
- 終了メニューが選ばれたときのイベントハンドラを実装しているだけ。Platform.exit()を使う。
FXML
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Menu?> <?import javafx.scene.control.MenuBar?> <?import javafx.scene.control.MenuItem?> <?import javafx.scene.layout.BorderPane?> <BorderPane prefHeight="200" prefWidth="300" xmlns:fx="http://javafx.com/fxml" fx:controller="exit.ExiterController"> <top> <MenuBar> <menus> <Menu text="ファイル"> <items> <MenuItem text="終了" onAction="#handleExit" /> </items> </Menu> </menus> </MenuBar> </top> </BorderPane>
- 連載のサンプルの真似。前回まではルート要素がAnchorPaneだったけど、今回はBorderPaneを使っている。
追記
終了のフックについての記事があったのでリンクしておく。setOnCloseRequest()を使うのはよくないのかも。
■ [c++] Boost Timerで時間を計る
Boost Timerは以前使ったことがあるのだけれど、もう一度使おうとしたら何も覚えていなかったのでサンプルをメモ。
#include <chrono> #include <iostream> #define _GLIBCXX_USE_NANOSLEEP #include <thread> #undef _GLIBCXX_USE_NANOSLEEP #include <boost/timer/timer.hpp> int main() { boost::timer::auto_cpu_timer timer0; boost::timer::auto_cpu_timer timer1; timer0.start(); std::this_thread::sleep_for(std::chrono::seconds(1)); timer0.stop(); timer1.start(); std::this_thread::sleep_for(std::chrono::seconds(2)); timer1.stop(); timer0.resume(); std::this_thread::sleep_for(std::chrono::seconds(3)); timer0.stop(); std::cout << "timer0: "; timer0.report(); std::cout << "timer1: "; timer1.report(); }
timer0: 4.000136s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%) timer1: 2.000066s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%)
- ヘッダオンリではなくビルド時に libboost_timer をリンクする必要がある。
- auto_cpu_timerクラスを使うと、経過時間・ユーザ時間・CPU時間を計ってくれる。
- インスタンスを作っただけで計測は開始されている。
- start()でゼロから計測を開始。
- stop()で計測を停止。
- resume()で計測を再開。
- report()で結果を出力。
ついでにC++11の<chrono>と<thread>を初めて使ってみたけれど、古いGCCだとマクロを定義しないとstd::this_thread::sleep_for()を使えないみたいだった。
2014年01月10日(金) [長年日記]
2014年01月20日(月) [長年日記]
■ [c++] 依存名に修飾された型名は推論されない
大した話ではないけれど分かっていなかったのでメモ。
C++のテンプレート引数の推論は、A<T>::B のような依存名に修飾された型名(言葉の使い方がこれで正しいかよく分からない)に対しては行われないみたい。C++テンプレート完全ガイドの「11.2 文脈の推論」に
だが、文脈が推論されない構造もいくつかある。
- 修飾された型名。例えば、Q<T>::X のような型名はテンプレートパラメータ T を推論するのに使われることはない。
とあるのが該当しそう。
これにより次のコードはコンパイルエラーになる。
#include <iostream> template<typename T> struct A { struct B { T t; }; }; template<typename T> T f(const typename A<T>::B& b) { return b.t; } int main() { A<int>::B b; std::cout << f<int>(b) << std::endl; // OK std::cout << f(b) << std::endl; // エラー }
g++ 4.4.7が出力するエラーメッセージは次の通り。
sample.cpp: In function ‘int main()’: sample.cpp:16: error: no matching function for call to ‘f(A<int>::B&)’
2014年01月21日(火) [長年日記]
■ [c++] メンバ関数へのポインタを返す関数
C++でメンバ関数へのポインタを返す関数を書こうとしたんだけど、そのまま書くとコンパイルエラーになってしまった。
- typedefを使う
- C++11の新しい関数宣言記法を使う
とすれば書けたけど、そのまま書く方法が分からない。
#include <iostream> using std::string; struct A { string repeat(const string& s) const { return s + " " + s; } }; // エラーになってしまう //string ((A::* f0)(const string&) const)() { return &A::repeat; } // typedefを使えばOK typedef string (A::* F)(const string&) const; F f1() { return &A::repeat; } // C++11での新記法でもOK auto f2() -> string (A::*)(const string&) const { return &A::repeat; } int main() { A a; //std::cout << (a.*f0())("f0") << std::endl; std::cout << (a.*f1())("f1") << std::endl; std::cout << (a.*f2())("f2") << std::endl; }
コメントアウトしている箇所を生かしてg++ 4.4.7でコンパイルすると
func_ptr.cpp:10: error: ‘f0’ declared as function returning a function func_ptr.cpp:10: error: expected primary-expression before ‘return’ func_ptr.cpp:10: error: expected ‘}’ before ‘return’ func_ptr.cpp:10: error: expected declaration before ‘}’ token
というエラーになる。
2014年01月30日(木) [長年日記]
■ [c++] rvalueリファレンス自体はlvalue
C++11のrvalueリファレンスの理解が怪しい。次のプログラムが
right left right
と出力するleftの所がいまいちピンとこない。確かに左側にあるけど。
#include <iostream> #include <utility> void f(int& i) { std::cout << "left" << std::endl;} void f(int&& i) { std::cout << "right" << std::endl; } int main() { { int i0 = 0; f(std::move(i0)); //=> right } { int i0 = 0; int&& i1 = std::move(i0); f(i1); //=> left } { int i0 = 0; int&& i1 = std::move(i0); f(std::move(i1)); // right } }
C++11の文法と機能(C++11: Syntax and Feature)に
rvalueリファレンス自体はlvalueであるということに、注意しなければならない。
と説明がある。