Взаимодействие с актерами в приложениях Scala Swing - PullRequest
9 голосов
/ 06 января 2011

Я пишу небольшое приложение в Scala.Приложение обрабатывает простые файлы журнала.Поскольку обработка занимает некоторое время, я решил позволить ядру моего приложения расширить Actor.

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

Обработка файла журнала запускается нажатием кнопки в графическом интерфейсе.Gui использует 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)
    }
  }
}

Теперь ядро ​​приложения должно уведомлять графический интерфейс о текущем прогрессе.

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

Я решил это, создав отдельного актера внутри графического интерфейса.Актер исполняется внутри потока edt.Он слушает сообщения из ядра приложения и обновляет графический интерфейс.

  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
          }
        }
      }
    }
  } 

Поскольку ядру приложения необходимо знать об отправителе сообщения, я также использую этот субъект для пересылки сообщений из графического интерфейса в ядро ​​приложения, что делает моего субъекта новым отправителем.1014 *

Этот код работает просто отлично.Мой вопрос: есть ли более простой способ сделать это?Было бы неплохо просто использовать механизм реакций для сообщений моего приложения:

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

Есть идеи, как этого добиться?

Ответы [ 2 ]

6 голосов
/ 22 января 2011

Я расширил идею Герферраса о том, чтобы сделать мое приложение swing.Publisher.Следующий класс действует как посредник между swing.Reactor и 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)))
      }
    }
  }
}

Теперь мои реакции могут включать как события свинга, так и события приложения.

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

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

Обратите внимание, что case class Send предоставляет некоторый синтаксический сахар для простого создания событий и передачи их посреднику.

4 голосов
/ 12 января 2011

Может быть, это проще, но не знаю, лучше ли это.Вместо того, чтобы делать приложение бэкэндом актера, вы можете создавать анонимного актера каждый раз, когда вам нужно обработать файл:

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

Для части обновления прогресса вы можете передать обратный вызов методу процессавыполняется каждый раз, когда выполняется новый прогресс:

import scala.actors.Actor.actor

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

В качестве альтернативы (не проверял) вы могли бы сделать ваше приложение scala.swing.Publisher и вместо использования обратного вызова каждый раз публиковать и обрабатывать события.Таким образом, код может быть:

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

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

А в приложении:

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))
  }  
}
...