Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interacting with actors in scala swing applications

Tags:

scala

swing

actor

I'm writing a small application in scala. The application processes simple log files. Because the processing takes some time, I've decided to let my application core extend Actor.

class Application extends Actor {
  def react() {
    loop {
      react {
        case Process(file) => // do something interesting with file...
      }
    }
  }
}

The processing of a log file is triggered by clicking a button in the gui. The gui uses scala swing.

object Gui extends SimpleSwingApplication {
  val application = new Application().start()

  def top = new MainFrame {
    val startButton = new Button

    reactions += {
      case ButtonClicked(`startButton`) => application ! Process(file)
    }
  }
}

Now, the application core needs to notify the gui about the current progress.

  sender ! Progress(value) // whenever progress is made

I've solved this by creating a separate actor inside the gui. The actor is executed inside the edt thread. It listens to messages from the application core and updates the gui.

  object Gui extends SimpleSwingApplication {
    val actor = new Actor {
      override val scheduler = new SchedulerAdapter {
        def execute(fun: => Unit) { Swing.onEDT(fun) }
      }
      start()

      def act() {
        loop {
          react {
            case ForwardToApplication(message) => application ! message
            case Progress(value) => progressBar.value = value
          }
        }
      }
    }
  } 

Since the application core needs to know about the sender of the message, I also use this actor to forward messages from the gui to the application core, making my actor the new sender.

  reactions += {
    case ButtonClicked(`startButton`) => actor ! ForwardToApplication(Process(file))
  }

This code works just fine. My question: Is there a simpler way to do this? It whould be nice to simple use the reactions mechanism for my application messages:

  reactions += {
    case Progress(value) => progressBar.value = value
  }

Any ideas how to achieve this?

like image 230
Daniel Seidewitz Avatar asked Jan 06 '11 13:01

Daniel Seidewitz


1 Answers

I have extended on gerferras idea of making my application a swing.Publisher. The following class acts as intermediator between a swing.Reactor and an Actor.

import actors.Actor
import swing.Publisher
import swing.event.Event
import swing.Swing.onEDT

case class Send(event: Any)(implicit intermediator: Intermediator) {
  intermediator ! this
}
case class Receive(event: Any) extends Event

case class Intermediator(application: Actor) extends Actor with Publisher {
  start()

  def act() {
    loop {
      react {
        case Send(evt) => application ! evt
        case evt => onEDT(publish(Receive(evt)))
      }
    }
  }
}

Now my reactions can include both swing events and application events.

implicit val intermediator = Intermediator(application)
listenTo(intermediator, button)

reactions += {
  case ButtonClicked(`button`) => Send(Process(file))
  case Receive(Progress(value)) => progressBar.value = value
}

Note how the case class Send provides some syntactic sugar to easily create events and pass them to the intermediator.

like image 193
Daniel Seidewitz Avatar answered Sep 21 '22 11:09

Daniel Seidewitz