2011-01-06 10 views
9

Estoy escribiendo una pequeña aplicación en Scala. La aplicación procesa archivos de registro simples. Debido a que el procesamiento lleva tiempo, he decidido permitir que mi aplicación extienda Actor.Interactuando con actores en aplicaciones de escalamiento de voz

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

El procesamiento de un archivo de registro se desencadena al hacer clic en un botón de la interfaz gráfica de usuario. La GUI usa 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) 
    } 
    } 
} 

Ahora, el núcleo de la aplicación necesita notificar a la guía sobre el progreso actual.

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

He resuelto esto mediante la creación de un actor independiente dentro de la GUI. El actor se ejecuta dentro del hilo edt. Escucha los mensajes del núcleo de la aplicación y actualiza la 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 
      } 
     } 
     } 
    } 
    } 

Puesto que el núcleo aplicación necesita saber sobre el remitente del mensaje, también utilizo este actor para reenviar mensajes desde la interfaz gráfica de usuario a la base de la aplicación, por lo que mi actor del nuevo remitente.

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

Este código funciona bien. Mi pregunta: ¿hay una manera más simple de hacer esto? Se whould sea agradable a simple uso del mecanismo de las reacciones de mis mensajes de aplicación:

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

Alguna idea de cómo lograr esto?

Respuesta

6

he extendido en gerferras idea de hacer mi solicitud de una swing.Publisher. La siguiente clase actúa como intermediario entre swing.Reactor y 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))) 
     } 
    } 
    } 
} 

Ahora mis reacciones pueden incluir eventos de columpios y eventos de aplicación.

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

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

Nota cómo el case class Send proporciona un poco de azúcar sintáctica para crear fácilmente eventos y pasarlas al intermediario.

+0

Creo que es una buena solución. Tal vez merezco un voto a cambio;) – gerferra

+0

Creo que te estás perdiendo 'intermediario!' En tu reacción a 'ButtonClicked' – gerferra

+0

@geferra La llamada' intermediador! 'Está dentro del constructor de' case class Send'. El 'intermediario' se pasa a través de un parámetro implícito. Estoy votando tu respuesta, ya que me inspiró para mi propia solución. –

4

Quizás esto sea más simple, pero no sé si es mejor. En lugar de hacer su aplicación secundaria de un actor, puede crear un actor anónimo cada vez que necesita para procesar el archivo:

reactions += { 
    case ButtonClicked(`startButton`) => application.process(file, { v: Int => Swing.onEDT(progressBar.value = v) }) 
} 

Para la parte progreso de la actualización, puede pasar una devolución de llamada al método de proceso para ser ejecutado cada vez que se realiza un nuevo curso:

import scala.actors.Actor.actor 

def process(f: File, progress: Int => Unit) { 
    actor { 
    // process file while notifying the progress using the callback 
    progress(n) 
    } 
} 

alternativa (no se han probado) que podría hacer su solicitud una scala.swing.Publisher y, en lugar de utilizar la devolución de llamada, publicar y evento cada vez. Por lo que el código podría ser:

listenTo(startButton, application) //application is a Publisher 

reactions += { 
    case ButtonClicked(`startButton`) => application.process(file) 
    case Progress(v) => progressBar.value = v 
} 

Y en la aplicación:

import scala.actors.Actor.actor 

def process(f: File) { 
    actor { 
    // process file while notifying the progress using an scala.swing.event.Event 
    publish(Progess(n)) 
    } 
} 
+0

Su primera idea con la devolución de llamada funciona, pero creo que mi enfoque con el actor es más extensible si necesito pasar mensajes adicionales. –

+0

¿Qué pasa con el segundo enfoque?Su aplicación estará unida a un front-end de Swing, pero creo que no es tan malo y, si leí su pregunta correctamente, es lo que está pidiendo al final – gerferra

+0

He intentado su segundo enfoque y de alguna manera lo extendí. Vea mi propia respuesta sobre cómo se ve esta solución. –

Cuestiones relacionadas