概要

scala.swingパッケージ内のクラスを利用して、ウィンドウの表示、コンポーネントの配置、イベントの取得などの基本的なことをやってみる。

@CretedDate 2012/01/20
@Versions Scala2.9.1

Windowの表示

SimpleSwingApplicationを使うと簡単にWindowが作成できる。

import scala.swing.{ SimpleSwingApplication, MainFrame, Dimension }

object MainFrameSample extends SimpleSwingApplication {
  def top = new MainFrame {
    // Windowのタイトル
    title = "Window Title"
    // Windowのサイズ
    minimumSize = new Dimension( 300, 200 )
  }
}

これで空のWindowが表示される。

上の例で使用しているtitleminimumSizeはMainFrameクラスが持つメソッド。

MainFrameクラスでは他にも以下のような指定ができる。

// カーソルの指定
cursor = new Cursor( Cursor.HAND_CURSOR )
// リサイズ不能
resizable = false
// アイコンの設定(リソースにアイコンファイルが置いてある場合の例)
iconImage = ImageIO.read( getClass().getResourceAsStream( "icon.bmp" ) )
// メニューバーの指定
menuBar = new MenuBar() { contents += new Menu( "menu1" ) }

サンプルコード

デフォルトのフォントの指定

Linuxだと日本語表示で文字化けすることが良くあるので、先にその対策をしておく。

日本語が化けないフォントのファイルをリソースの中に入れておいて、デフォルトのフォントに設定してしまえば確実に文字化けは防げるはず。

// Resourceの中に入れておいたフォントを読み込む
val baseFont = new FontUIResource( Font.createFont( Font.TRUETYPE_FONT,
  getClass().getResourceAsStream( "TakaoMincho.ttf" ) ).deriveFont( 14.0f ) )

// 読み込んだフォントをUIManagerでデフォルトに設定
import scala.collection.JavaConversions._
for ( entry <- UIManager.getDefaults().entrySet() )
  if ( entry.getKey().toString().endsWith( ".font" ) )
    UIManager.put( entry.getKey(), baseFont )

上の例ではリソースの中に入れておいたTakaoMincho.ttfのファイルを読み込んで、サイズを14に指定し、デフォルトフォントの内容を上書きしている。

コンポーネントの配置

JavaのJPanelのラッパーであるPanelを利用してコンポーネントを配置してみる。

ScalaのPanelには、BorderPanel、BoxPanel、FlowPanel、GridPanel、GridBagPanelなどがいる。JavaのSwingにいるBorderLayoutとかBoxLayoutを思い出すような名前。

とりあえずBoxPanelを使ってみる。BoxPanelは水平方向か垂直方向のどちらか一方にコンポーネントを並べて配置するレイアウト。

object BoxPanelSample extends SimpleSwingApplication {
  def top = new MainFrame {
    title = "Window Title"
    minimumSize = new Dimension( 300, 100 )
    // コンテンツにPanelを設定
    contents = new BoxPanel( Orientation.Vertical ) {
      contents += new Label( "展覧会の絵" )
      contents += new Label( "クープランの墓" )
      contents += new Button( "亡き王女のためのパヴァーヌ" )
    }
  }
}

上記のコードでは、垂直(Vertical)方向にコンポーネントが並ぶよう指定した上で、ラベルを2つ、ボタンを1つ配置している。

このコードを実行すると、以下のようなWindowが表示される。

BoxPanelの例

以下のように、Panel内で背景色ツールチップ枠線などを指定することもできる。

// コンテンツにPanelを設定
contents = new BoxPanel( Orientation.Vertical ) {
  // コンポーネントの配置
  contents += new Label( "展覧会の絵" )
  // 背景色
  background = Color.CYAN
  // ツールチップ
  tooltip = "ツールチップ"
  // ボーダー
  border = new LineBorder(Color.WHITE, 3)
}

背景色や枠線を指定

サンプルコード

その他のPanel

BorderPanel、FlowPanel、GridPanel、GridBagPanelについても軽く試してみる。

BorderPanelはコンポーネントの配置場所をNorth、South、East、West、Centerの5つの中から選べる。

