Question

I'm writing an image library for intro programming students to play with. (I stole the idea and the patterns from the image library for DrRacket.)

https://github.com/dupontmanualhs/dm-image

It's mostly written in Swing (that's the master branch), but I'm trying to convert it to ScalaFX (see the scalafx branch), and there are some issues. Ideally, a student should be able to do something like:

scala> import org.dupontmanual.image._
scala> TrainEngine.display()

and have a dialog box with a train engine show up. I've tried using the code at

https://github.com/scalafx/ScalaFX-Tutorials

in the stand-alone-dialog project, but if I include System.exit(0) after I dialog.showAndWait(), I get this error:

Not interrupting system thread Thread[process reaper,10,system]
Exception while removing reference: java.lang.InterruptedException
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at com.sun.glass.utils.Disposer.run(Disposer.java:69)
    at java.lang.Thread.run(Thread.java:744)
Not interrupting system thread Thread[Prism Font Disposer,10,system]
Exception in runnable
Exception in thread "JavaFX Application Thread"

(Note that I get the same error if I try to run the App from stand-alone-dialog in the console, so I'm guessing that calling System.exit(0) is not a great idea in the SBT console.)

If I leave the System.exit(0) line out, then things seem to work fine--mostly. After the first time I display the dialog, it doesn't bring the dialog into focus, so I have to click it to dismiss the dialog. But the real problem is that when I :q to exit the console, SBT hangs and I have to Ctrl-C to be able to type again. (And, yes, Ctrl-C exits SBT completely, not just the console.)

I think what I may need to do is create a thread specifically for ScalaFX stuff. For example, I have a method to stack one image on top of another, and I got an IllegalStateException when I tried to call that function, even though it doesn't actually display anything, just creates a new Group with the two previous Nodes stacked appropriately. Unfortunately, I'm not sure how to create a new thread and make sure that everything image-related runs through that.

I've already set fork := true in build.sbt, but that doesn't seem to make a difference with the console.

== Update ==

I found initialCommands and cleanupCommands in the SBT documentation and tried to clean up after everything when the console starts and ends. The values are:

initialCommands in console := """import org.dupontmanual.image._; org.dupontmanual.image.initialize()"""

cleanupCommands in console := """org.dupontmanual.image.cleanUp()"""

which are defined thusly:

package object image {
  var masterFrame: JFrame = _

  def initialize() {
    masterFrame = new JFrame()
    masterFrame.add(new JFXPanel())
    masterFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
  }

  def cleanUp() {
    println("exiting platform")
    Platform.exit()
    println("disposing of frames")
    Frame.getFrames().foreach {
      _.dispose()
    }
    println("frames all disposed")
    System.exit(0);
  }

Here's the result of running the console and then trying to quit:

> console
[info] Compiling 1 Scala source to /home/sysadmin/dm-workspace/dm-image/target/scala-2.10/classes...
[info] Starting scala interpreter...
[info] 
import org.dupontmanual.image._
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_51).
Type in expressions to have them evaluated.
Type :help for more information.

scala> Hacker.display()

scala> :q
exiting platform
disposing of frames
frames all disposed
Not interrupting system thread Thread[XToolkt-Shutdown-Thread,5,system]
Not interrupting system thread Thread[AWT-XAWT,6,system]
Not interrupting system thread Thread[Prism Font Disposer,10,system]
Not interrupting system thread Thread[Java2D Disposer,10,system]
Exception while removing reference: java.lang.InterruptedException
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at com.sun.glass.utils.Disposer.run(Disposer.java:69)
    at java.lang.Thread.run(Thread.java:744)

Exception: sbt.TrapExitSecurityException thrown from the UncaughtExceptionHandler in thread "run-main-0"

and that doesn't even exit the console. You still have to use Ctrl-C, which exits out of SBT completely.

Something is still running, but I can't figure out what it is. Grrr.

Was it helpful?

Solution

I think the problem is that you would somehow need to fork the console, so perhaps it's this issue: https://github.com/sbt/sbt/issues/1918

The following idea seems to work: Instead of the sbt console you embed a REPL, such as Ammonite. Still sbt run doesn't work, even with fork in run := true. But packaging a fat jar and running that does seem to work:

build.sbt

name := "Foo"

version := "0.1.0"

scalaVersion := "2.11.7"

libraryDependencies ++= Seq(
  "org.scala-lang.modules" %% "scala-swing"   % "1.0.2",
  "com.lihaoyi"            %  "ammonite-repl" % "0.5.1" cross CrossVersion.full
)

project/build.properties

sbt.version=0.13.9

project/plugins.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.1")

src/main/scala/foo/Main.scala

package foo

import scala.swing._

object Main extends App {
  def test(): Unit = Swing.onEDT {
    new MainFrame {
      contents = new Label("App exits if you close window")
    } .open()
  }

  ammonite.repl.Main.run("")
}

Then

$ sbt assembly
...
$ java -jar target/scala-2.11/Foo-assembly-0.1.0.jar
Loading...
Welcome to the Ammonite Repl 0.5.1
(Scala 2.11.7 Java 1.8.0_66)
@ foo.Main.test()

The only oddity is that after the application exists, the shell character echoing is broken, perhaps that's an issue of Ammonite, you might want to try with the default Scala REPL.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top