Helsingin yliopisto / tietojenkäsittelytieteen laitos / Ohjelmointitekniikka (Scala) / © Arto Wikla 2010

GUIsta lyhyesti

Muutettu viimeksi 29.4.2010 / Sivu luotu 26.4.2010 / [oppikirjan esimerkit] / [Scala]

Sivun sisältöä:

Tässä luvussa luodaan pikakatsaus graafisen käyttöliittymän toteuttamisen vaivattomuuteen Scalalla - vaivatonta se on ainakin Javaan verrattuna. Monet Javassa kömpelöt ilmaukset on saatu luonnollistettua Scalan funktioparametrien ja operaattorimerkeiltä näyttävien metodikutsujen ansiosta. Eikä lopulta ole lainkaan yhdentekevää, voiko ohjelmoija ajatella selkein ja luontevin käsittein!

Swing-sovellus

Pakkauksessa scala.swing on monenlaisiin graafisiin sovelluksiin riittävä luokka SimpleGUIApplication [API]. Luokka on abstrakti. Sen ainoa abstrakti metodi on abstract def top: Frame. Sen saa toteutettua esimerkiksi MainFrame-oliona:
  import scala.swing._

  object FirstSwingApp extends SimpleGUIApplication {
    def top = new MainFrame {
      title = "First Swing App"
      contents = new Button {
        text = "Click me"
      }
    }
  }
Kuten näkyy, MainFrame-luokassa on mukavia muuttujia, joille voi antaa arvoja! Sama tekniikka löytyy myös luokasta Button. Hyvä idea!

MainFrame myös sisältää pääohjelman, joka mm. kutsuu top-metodia. MainFrame on sellainen Frame, joka osaa päättyessään lopettaa koko graafisen sovelluksen.

Layout

Edellä contents-muuttujaan asetettiin vain yksittäinen nappula. Kun halutaan useampia käyttöliittymäkomponentteja, tarvitaan jokin säiliö, "paneeli", jonne komponentteja sijoitellaan. Luokan scala.swing.Panel aliluokista tällaisia löytyy: BorderPanel, BoxPanel, FlowPanel, GridBagPanel, GridPanel. Javan GUI-ohjelmointia tunteva saattaa ehkä aavistaa, millaisia asemointeja (lay-out) nämä paneelit tarjioavat...;-)
import scala.swing._

object SecondSwingApp extends SimpleGUIApplication {
  def top = new MainFrame {
    title = "Second Swing App"
    val button = new Button {
      text = "Click me"
    }
    val label = new Label {
      text = "No button clicks registered"
    }	
    contents = new BoxPanel(Orientation.Vertical) {
      contents += button
      contents += label
      border = Swing.EmptyBorder(30, 30, 10, 30)
    }
  }
}  
Taas tätä Scala tyylikkyyttä: contents-muuttujaan lisätään nappula ja teksti lisäysoperaatiolla "+=".

Huom: Tuossa BoxPanelin luonnissa on uutuus: new-ilmauksen yhteydessä muutetaan luotavan olioin kenttien arvoja. Pienempi esimerkki: Hmm.scala.

Reagointia

Scalan tapahtumiin reagointi muistuttaa logiikaltaan Javan tapaa, mutta kuten niin monesti on havaittu, Scalan vahvat rakenteet ovat tehneet mahdolliseksi suoristaa monia mutkia ja saada ohjelma paremmin näyttämään siltä, mitä tarkoitetaan.

Tapahtumia kuunnellaan (listenTo(source)) ja niihin reagoidaan (reactions += ...):

import scala.swing._
import scala.swing.event._

object ReactiveSwingApp extends SimpleGUIApplication {
  def top = new MainFrame {
    title = "Reactive Swing App"
    val button = new Button {
      text = "Click me"
    }
    val label = new Label {
      text = "No button clicks registered"
    }	
    contents = new BoxPanel(Orientation.Vertical) {
      contents += button
      contents += label
      border = Swing.EmptyBorder(30, 30, 10, 30)
    }
    listenTo(button)
    var nClicks = 0
    reactions += {
      case ButtonClicked(b) =>
        nClicks += 1
        label.text = "Number of button clicks: "+ nClicks
        b.text = nClicks +". time. Again?" // oma lisäys
    }
  }
}  
Kuuntelu voidaan myös lopettaa (deafTo(source)). Tapahtumista saadaan case-class-oliota (ei käsitelty kurssilla!), joihin reagointi eli tapahtuman käsittely lisätään top-kehyksen reactions-ominaisuuteen. Tuo b tuolla ButtonClicked(b)ssä antaa käyttöön AbstractButton-tyyppisen komponentin, jolle tapahtui jotakin ja jota voidaan toki myös itse käyttää.

Yhdessä lisättävässä reaktiossa saa olla useampiakin case-osia. Sekä itse lisätyistä että perityistä case-osista suoritetaan kaikki sopivat järjestyksessä: viimeisimmäksi lisätty, toiseksi viimeisimmäksi lisätty, jne. Regoinnit siis viedään pinoon.

Reagointeja voi myös poistaa reactions-kokoelmasta. Ja sehän tietenkin tehdään operaatiolla -=.