new BorderPanel() {
  add( new Label( "展覧会の絵" ), BorderPanel.Position.North )
  add( new Label( "クープランの墓" ), BorderPanel.Position.East )
  add( new Button( "亡き王女のためのパヴァーヌ" ), BorderPanel.Position.South )
}

BorderPanel


FlowPanelは水平方向にコンポーネントを並べる。
val flowPanel = new FlowPanel() {
  contents += new Label( "展覧会の絵" )
  contents += new Label( "クープランの墓" )
  contents += new Button( "亡き王女のためのパヴァーヌ" )
}

FlowPanel


GridLayoutはHTMLのテーブルのような感じ。
val gridPanel = new GridPanel( 3, 2 ) {
  contents += new Label( "展覧会" )
  contents += new Label( "の絵" )
  contents += new Label( "クープラン" )
  contents += new Label( "の墓" )
  contents += new Button( "亡き王女の" )
  contents += new Button( "ためのパヴァーヌ" )
}

GridPanel


GridBagLayoutはHTMLのテーブルでcolspanやrowspanのような複数セルの結合ができる感じ。
new GridBagPanel() {
  layout += new Label( "展覧会" ) -> ( 0, 0 )
  layout += new Label( "の絵" ) -> ( 1, 0 )
  layout += new Label( "クープラン" ) -> ( 0, 1 )
  layout += new Label( "の墓" ) -> ( 1, 1 )

  // 3段目のマスは横に2つ繋げる
  val c = pair2Constraints( 0, 2 )
  c.gridwidth = 2
  layout += new Button( "亡き王女のためのパヴァーヌ" ) -> c
}

GridBagPanel

サンプルコード

イベントの取得

ボタンクリックのイベントを取得して、ダイアログボックスを表示するコードを書いてみる。

こうした処理はreactionsにイベントごとの挙動を記述することで実現できるようだ。

// クリックされるとダイアログが出るボタン
contents += new Button( "押してみろ" ) {
  reactions += {
    case e: ButtonClicked => Dialog.showMessage( message = "ボタン押された" )
  }
}

このコードを実行すると、下記のようにダイアログが出現するボタンが表示される。

ボタンを押すとダイアログが出る


次にラベルに対してもイベントを付加してみる。

ラベルの場合はデフォルトではイベントの発生を受け取る設定がされていないので、listenToで明示的に監視することを宣言してあげる必要がある。(Buttonは親クラス内でButtonClickedをイベント登録してるのでlistenToが不要だった)

// イベントによってテキストが変わるラベル
contents += new Label( "ラベル" ) {
  listenTo( mouse.clicks, mouse.moves )
  reactions += {
    case e: MousePressed  => text = "MousePressed"
    case e: MouseReleased => text = "MouseReleased"
    case e: MouseEntered  => text = "MouseEntered"
  }
}

このコードを実行すると、MouseEnteredやMousePressedに反応して文字が変化するラベルが表示される。

この辺のイベントの書き方はすっきりしていてけっこう好き。

サンプルコード

その他のコンポーネント

ここまでラベルとボタンしか使ってこなかったので、他のコンポーネントも少し触ってみる。

テキストフィールド、チェックボックス、ラジオボタンなどのUIは一通り揃っている。プログレスバーやスライダーもいる。

def top = new MainFrame {
  contents = new BoxPanel( Orientation.Vertical ) {
    // テキストフィールド
    contents += new TextField( "テキスト" )
    // パスワード
    contents += new PasswordField( "password" )
    // テキストエリア
    contents += new TextArea( "テキストエリア" )
    // チェックボックス
    contents += new CheckBox( "チェックボックス" )
    // ラジオボタン
    val button1 = new RadioButton( "ラジオボタン1" )
    val button2 = new RadioButton( "ラジオボタン2" )
    val group = new ButtonGroup( button1, button2 )
    contents += button1
    contents += button2
    // プログレスバー
    val progressBar = new ProgressBar()
    progressBar.value = 30
    contents += progressBar
    // スライダー
    contents += new Slider()
  }
}

いろんなコンポーネント

サンプルコード