連載
» 2004年02月21日 00時00分 公開

Eclipse徹底活用(7):EclipseによるSWTアプリケーションの作成 (2/4)

[金子崇之, 岡本隆史,NTTデータ]

SWTアプリケーションを作る

 ここからは、いよいよSWTを使用した開発を実践的に解説していきます。

作成するアプリケーション

 今回はSWTのアプリケーションとして、CSVファイルを表形式で表示するCSVViewerを作成します。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 + "行目");
  }

 以上で必要な処理が実装できました。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。