Reputation: 2146
I have a simple swing application in Scala. Work is done by a separate object but progress must be periodically reported to the GUI which launched it. The problem is that the updates are only visible once the Thread has completed its task. I've tried adding various calls to repaint()
top.peer.repaint()
and Thread.sleep(0)
all to no avail and all the while feeling that the fact that I'm resorting to adding these is a sign I'm doing something wrong.
I remember struggling and overcoming this issue with this back when I used to develop in Java and have tried to structure my solution based on what I recall being the right approach but I must be missing something.
Here is a simple example which reproduces the problem:
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
}
}
}
Update I've accepted the answer which drew attention to the cause of the problem. I stupidly thought that since the worker was a Runnable that EventQueue.invokeLater
would spawn a separate thread for it. I have been pointed in the direction of Swing Worker which is probably the right way to go.
Upvotes: 1
Views: 425
Reputation: 33637
The EventQueue.invokeLater
cause the loop to run in the dispatch thread (i.e the UI thread), which basically means that the UI thread is occupied to do the for loop and once that is done then it will update the UI and perform other tasks, after all a thread can do a single thing at a time. This is the reason your UI gets updated after the loop is completed.
What you can do is, run the worker runnable in a new thread and from that worker code dispatch a new runnable, for each update, (whose run method updated the text area value) using EventQueue.invokeLater
.
Upvotes: 2