使用Swing进行异步UI更新

Pla*_*ove 6 swing scala

我写了一个Scala程序,我希望通过UI(也在Swing中)触发.问题是,当我触发它时,UI会挂起,直到后台程序完成.我认为解决这个问题的唯一方法是让程序在另一个线程/ actor中运行,并在需要时让它更新UI.更新将包括一个状态栏,显示当前正在处理的文件和进度条.

由于Scala演员已被弃用,我现在很难通过Akka来进行某种基本的多线程运行.Akka网站上给出的例子也很复杂.

但更重要的是,我发现很难绕过如何尝试这个问题.我能想到的是:

  1. 后台程序作为一个演员运行
  2. UI是主程序
  3. 让另一个演员告诉UI更新一些东西

第3步让我感到困惑.如何在不锁定某个变量的情况下告诉UI?

此外,我确信这个问题已经解决了.任何示例代码都将受到高度赞赏.

pag*_*_5b 7

对于scala 2.10

您可以scala.concurrent.future在完成时使用然后注册回调.回调将更新EDT线程上的GUI.

我们开始做吧!

//in your swing gui event listener (e.g. button clicked, combo selected, ...)
import scala.concurrent.future
//needed to execute futures on a default implicit context
import scala.concurrent.ExecutionContext.Implicits._ 


val backgroundOperation: Future[Result] = future {
    //... do that thing, on another thread
    theResult
}

//this goes on without blocking
backgroundOperation onSuccess {
    case result => Swing.onEDT {
        //do your GUI update here
    }
}
Run Code Online (Sandbox Code Playgroud)

这是最简单的情况:

  1. 我们只在完成后更新,没有进展
  2. 我们只处理成功案例

要处理(1)您可以使用实例上的map/ flatMapmethods 组合不同的未来Future.当这些被调用时,您可以更新UI中的进度(始终确保您在Swing.onEDT块中执行此操作)

//example progress update
val backgroundCombination = backgroundOperation map { partial: Result =>
    progress(2)
    //process the partial result and obtain
    myResult2
} //here you can map again and again

def progress(step: Int) {
    Swing.onEDT {
        //do your GUI progress update here
    }
}
Run Code Online (Sandbox Code Playgroud)

要处理(2)你可以注册回调onFailure或处理两种情况onComplete.

对于相关的例子:scaladocs和相关的SIP(虽然SIP示例似乎已经过时,但它们应该给你一个好主意)


Arn*_*cek 5

如果您想使用Actors,以下内容可能适合您.

有两个演员:

  • WorkerActor进行数据处理(这里有Thread.sleep的简单循环).该actor将有关工作进度的消息发送给另一个actor:
  • GUIUpdateActor - 通过调用handleGuiProgressEvent方法接收有关进度的更新并更新UI

UI更新方法handleGuiProgressEvent接收更新事件.重要的一点是,Actor使用一个Akka线程调用此方法,并使用Swing.onEDT在Swing事件调度线程中执行Swing工作.

您可以在各个地方添加以下内容以查看当前线程的内容.

println("Current thread:" + Thread.currentThread())
Run Code Online (Sandbox Code Playgroud)

代码是可运行的Swing/Akka应用程序.

import akka.actor.{Props, ActorRef, Actor, ActorSystem}
import swing._
import event.ButtonClicked

trait GUIProgressEventHandler {
  def handleGuiProgressEvent(event: GuiEvent)
}

abstract class GuiEvent

case class GuiProgressEvent(val percentage: Int) extends GuiEvent
object ProcessingFinished extends GuiEvent


object SwingAkkaGUI extends SimpleSwingApplication with GUIProgressEventHandler {

  lazy val processItButton = new Button {text = "Process it"}
  lazy val progressBar = new ProgressBar() {min = 0; max = 100}

  def top = new MainFrame {
    title = "Swing GUI with Akka actors"

    contents = new BoxPanel(Orientation.Horizontal) {
      contents += processItButton
      contents += progressBar
      contents += new CheckBox(text = "another GUI element")
    }

    val workerActor = createActorSystemWithWorkerActor()

    listenTo(processItButton)

    reactions += {
      case ButtonClicked(b) => {
        processItButton.enabled = false
        processItButton.text = "Processing"
        workerActor ! "Start"
      }

    }

  }

  def handleGuiProgressEvent(event: GuiEvent) {
    event match {
      case progress: GuiProgressEvent  => Swing.onEDT{
        progressBar.value = progress.percentage
      }
      case ProcessingFinished => Swing.onEDT{
        processItButton.text = "Process it"
        processItButton.enabled = true
      }
    }

  }

  def createActorSystemWithWorkerActor():ActorRef = {
    def system = ActorSystem("ActorSystem")

    val guiUpdateActor = system.actorOf(
      Props[GUIUpdateActor].withCreator(new GUIUpdateActor(this)), name = "guiUpdateActor")

    val workerActor = system.actorOf(
      Props[WorkerActor].withCreator(new WorkerActor(guiUpdateActor)), name = "workerActor")

    workerActor
  }


  class GUIUpdateActor(val gui:GUIProgressEventHandler) extends Actor {
    def receive = {
      case event: GuiEvent => gui.handleGuiProgressEvent(event)
    }
  }


  class WorkerActor(val guiUpdateActor: ActorRef) extends Actor {
    def receive = {
      case "Start" => {
        for (percentDone <- 0 to 100) {
            Thread.sleep(50)
            guiUpdateActor ! GuiProgressEvent(percentDone)
        }
      }
      guiUpdateActor ! ProcessingFinished
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你.由于缺少(或过量)文档而且因为API的版本不同,因此很难对akka和演员进行任何操作.就目前而言,我相信期货会按照我想要的方式完成.我现在觉得Actors对UI来说有点过分 (2认同)