Asynchronous UI update with Swing - swing

I've written a Scala program that I'd like to be triggered through a UI (also in Swing). The problem is, when I trigger it, the UI hangs until the background program completes. I got to thinking that the only way to get around this is by having the program run in another thread/actor and have it update the UI as and when required. Updating would include a status bar which would show the file currently being processed and a progressbar.
Since Scala actors are deprecated, I'm having a tough time trying to plough through Akka to get some kind of basic multithreading running. The examples given on the Akka website are also quite complicated.
But more than that, I'm finding it difficult to wrap my head around how to attempt this problem. What I can come up with is:
Background program runs as one actor
UI is the main program
Have another actor that tells the UI to update something
Step 3 is what is confounding me. How do I tell the UI without locking up some variable somewhere?
Also, I'm sure this problem has been solved earlier. Any sample code for the same would be highly appreciated.

For scala 2.10
You can use scala.concurrent.future and then register a callback on completion. The callback will update the GUI on the EDT thread.
Lets do it!
//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
}
}
This is the most simple case:
we're updating only when done, with no progress
we're only handling the successful case
To deal with (1) you could combine different futures, using the map/flatMap methods on the Future instance. As those gets called, you can update the progress in the UI (always making sure you do it in a Swing.onEDT block
//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
}
}
To deal with (2) you can register a callback onFailure or handle both cases with the onComplete.
For relevant examples: scaladocs and the relevant SIP (though the SIP examples seems outdated, they should give you a good idea)

If you want to use Actors, following may work for you.
There are two actors:
WorkerActor which does data processing (here, there is simple loop with Thread.sleep). This actor sends messages about progress of work to another actor:
GUIUpdateActor - receives updates about progress and updates UI by calling handleGuiProgressEvent method
UI update method handleGuiProgressEvent receives update event.
Important point is that this method is called by Actor using one of Akka threads and uses Swing.onEDT to do Swing work in Swing event dispatching thread.
You may add following to various places to see what is current thread.
println("Current thread:" + Thread.currentThread())
Code is runnable Swing/Akka application.
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
}
}
}

If you need something simple you can run the long task in a new Thread and just make sure to update it in the EDT:
def swing(task: => Unit) = SwingUtilities.invokeLater(new Runnable {
def run() { task }
})
def thread(task: => Unit) = new Thread(new Runnable {
def run() {task}
}).run()
thread({
val stuff = longRunningTask()
swing(updateGui(stuff))
})

You can define your own ExecutionContext which will execute anything on the Swing Event Dispatch Thread using SwingUtilities.invokeLater and then use this context to schedule a code which needs to be executed by Swing, still retaining the ability to chain Futures the Scala way, including passing results between them.
import javax.swing.SwingUtilities
import scala.concurrent.ExecutionContext
object OnSwing extends ExecutionContext {
def execute(runnable: Runnable) = {
SwingUtilities.invokeLater(runnable)
}
def reportFailure(cause: Throwable) = {
cause.printStackTrace()
}
}
case ButtonClicked(_) =>
Future {
doLongBackgroundProcess("Timestamp")
}.foreach { result =>
txtStatus.text = result
}(OnSwing)

Related

Return string from Firebase addOnSuccessListener [duplicate]

