我在Scala中有一个简单的swing应用程序。工作由一个单独的对象完成,但必须定期向启动它的GUI报告进度。问题是只有在Thread完成任务后才能看到更新。我尝试向repaint()
top.peer.repaint()
和Thread.sleep(0)
添加各种调用都无济于事,并且一直觉得我正在尝试添加这些是我的标志做错了。
我记得当我以前用Java开发并尝试根据我记得的正确方法来构建我的解决方案时,我正在努力克服这个问题,但我必须遗漏一些东西。
这是一个简单的例子,它可以重现这个问题:
import scala.swing._
import scala.swing.event.ButtonClicked
import BorderPanel.Position._
import java.awt.EventQueue
class HeavyLifter extends Runnable {
override def run = {
UpdateInterface.say("Performing Useful Work")
for (i <- 0 until Int.MaxValue) {
UpdateInterface.say(i.toString)
}
}
}
object UpdateInterface extends SimpleSwingApplication {
private val txtLog = new TextArea(32,64) {
editable = false
}
private val scrollPane = new ScrollPane(txtLog)
private val btnGo = new Button("Go")
def say(strWhat : String) = {
txtLog.append(strWhat + "\n")
}
def top = new MainFrame {
contents = new BorderPanel {
listenTo(btnGo)
reactions += {
case ButtonClicked(`btnGo`) => EventQueue.invokeLater(new HeavyLifter)
}
layout(scrollPane) = Center
layout(btnGo) = South
}
}
}
更新我已经接受了提请注意问题原因的答案。我愚蠢地认为,因为工作者是一个Runnable,EventQueue.invokeLater
会为它生成一个单独的线程。我一直指向Swing Worker的方向,这可能是正确的方法。
答案 0 :(得分:2)
EventQueue.invokeLater
导致循环在调度线程(即UI线程)中运行,这基本上意味着UI线程被占用来执行for循环,一旦完成,那么它将更新UI并且执行其他任务,毕竟一个线程可以一次完成一件事。这就是循环完成后UI更新的原因。
您可以做的是,在新线程中运行worker runnable,并使用EventQueue.invokeLater
为该每个更新(其run方法更新文本区域值)从该worker代码调度新的runnable。