SWTアプリケーションを作る
ここからは、いよいよSWTを使用した開発を実践的に解説していきます。
■作成するアプリケーション
今回はSWTのアプリケーションとして、CSVファイルを表形式で表示するCSVViewerを作成します。CSVViewerの概観は、以下のようになります。
このアプリケーションの仕様は、以下のとおりです。
- メニューから、読み込むファイルの選択と、アプリケーションの終了を行える
- 読み込んでいるファイル名を、テキストボックスに表示する
- CSVファイルの内容は、テーブルを使用して表示する。列名は「列1」「列2」…とCSVファイルの最大列数分だけ表示する
- 現在の状況を表示するためのステータスバーを設ける
- テーブル内の行を選択した際に、行番号を表示する
- ファイルの読み込みにかかった時間を表示する
ここでは、以下の順番でアプリケーションの作成を行います。
(1) シェル(ウィンドウ)の生成
(2) シェルへのウィジェット(GUIコンポーネント)の配置
(3) CSV読み込み、表示処理の実装
(4) ウィジェットへのイベントリスナーの登録(処理の追加)
シェル(ウィンドウ)の生成
まずここでは、ウィンドウを表示できるところまで完成させます。
Eclipseのメニューから、[ファイル]->[新規]->[クラス]で新規Javaクラスダイアログを開きます。以下のとおり入力して、[終了]ボタンをクリックします。
ソース・フォルダ |
swtPrj/src |
パッケージ名 |
csvviewer |
名前 |
CSVViewer |
|
Eclipseが生成したコードを以下のように編集します。
package csvviewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* CSVViewer
*/
public class CSVViewer {
private Shell shell;
private Display display;
public Shell open(Display display) {
this.display = display;
shell = new Shell(display); // (2)
shell.setText("CSVViewer");
shell.open(); // (6)
return shell;
}
public static void main(String[] args) {
Display display = new Display(); // (1)
CSVViewer viewer = new CSVViewer();
Shell shell = viewer.open(display);
// ウィンドウが破棄されるまでループ
while (!shell.isDisposed()) { // (7)
if (!display.readAndDispatch())
display.sleep();
}
display.dispose(); // (8)
}
} |
一般的なSWTアプリケーションの処理の流れは、次のようになります(番号はコード中の番号に対応しています)。
(1) SWTセッションを表すDisplayオブジェクトを生成
(2) アプリケーションのメイン・ウィンドウとなる1つ以上のShellを生成
(3) ウィンドウ内で必要となる他のウィジェットを生成
(4) ウィジェットのサイズや、その他の必要な状態を初期化
(5) 処理を実行する必要のあるウィジェットにリスナーを登録
(6) Shellを開く
(7) メイン・ウィンドウが破棄されるまで、DisplayオブジェクトのreadAndDispatchメソッドとsleepメソッドでループ
(8) メイン・ウィンドウが破棄されたら、Displayオブジェクトを破棄
ここまでのCSVViewerでは、上記のうち(1)、(2)、(6)、(7)、(8)を実装しています。上記のソースコードにコメントとして記述されている番号と見比べてください。
CSVViewerのソースが選択されている状態で、Eclipseのメニューから「実行」->「次を実行」->「Javaアプリケーション」を選択すると、プログラムを実行できます。以下のようなウィンドウが表示されます。
アプリケーションとしてはこれ以上何もできませんので、CSVViewerのタイトルバー上の「×」ボタンをクリックし、終了してください。
ここまでのソースは、ここからダウンロードしてください(ファイル名、クラス名を変えています)。
シェルへのウィジェット(GUIコンポーネント)の配置
次に、「(3)ウィンドウ内で必要となるほかのウィジェットを生成」と「(4)ウィジェットのサイズや、そのほかの必要な状態を初期化」を実装します。
以下では、修正個所ごとに説明を行います。変更した行は強調表示してあります。
■インスタンス変数の追加
後で別のメソッドから操作されるウィジェットは、インスタンス変数に宣言しておきます。
private Shell shell;
private Display display;
private Text textBox;
private Table table;
private Label statusBar;
private MenuItem mItemOpen; |
textBox、table、statusBar、mItemOpenを宣言しました。
■GridLayoutの指定
CSVViewerのopenメソッド内で、ウィジェットを生成する前に、shellに対しGridLayoutを指定します。
this.display = display;
shell = new Shell(display);
shell.setText("CSVViewer");
shell.setLayout(new GridLayout(1, true));
|
GridLayoutのコンストラクタの引数numColumnsに1を指定したので、shellに追加されるウィジェットは、縦1列に並べられます。
■Compositeの生成
shellの設定の次に、テキストボックスとテーブルを配置するためのCompositeを生成します。
// 部品を複数配置できるCompositeを生成
Composite comp = new Composite(shell, SWT.NO_FOCUS);
comp.setLayout(new GridLayout(1, true));
GridData compGrid = new GridData();
compGrid.horizontalAlignment = GridData.FILL;
compGrid.verticalAlignment = GridData.FILL;
compGrid.grabExcessHorizontalSpace = true;
compGrid.grabExcessVerticalSpace = true;
comp.setLayoutData(compGrid ); |
CompositeにはShell同様、GridLayoutを指定します。
また、GridDataを設定し、親ウィジェットとなるShellのGridLayoutに対して幅全体、高さ全体に広がった配置が行われるようにしています。
■テキストボックスの生成
Compositeを生成した後、単一行のテキストボックスを作成します。親ウィジェットには先ほど作成したCompositeを指定します。
// 単一行テキストボックスを作成
textBox = new Text(comp, SWT.SINGLE | SWT.BORDER);
textBox.setText("");
GridData textGrid = new GridData();
textGrid.horizontalAlignment = GridData.FILL;
textGrid.grabExcessHorizontalSpace = true;
textBox.setLayoutData(textGrid); |
単一行であるか、複数行であるかは、生成時にコンストラクタのstyleというフラグで指定します。さらに今回はボーダー(ふち)を付けるようにBORDERを指定しました。フラグは、ビット演算子「|」で複数指定することができます。
また、GridDataを設定し、親ウィジェットとなるCompositeのGridLayoutに対して幅全体に広がった配置が行われるようにしています。
■テーブルの作成
テキストボックスに続いて、CSVファイルのデータを表示するためのテーブルを生成します。親ウィジェットには、Compositeを指定します。
複数列を扱うMULTI、テーブル内の行を選択した際、1行全体が選択されるようにするFULL_SELECTION、ボーダーを付けるようにBORDERを指定しています。
// テーブルの作成
table = new Table(comp,
SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
GridData tableGrid = new GridData();
tableGrid.horizontalAlignment = GridData.FILL;
tableGrid.verticalAlignment = GridData.FILL;
tableGrid.grabExcessHorizontalSpace = true;
tableGrid.grabExcessVerticalSpace = true;
table.setLayoutData(tableGrid);
// 線を表示する
table.setLinesVisible(true);
// ヘッダを可視にする
table.setHeaderVisible(true);
// 列のヘッダの設定
TableColumn col = new TableColumn(table, SWT.LEFT);
col.setText("列1");
col.setWidth(50); |
また、GridDataを設定し、親ウィジェットとなるCompositeのGridLayoutに対して幅全体、高さ全体に広がった配置が行われるようにしています。
罫線を表示するためのsetLinesVisibleメソッド、列のヘッダを表示するためのsetHeaderVisibleメソッドを呼んでいます。
また、列のヘッダの設定を行います。ここで設定するのはCSVファイルを読み込んでいない状態で表示されるテーブルなので、取りあえず1列分だけ設定します。
■ステータスバーの作成
SWTではステータスバーというウィジェットは存在しないので、文字を表示するラベルを設定します。親ウィジェットにShellを指定します。
// ステータスバーの作成
statusBar = new Label(shell, SWT.LEFT);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
statusBar.setLayoutData(gridData);
statusBar.setText("メニューからCSVファイルを指定してください"); |
GridDataを設定し、親ウィジェットとなるShellのGridLayoutに対して幅全体に広がった配置が行われるようにしています。
■メニューの作成
MenuBarをShellに設定し、その子要素として以下の親子関係を持つメニューを作成します。
MenuBar +
+ MenuItem(「ファイル」) +
+ Menu +
+ MenuItem(「開く」)
+ MenuItem(区切り)
+ MenuItem(「終了」) |
// メニューの作成
Menu menubar = new Menu(shell, SWT.BAR);
shell.setMenuBar(menubar);
MenuItem mItemFile = new MenuItem(menubar, SWT.CASCADE);
mItemFile.setText("ファイル(&F)");
Menu menuFile = new Menu(mItemFile);
mItemFile.setMenu(menuFile);
mItemOpen = new MenuItem(menuFile, SWT.PUSH);
mItemOpen.setText("開く(&O)");
new MenuItem(menuFile, SWT.SEPARATOR);
MenuItem mItemExit = new MenuItem(menuFile, SWT.PUSH);
mItemExit.setText("終了(&X)"); |
MenuItem([ファイル])には、子要素としてメニューを持たせるためのCASCADEを指定します。MenuItem([開く])、MenuItem([終了])は、選択したときにイベントを発生させるためのPUSHを指定します。
ここまでのソースファイルはここから入手できます(ファイル名、クラス名を変えています)。
Eclipse上のメニューから[実行]->」前回の起動を[実行]を選択(又はキーボードから[Ctrl + F11]を入力)すると、以下のようなウィンドウが表示されます。
必要なウィジェットが配置され、アプリケーションらしくなってきました。ウィンドウのサイズを変更し、GridDataが期待どおりのレイアウトを行うかどうか確認してください。
しかし、ここまでのCSVViewerでは[開く]や[終了]メニューを選択しても何も起こりません。CSVViewerのタイトルバー上の[×]ボタンをクリックし、終了してください。
■CSV読み込み、表示処理の実装
次のCSVViewerでは、「(5)処理を実行する必要のあるウィジェットにリスナーを登録」を実装し、メニューを選択したときの処理を追加していきます。まずは処理の実装から行います。
引き続き、修正個所ごとに説明していきます。
■インスタンス変数の追加
// ファイルダイアログ用のフィルタ拡張子
private final String[] EXTENSIONS = { "*.csv", "*" };
// 読み込むファイル名
private String fileName;
// 読み込み時間測定用
private long beginTime; |
CSVファイル名を代入するfileNameと、ファイルダイアログを使用する際のフィルタ拡張子のEXTENSIONS、読み込み時間測定のためのbeginTimeを宣言します。
■テーブルの作成部をメソッド化
メニューを呼び出したときにもテーブルを再作成する処理を行うため、テーブルの作成部をメソッドとして抽出し、コードの重複を防ぎます。
openメソッド内の該当個所をドラッグして選択し、Eclipseのメニューから[リファクタリング]->[メソッドの抽出]を選択します。メソッド名に「createTable」と入力し、[OK]をクリックします。
public Shell open(Display display) {
...【省略】
//テーブルの作成
createTable(comp);
...【省略】
shell.open();
return shell;
}
private void createTable(Composite parent) {
table = new Table(comp,
SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
GridData tableGrid = new GridData();
tableGrid.horizontalAlignment = GridData.FILL;
tableGrid.verticalAlignment = GridData.FILL;
tableGrid.grabExcessHorizontalSpace = true;
tableGrid.grabExcessVerticalSpace = true;
table.setLayoutData(tableGrid);
// 線を表示する
table.setLinesVisible(true);
// ヘッダを可視にする
table.setHeaderVisible(true);
// 列のヘッダの設定
TableColumn col = new TableColumn(table, SWT.LEFT);
col.setText("列1");
col.setWidth(50);
} |
■ファイル名の取得処理の実装
ファイルダイアログを開き、ファイル名を入手する処理を実装します。
private void getOpenFileName() {
FileDialog openDialog = new FileDialog(shell, SWT.OPEN);
openDialog.setFilterExtensions(EXTENSIONS);
fileName = openDialog.open();
} |
■CSVファイル読み込み前の処理の実装
ファイル名を取得後、実際に読み込み処理に入る前の処理を実装します。
private void loadBegin() {
//[開く]メニューを選択不可にする
mItemOpen.setEnabled(false);
// テキストボックスにファイル名を設定
textBox.setText(fileName);
// カラム数が変わるので、テーブルを再作成する
Composite comp = table.getParent();
table.dispose();
createTable(comp);
comp.layout();
// ステータスバーに状態を表示
statusBar.setText("読み込み中...");
// 読み込み時間測定用
beginTime = System.currentTimeMillis();
} |
ファイル読み込み中は、新たにファイルの読み込みを始められないように、[開く]メニューを選択不可にします。
SWTのテーブルでは、一度設定したカラムは破棄することができません。そのため、テーブル自体を再作成しています。テーブルの再作成には、先ほど抽出したcreateTableメソッドを呼び出します。
テキストボックスとステータスバーの表示を変え、読み込み時間測定用に現在の時間をSystem.currentTimeMillisメソッドで取得します。
■CSVファイル読み込み処理の実装
ファイル名が存在した場合、カンマ区切りでデータを読み込み、テーブルにセットしていきます。
private void loadFile() {
BufferedReader reader = null;
try {
File file = new File(fileName);
if (!file.exists()) {
return;
}
// ファイルを開く
reader = new BufferedReader(new FileReader(file));
// 1行ごとに処理を行う
String line = null;
while ((line = reader.readLine()) != null) {
// カンマで文字列を区切る
String[] datas = line.split(",");
// 列が少なかったら加える
for (int i = table.getColumnCount();
i < datas.length; i++) {
TableColumn column = new TableColumn(table, SWT.LEFT);
column.setText("列" + (i + 1));
column.setWidth(50);
}
// 行を設定
TableItem item = new TableItem(table, SWT.NULL);
item.setText(datas);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// 何もしない
}
}
}
} |
BufferedReaderを使用して1行ごとにファイルから読み込みます。カンマで区切る処理はJDK1.4から使用できるsplitメソッドを使用しています。
また、現在テーブルが持つ列数よりも読み込んだデータの列数の方が多かった場合、差分をカラムとして追加しています。
テーブルへのデータの追加は、TableItemオブジェクトを生成することで実現できます。TableItemオブジェクトのsetTextメソッドを使用し、読み込んだ値を設定します。
最後にfinally句で、BufferedReaderをcloseします。
■CSVファイル読み込み後の処理の実装
ファイル読み込み後の処理を実装します。
private void loadEnd() {
mItemOpen.setEnabled(true);
long end = System.currentTimeMillis();
statusBar.setText("読込完了 : 処理時間"
+ (end - beginTime) + "ミリ秒");
} |
前処理で実施した、[開く]メニューの無効化を解除します。また、ステータスバーに、読み込み処理にかかった時間をミリ秒で表示します。
■終了処理の実装
[終了]メニュー実行時に呼び出される終了処理を実装します。
private void close() {
shell.close();
shell.dispose();
} |
■選択行の表示処理の実装
テーブル内の行を選択した際に、行番号をステータスバーに表示するための処理を実装します。
private void setTableSelectedIndex() {
int index = table.getSelectionIndex();
statusBar.setText(index + "行目");
} |
以上で必要な処理が実装できました。