I'm building an app for a friend and I use Firestore. What I want is to display a list of favorite places but for some reason, the list is always empty.
I cannot get the data from Firestore. This is my code:
fun getListOfPlaces() : List<String> {
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
return list;
}
If I try to print, let's say the size of the list in onCreate function, the size is always 0.
Log.d("TAG", getListOfPlaces().size().toString()); // Is 0 !!!
I can confirm Firebase is successfully installed.
What am I missing?
This is a classic issue with asynchronous web APIs. You cannot return something now, that hasn't been loaded yet. With other words, you cannot simply return the places list as a result of a method because it will always be empty due the asynchronous behavior of the onComplete function. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available.
But not only Cloud Firestore loads data asynchronously, almost all of modern other web APIs do, since it may take some time to get the data. But let's take an quick example, by placing a few log statements in the code, to see more clearly what I'm talking about.
fun getListOfPlaces() : List<String> {
Log.d("TAG", "Before attaching the listener!");
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d("TAG", "Inside onComplete function!");
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
Log.d("TAG", "After attaching the listener!");
return list;
}
If we run this code will, the output in your logcat will be:
Before attaching the listener!
After attaching the listener!
Inside onComplete function!
This is probably not what you expected, but it explains precisely why your places list is empty when returning it.
The initial response for most developers is to try and "fix" this asynchronous behavior, which I personally recommend against it. Here is an excelent article written by Doug Stevenson that I'll highly recommend you to read.
A quick solve for this problem would be to use the places list only inside the onComplete function:
fun readData() {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
//Do what you need to do with your list
}
}
}
If you want to use the list outside, there is another approach. You need to create your own callback to wait for Firestore to return you the data. To achieve this, first you need to create an interface like this:
interface MyCallback {
fun onCallback(value: List<String>)
}
Then you need to create a function that is actually getting the data from the database. This method should look like this:
fun readData(myCallback : MyCallback) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback.onCallback(list)
}
}
}
See, we don't have any return type anymore. In the end just simply call readData() function in your onCreate function and pass an instance of the MyCallback interface as an argument like this:
readData(object: MyCallback {
override fun onCallback(value: List<String>) {
Log.d("TAG", list.size.toString())
}
})
If you are using Kotlin, please check the other answer.
Nowadays, Kotlin provides a simpler way to achieve the same result as in the case of using a callback. This answer is going to explain how to use Kotlin Coroutines. In order to make it work, we need to add the following dependency in our build.gradle file:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.2.1"
This library that we use is called Module kotlinx-coroutines-play-services and is used for the exact same purpose. As we already know, there is no way we can return a list of objects as a result of a method because get() returns immediately, while the callback from the Task it returns will be called sometime later. That's the reason why we should wait until the data is available.
When calling "get()" on the Task object that is returned, we can attach a listener so we can get the result of our query. What we need to do now is to convert this into something that is working with Kotlin Coroutines. For that, we need to create a suspend function that looks like this:
private suspend fun getListOfPlaces(): List<DocumentSnapshot> {
val snapshot = placesRef.get().await()
return snapshot.documents
}
As you can see, we have now an extension function called await() that will interrupt the Coroutine until the data from the database is available and then return it. Now we can simply call it from another suspend method like in the following lines of code:
private suspend fun getDataFromFirestore() {
try {
val listOfPlaces = getListOfPlaces()
} catch (e: Exception) {
Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
}
}
The reason for having a empty list got perfectly answered by Alex Mamo above.
I just like to present the same thing without needing to add an extra interface.
In Kotlin you could just implement it like so:
fun readData(myCallback: (List<String>) -> Unit) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback(list)
}
}
}
and then use it like so:
readData() {
Log.d("TAG", it.size.toString())
})

How to create JSON output from a combined group of composite classes with Play framework

