SWT (Standard Widget Toolkit)
Eclipse Foundationが開発するJava用GUIツールキット。各プラットフォームのネイティブウィジェットを活用し、OSに最適化されたパフォーマンスと外観を実現。Eclipse IDEの基盤技術としても使用。
フレームワーク
SWT (Standard Widget Toolkit)
概要
SWT(Standard Widget Toolkit)は、Eclipseプロジェクトで開発されたJava用のGUIツールキットです。ネイティブのOSウィジェットを直接利用することで、プラットフォーム固有のルックアンドフィールとパフォーマンスを実現します。Eclipse IDEの基盤技術として開発され、現在も多くのEclipse系アプリケーションで使用されています。
詳細
2025年現在、SWTはEclipse Foundationのもとで継続的に開発されており、Eclipse IDE 2025年版を含む最新のEclipse RCP(Rich Client Platform)アプリケーションの基盤として活用されています。JavaのSwingとは根本的に異なるアプローチを採用しています。
SWTの主要な特徴:
- ネイティブウィジェット: WindowsではWin32 API、macOSではCocoa、LinuxではGTK+を直接使用
- 高パフォーマンス: OSネイティブレンダリングによる高速描画
- 明示的リソース管理: メモリリークを防ぐためのdispose()パターン
- プラットフォーム統合: 各OSの標準ウィジェットと完全に統合
- Eclipse RCP: 豊富なプラグインアーキテクチャとワークベンチフレームワーク
- OSGI対応: モジュラーアプリケーション開発のサポート
活用分野:
- Eclipse IDE(最も著名な利用例)
- IBM Watson Studio、IBM Notes
- SAP Development Tools
- 科学技術アプリケーション(NASA、研究機関)
- 金融系デスクトップアプリケーション
- 産業用制御ソフトウェア
一方で、SwingやJavaFXと比較すると学習コストが高く、プラットフォーム固有のネイティブライブラリ管理、リソースの明示的な解放が必要という課題があります。また、モバイル対応は限定的で、コミュニティサイズも相対的に小さいです。
メリット・デメリット
メリット
- ネイティブパフォーマンス: OSのネイティブウィジェットを使用し高速動作
- プラットフォーム統合: 各OSのルックアンドフィールを自然に再現
- メモリ効率: 適切にリソース管理すれば軽量な動作
- Eclipse統合: Eclipse RCP(Rich Client Platform)での豊富な機能
- プロフェッショナル利用: 企業向けアプリケーションでの実績
- ネイティブ機能: OS固有のAPIへの直接アクセス可能
デメリット
- 複雑なリソース管理: メモリリークを避けるための明示的なdispose必須
- 学習コストの高さ: Swingより複雑なプログラミングモデル
- 大きな依存関係: プラットフォーム固有のネイティブライブラリが必要
- 限定的なコミュニティ: Swing/JavaFXと比較してコミュニティが小さい
- デバッグの困難さ: ネイティブ部分のデバッグが複雑
- 配布の複雑さ: プラットフォーム固有のライブラリ配布が必要
参考ページ
書き方の例
Hello World
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;
public class HelloWorldSWT {
public static void main(String[] args) {
// ディスプレイとシェルの作成
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Hello World - SWT");
shell.setLayout(new FillLayout());
// ラベルの作成
Label label = new Label(shell, SWT.CENTER);
label.setText("Hello, SWT World!");
// シェルのサイズ設定と表示
shell.setSize(400, 300);
shell.open();
// イベントループ
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
// リソースの解放
display.dispose();
}
}
基本的なレイアウト
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;
public class LayoutExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Layout Example");
shell.setLayout(new BorderLayout());
// ツールバー(上部)
ToolBar toolBar = new ToolBar(shell, SWT.HORIZONTAL);
ToolItem newItem = new ToolItem(toolBar, SWT.PUSH);
newItem.setText("新規");
ToolItem openItem = new ToolItem(toolBar, SWT.PUSH);
openItem.setText("開く");
ToolItem saveItem = new ToolItem(toolBar, SWT.PUSH);
saveItem.setText("保存");
// メインエリア(中央)
Composite centerComposite = new Composite(shell, SWT.NONE);
centerComposite.setLayout(new FillLayout());
Text textArea = new Text(centerComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
textArea.setText("ここにテキストを入力してください...");
// ステータスバー(下部)
Label statusBar = new Label(shell, SWT.BORDER);
statusBar.setText("準備完了");
// レイアウトデータの設定
toolBar.setLayoutData(BorderData.NORTH);
centerComposite.setLayoutData(BorderData.CENTER);
statusBar.setLayoutData(BorderData.SOUTH);
shell.setSize(600, 400);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
イベントハンドリング
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.SWT;
public class EventHandlingExample {
private int counter = 0;
private Label counterLabel;
public static void main(String[] args) {
new EventHandlingExample().run();
}
private void run() {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Event Handling Example");
shell.setLayout(new GridLayout(2, false));
// カウンター表示ラベル
counterLabel = new Label(shell, SWT.NONE);
counterLabel.setText("カウンター: 0");
GridData labelData = new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1);
counterLabel.setLayoutData(labelData);
// インクリメントボタン
Button incrementButton = new Button(shell, SWT.PUSH);
incrementButton.setText("増加");
incrementButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
counter++;
updateCounterLabel();
}
});
// デクリメントボタン
Button decrementButton = new Button(shell, SWT.PUSH);
decrementButton.setText("減少");
decrementButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
counter--;
updateCounterLabel();
}
});
// リセットボタン
Button resetButton = new Button(shell, SWT.PUSH);
resetButton.setText("リセット");
resetButton.addSelectionListener(e -> {
counter = 0;
updateCounterLabel();
});
GridData resetData = new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1);
resetButton.setLayoutData(resetData);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private void updateCounterLabel() {
counterLabel.setText("カウンター: " + counter);
}
}
テーブルとデータ表示
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;
public class TableExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Table Example");
shell.setLayout(new GridLayout());
// テーブルの作成
Table table = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
GridData tableData = new GridData(SWT.FILL, SWT.FILL, true, true);
tableData.heightHint = 200;
table.setLayoutData(tableData);
// カラムの作成
String[] columnTitles = {"ID", "名前", "年齢", "部署"};
for (String title : columnTitles) {
TableColumn column = new TableColumn(table, SWT.NONE);
column.setText(title);
column.setWidth(100);
}
// データの追加
String[][] data = {
{"1", "田中太郎", "30", "開発"},
{"2", "佐藤花子", "28", "営業"},
{"3", "鈴木一郎", "35", "企画"},
{"4", "高橋美子", "32", "人事"}
};
for (String[] row : data) {
TableItem item = new TableItem(table, SWT.NONE);
item.setText(row);
}
// ボタンパネル
Composite buttonComposite = new Composite(shell, SWT.NONE);
buttonComposite.setLayout(new RowLayout());
Button addButton = new Button(buttonComposite, SWT.PUSH);
addButton.setText("追加");
addButton.addSelectionListener(e -> {
TableItem newItem = new TableItem(table, SWT.NONE);
newItem.setText(new String[]{
String.valueOf(table.getItemCount()),
"新規ユーザー",
"25",
"未定"
});
});
Button deleteButton = new Button(buttonComposite, SWT.PUSH);
deleteButton.setText("削除");
deleteButton.addSelectionListener(e -> {
int selectionIndex = table.getSelectionIndex();
if (selectionIndex != -1) {
table.remove(selectionIndex);
}
});
shell.setSize(500, 350);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
ダイアログとメッセージボックス
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;
public class DialogExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Dialog Example");
shell.setLayout(new GridLayout(2, false));
// メッセージダイアログボタン
Button messageButton = new Button(shell, SWT.PUSH);
messageButton.setText("メッセージダイアログ");
messageButton.addSelectionListener(e -> {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setMessage("これはメッセージです");
messageBox.setText("情報");
messageBox.open();
});
// 確認ダイアログボタン
Button confirmButton = new Button(shell, SWT.PUSH);
confirmButton.setText("確認ダイアログ");
confirmButton.addSelectionListener(e -> {
MessageBox confirmBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
confirmBox.setMessage("削除してもよろしいですか?");
confirmBox.setText("確認");
int result = confirmBox.open();
if (result == SWT.YES) {
MessageBox resultBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
resultBox.setMessage("削除されました");
resultBox.setText("完了");
resultBox.open();
}
});
// 入力ダイアログボタン
Button inputButton = new Button(shell, SWT.PUSH);
inputButton.setText("入力ダイアログ");
inputButton.addSelectionListener(e -> {
InputDialog inputDialog = new InputDialog(shell, "入力", "名前を入力してください:", "", null);
if (inputDialog.open() == InputDialog.OK) {
String input = inputDialog.getValue();
if (input != null && !input.trim().isEmpty()) {
MessageBox resultBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
resultBox.setMessage("こんにちは、" + input + "さん!");
resultBox.setText("挨拶");
resultBox.open();
}
}
});
// ファイルダイアログボタン
Button fileButton = new Button(shell, SWT.PUSH);
fileButton.setText("ファイルダイアログ");
fileButton.addSelectionListener(e -> {
FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
fileDialog.setText("ファイルを選択");
fileDialog.setFilterExtensions(new String[]{"*.txt", "*.java", "*.*"});
fileDialog.setFilterNames(new String[]{"テキストファイル (*.txt)", "Javaファイル (*.java)", "すべてのファイル (*.*)"});
String fileName = fileDialog.open();
if (fileName != null) {
MessageBox resultBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
resultBox.setMessage("選択されたファイル: " + fileName);
resultBox.setText("ファイル選択結果");
resultBox.open();
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
// カスタム入力ダイアログクラス
class InputDialog extends Dialog {
private String title;
private String message;
private String initialValue;
private String value;
public InputDialog(Shell parent, String title, String message, String initialValue, IInputValidator validator) {
super(parent);
this.title = title;
this.message = message;
this.initialValue = initialValue;
}
public int open() {
Shell shell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
shell.setText(title);
shell.setLayout(new GridLayout(2, false));
Label label = new Label(shell, SWT.NONE);
label.setText(message);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
Text text = new Text(shell, SWT.BORDER);
text.setText(initialValue != null ? initialValue : "");
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
Button okButton = new Button(shell, SWT.PUSH);
okButton.setText("OK");
okButton.addSelectionListener(e -> {
value = text.getText();
shell.dispose();
});
Button cancelButton = new Button(shell, SWT.PUSH);
cancelButton.setText("キャンセル");
cancelButton.addSelectionListener(e -> {
value = null;
shell.dispose();
});
shell.pack();
shell.open();
Display display = shell.getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
return value != null ? OK : CANCEL;
}
public String getValue() {
return value;
}
public static final int OK = 0;
public static final int CANCEL = 1;
}
メニューとツールバー
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;
public class MenuToolbarExample {
private Text textArea;
public static void main(String[] args) {
new MenuToolbarExample().run();
}
private void run() {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Menu and Toolbar Example");
shell.setLayout(new BorderLayout());
// メニューバーの作成
Menu menuBar = new Menu(shell, SWT.BAR);
shell.setMenuBar(menuBar);
// ファイルメニュー
MenuItem fileMenuItem = new MenuItem(menuBar, SWT.CASCADE);
fileMenuItem.setText("ファイル(&F)");
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
fileMenuItem.setMenu(fileMenu);
MenuItem newMenuItem = new MenuItem(fileMenu, SWT.PUSH);
newMenuItem.setText("新規(&N)\tCtrl+N");
newMenuItem.setAccelerator(SWT.CTRL + 'N');
newMenuItem.addSelectionListener(e -> newFile());
MenuItem openMenuItem = new MenuItem(fileMenu, SWT.PUSH);
openMenuItem.setText("開く(&O)\tCtrl+O");
openMenuItem.setAccelerator(SWT.CTRL + 'O');
openMenuItem.addSelectionListener(e -> openFile(shell));
MenuItem saveMenuItem = new MenuItem(fileMenu, SWT.PUSH);
saveMenuItem.setText("保存(&S)\tCtrl+S");
saveMenuItem.setAccelerator(SWT.CTRL + 'S');
saveMenuItem.addSelectionListener(e -> saveFile(shell));
new MenuItem(fileMenu, SWT.SEPARATOR);
MenuItem exitMenuItem = new MenuItem(fileMenu, SWT.PUSH);
exitMenuItem.setText("終了(&X)");
exitMenuItem.addSelectionListener(e -> shell.dispose());
// 編集メニュー
MenuItem editMenuItem = new MenuItem(menuBar, SWT.CASCADE);
editMenuItem.setText("編集(&E)");
Menu editMenu = new Menu(shell, SWT.DROP_DOWN);
editMenuItem.setMenu(editMenu);
MenuItem cutMenuItem = new MenuItem(editMenu, SWT.PUSH);
cutMenuItem.setText("切り取り(&T)\tCtrl+X");
cutMenuItem.setAccelerator(SWT.CTRL + 'X');
cutMenuItem.addSelectionListener(e -> textArea.cut());
MenuItem copyMenuItem = new MenuItem(editMenu, SWT.PUSH);
copyMenuItem.setText("コピー(&C)\tCtrl+C");
copyMenuItem.setAccelerator(SWT.CTRL + 'C');
copyMenuItem.addSelectionListener(e -> textArea.copy());
MenuItem pasteMenuItem = new MenuItem(editMenu, SWT.PUSH);
pasteMenuItem.setText("貼り付け(&P)\tCtrl+V");
pasteMenuItem.setAccelerator(SWT.CTRL + 'V');
pasteMenuItem.addSelectionListener(e -> textArea.paste());
// ツールバーの作成
ToolBar toolBar = new ToolBar(shell, SWT.HORIZONTAL | SWT.FLAT);
toolBar.setLayoutData(BorderData.NORTH);
ToolItem newToolItem = new ToolItem(toolBar, SWT.PUSH);
newToolItem.setText("新規");
newToolItem.setToolTipText("新しいファイル");
newToolItem.addSelectionListener(e -> newFile());
ToolItem openToolItem = new ToolItem(toolBar, SWT.PUSH);
openToolItem.setText("開く");
openToolItem.setToolTipText("ファイルを開く");
openToolItem.addSelectionListener(e -> openFile(shell));
ToolItem saveToolItem = new ToolItem(toolBar, SWT.PUSH);
saveToolItem.setText("保存");
saveToolItem.setToolTipText("ファイルを保存");
saveToolItem.addSelectionListener(e -> saveFile(shell));
new ToolItem(toolBar, SWT.SEPARATOR);
ToolItem cutToolItem = new ToolItem(toolBar, SWT.PUSH);
cutToolItem.setText("切り取り");
cutToolItem.addSelectionListener(e -> textArea.cut());
ToolItem copyToolItem = new ToolItem(toolBar, SWT.PUSH);
copyToolItem.setText("コピー");
copyToolItem.addSelectionListener(e -> textArea.copy());
ToolItem pasteToolItem = new ToolItem(toolBar, SWT.PUSH);
pasteToolItem.setText("貼り付け");
pasteToolItem.addSelectionListener(e -> textArea.paste());
// テキストエリア
textArea = new Text(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
textArea.setLayoutData(BorderData.CENTER);
textArea.setText("ここにテキストを入力してください...");
// ステータスバー
Label statusBar = new Label(shell, SWT.BORDER);
statusBar.setText("準備完了");
statusBar.setLayoutData(BorderData.SOUTH);
shell.setSize(700, 500);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private void newFile() {
textArea.setText("");
}
private void openFile(Shell shell) {
FileDialog dialog = new FileDialog(shell, SWT.OPEN);
dialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
String fileName = dialog.open();
if (fileName != null) {
try {
java.nio.file.Path path = java.nio.file.Paths.get(fileName);
String content = java.nio.file.Files.readString(path);
textArea.setText(content);
} catch (Exception e) {
MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
errorBox.setMessage("ファイルの読み込みエラー: " + e.getMessage());
errorBox.setText("エラー");
errorBox.open();
}
}
}
private void saveFile(Shell shell) {
FileDialog dialog = new FileDialog(shell, SWT.SAVE);
dialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
String fileName = dialog.open();
if (fileName != null) {
try {
java.nio.file.Path path = java.nio.file.Paths.get(fileName);
java.nio.file.Files.writeString(path, textArea.getText());
MessageBox infoBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
infoBox.setMessage("ファイルが保存されました");
infoBox.setText("保存完了");
infoBox.open();
} catch (Exception e) {
MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
errorBox.setMessage("ファイルの保存エラー: " + e.getMessage());
errorBox.setText("エラー");
errorBox.open();
}
}
}
}