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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With