I am a Scala newbie, extending someone else's code. The code uses the Play framework's JSON libraries. I am accessing objects of class Future[Option[A]] and Future[Option[List[B]]. The classes A and B each have their own JSON writes method, so each can return JSON as a response to a web request. I'm trying to combine these into a single JSON response that I can return as an HTTP response.
I thought creating a class which composes A and B into a single class would allow me to do this, something along these lines:
case class AAndB(a: Future[Option[A]], b: Future[Option[List[B]]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}
But that fails all over the place. A and B are both structured like this:
sealed trait A extends SuperClass {
val a1: String = "identifier"
}
case class SubA(a2: ClassA2) extends A {
override val a1: String = "sub identifier"
}
object SubA {
val writes = Writes[SubA] { aa =>
Json.obj(
"a1" -> aa.a1
"a2" -> aa.a2
)
}
}
Since B is accessed as a List, the expected output would be along these lines:
{
"a":{
"a1":"val1",
"a2":"val2"
},
"b":[
{
"b1":"val 3",
"b2":"val 4"
},
{
"b1":"val 5",
"b2":"val 6"
},
{
"b1":"val 7",
"b2":"val 8"
}
]
}
Your help is appreciated.
As #cchantep mentioned in the comments on your question, having Futures as part of a case class declaration is highly unusual - case classes are great for encapsulating immutable domain objects (i.e that don't change over time) but as soon as you involve a Future[T] you potentially have multiple outcomes:
The Future hasn't completed yet
The Future failed
The Future completed successfully, and contains a T instance
You don't want to tangle up this temporal stuff with the act of converting to JSON. For this reason you should model your wrapper class with the Futures removed:
case class AAndB(a: Option[A], b: Option[List[B]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}
and instead use Scala/Play's very concise handling of them in your Controller class to access the contents of each. In the below example, assume the existence of injected service classes as follows:
class AService {
def findA(id:Int):Future[Option[A]] = ...
}
class BListService {
def findBs(id:Int):Option[Future[List[B]]] = ...
}
Here's what our controller method might look like:
def showCombinedJson(id:Int) = Action.async {
val fMaybeA = aService.findA(id)
val fMaybeBs = bService.findBs(id)
for {
maybeA <- fMaybeA
maybeBs <- fMaybeBs
} yield {
Ok(Json.toJson(AAndB(maybeA, maybeBs)))
}
}
So here we launch both the A- and B-queries in parallel (we have to do this outside the for-comprehension to achieve this parallelism). The yield block of the for-comprehension will be executed only if/when both the Futures complete successfully - at which point it is safe to access the contents within. Then it's a simple matter of building an instance of the wrapper class, converting to JSON and returning an Ok result to Play.
Note that the result of the yield block will itself be inside a Future (in this case it's a Future[Result]) so we use Play's Action.async Action builder to handle this - letting Play deal with all of the actual waiting-for-things-to-happen.

Accessing java.awt.Container.getComponents from scala

I'm trying to write automated tests for a gui application written in scala with swing. I'm adapting the code from this article which leverages getName() to find the right ui element in the tree.
This is a self-contained version of my code. It's incomplete, untested, and probably broken and/or non-idiomatic and/or suboptimal in various ways, but my question is regarding the compiler error listed below.
import scala.swing._
import java.awt._
import ListView._
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
import org.scalatest.FunSpec
import org.scalatest.matchers.ShouldMatchers
object MyGui extends SimpleSwingApplication {
def top = new MainFrame {
title = "MyGui"
val t = new Table(3, 3)
t.peer.setName("my-table")
contents = t
}
}
object TestUtils {
def getChildNamed(parent: UIElement, name: String): UIElement = {
// Debug line
println("Class: " + parent.peer.getClass() +
" Name: " + parent.peer.getName())
if (name == parent.peer.getName()) { return parent }
if (parent.peer.isInstanceOf[java.awt.Container]) {
val children = parent.peer.getComponents()
/// COMPILER ERROR HERE ^^^
for (child <- children) {
val matching_child = getChildNamed(child, name)
if (matching_child != null) { return matching_child }
}
}
return null
}
}
#RunWith(classOf[JUnitRunner])
class MyGuiSpec extends FunSpec {
describe("My gui window") {
it("should have a table") {
TestUtils.getChildNamed(MyGui.top, "my-table")
}
}
}
When I compile this file I get:
29: error: value getComponents is not a member of java.awt.Component
val children = parent.peer.getComponents()
^
one error found
As far as I can tell, getComponents is in fact a member of java.awt.Component. I used the code in this answer to dump the methods on parent.peer, and I can see that getComponents is in the list.
Answers that provide another approach to the problem (automated gui testing in a similar manner) are welcome, but I'd really like to understand why I can't access getComponents.
The issue is that you're looking at two different classes. Your javadoc link points to java.awt.Container, which indeed has getComponents method. On the other hand, the compiler is looking for getComponents on java.awt.Component (the parent class), which is returned by parent.peer, and can't find it.
Instead of if (parent.peer.isInstanceOf[java.awt.Container]) ..., you can verify the type of parent.peer and cast it like this:
parent.peer match {
case container: java.awt.Container =>
// the following works because container isA Container
val children = container.getComponents
// ...
case _ => // ...
}

How to run SimpleSwingApplication from console main method?

I wrote my first console app in Scala, and I wrote my first Swing app in Scala -- in case of the latter, the entry point is top method in my object extending SimpleSwingApplication.
However I would like to still go through main method, and from there call top -- or perform other equivalent actions (like creating a window and "running" it).
How to do it?
Just in case if you are curious why, the GUI is optional, so I would like to parse the command line arguments and then decide to show (or not) the app window.
If you have something like:
object MySimpleApp extends SimpleSwingApplication {
// ...
}
You can just call MySimpleApp.main to start it from the console. A main method is added when you mix SimpleSwingApplication trait. Have a look at the scaladoc.
Here is the most basic example:
import swing._
object MainApplication {
def main(args: Array[String]) = {
GUI.main(args)
}
object GUI extends SimpleSwingApplication {
def top = new MainFrame {
title = "Hello, World!"
}
}
}
Execute scala MainApplication.scala from the command line to start the Swing application.
If you override the main method that you inherit from SimpleSwingApplication, you can do whatever you want:
object ApplicationWithOptionalGUI extends SimpleSwingApplication {
override def main(args: Array[String]) =
if (parseCommandLine(args))
super.main(args) // Starts GUI
else
runWithoutGUI(args)
...
}
I needed main.args to be usable in SimpleSwingApplication. I wanted to give a filename to process as an argument in CLI, or then use JFileChooser in case the command line argument list was empty.
I do not know if there is simple method to use command line args in SimpleSwingApplication, but how I got it working was to define, in demoapp.class:
class demoSSA(args: Array[String]) extends SimpleSwingApplication {
....
var filename: String = null
if (args.length > 0) {
filename = args(0)
} else {
// use JFileChooser to select filename to be processed
// https://stackoverflow.com/questions/23627894/scala-class-running-a-file-chooser
}
....
}
object demo {
def main(args: Array[String]): Unit = {
val demo = new demoSSA(args)
demo.startup(args)
}
}
Then start the app by
demo [args], by giving filename as a CLI argument or leaving it empty and program uses JFileChooser to ask it.
Is there a way to get to the args of main() in SimpleSwingApplication singleton object?
Because it doesn't work to parse 'filename' in overridden SimpleSwingApplication.main and to use 'filename' in the singleton object it needs to be declared (val args: Array[String] = null;), not just defined (val args: Array[String];). And singleton objecs inherited from SSA cannot have parameters.
(Edit)
Found one another way:
If whole demoSSA is put inside overridden main() or startup(), then top MainFrame needs to defined outside as top = null and re-declare it from startup() as this.top:
object demo extends SimpleSwingApplication {
var top: MainFrame = null
override def startup(args: Array[String]) {
... // can use args here
this.top = new MainFrame {
... // can use args here also
};
val t = this.top
if (t.size == new Dimension(0,0)) t.pack()
t.visible = true
}
}
But I think I like the former method more, with separated main object.It has at least one level indentation less than the latter method.

Scala function call

I have found following function calls in several frameworks which appear to me as if the framework extends some base classes. Some examples:
within(500 millis)
or
"Testcase description" in
{ .... }
First example returns a duration object with the duration of 500 milliseconds from akka and second is the definition of a testcase from scalatest.
I would like to know how this behavior is achieved and how it is called.
This is done with the "Pimp my library" technique.
To add non existing methods to a class, you define an implicit method that converts objects of that class to objects of a class that has the method:
class Units(i: Int) {
def millis = i
}
implicit def toUnits(i: Int) = new Units(i)
class Specs(s: String) {
def in(thunk: => Unit) = thunk
}
implicit def toSpecs(s: String) = new Specs(s)
See also "Where does Scala looks for Implicits?"
If I'm not mistaken, those pieces of code can be desugared as
within(500.millis)
and
"Testcase description".in({ ... })
This should make it easier to see what's going on.