3

I have a very simple scala swing app and I want to return a value to the command line app from where the Swing app was triggered. If I select value "b", I want the GUI to return value "b" to me as soon as the button is pressed. How do I make the app wait for the correct user input and how do I return the value to the calling app?

import swing._
import event.{ButtonClicked}
object GuiDemo extends App {        

  object GUI extends SimpleSwingApplication {
    val button = new Button {
      text = "Go!"
    }

    val comboBox = new ComboBox(List("a", "b", "c"))
    def top = new MainFrame {
      contents = new FlowPanel {
        contents += comboBox
        contents += button
      }
    }

    listenTo(button)

    reactions += {
      case ButtonClicked(`button`) => { 
        val selection = comboBox.item
        button.enabled_= (false)
      } 
    }    
  }  

  println("Before starting the GUI")
  GUI.main(args)  
  val myValue = GUI.comboBox.item
  println("""Now I need to make a complicated transformation in scala 
  with the selected value: """ + myValue.map(_.toUpper) ) 
  // how do I get the selected value from the GUI?
}

Thanks!

Edit: At the moment I am not packaging it into a jar. Just compiling it and then running it with scala...

I need to return the selected value from the "GUI" to the "GuiDemo" scala app to do some further processing in scala.

So question really is:

How to wait for the GUI part to finish and then return (hand over) the selected value to GuiDemo.

Meredith
  • 3,928
  • 4
  • 33
  • 58
nemoo
  • 3,269
  • 4
  • 38
  • 51
  • Do you want to start that application via `scala ...`, or do you intend to compile it and run a whole jar? – 0__ Jun 24 '13 at 19:07
  • 1
    I ran your code ( thanks! ) and I think I see what you want. In Java, we'd make the dialog "pause" happen with a JOptionPane modal dialog, where closing the dialog returns the value selected. Check out scala.swing.Dialog. Normally those appear over other frames of an app though, like app->modal dialog over app which is shown then then the return value is returned to the app which is still active. Not sure if you can get a Dialog without a parent here. Maybe this answer would help: [scala-swing-newbie](http://stackoverflow.com/questions/7921182/scala-swing-newbie) – n0741337 Jun 24 '13 at 19:24
  • 1
    relevant: http://stackoverflow.com/q/6935918/770361 – Luigi Plinge Jun 24 '13 at 22:39
  • will try to implement something with the observer pattern – nemoo Jun 25 '13 at 12:59

3 Answers3

2

As soon as you access AWT/Swing, it will spin up the event dispatch thread and the application will keep running. So all you need to do to return to the terminal is quit the application, when the user has filled out the GUI from.

To "return a value to the command line app", that would be printing things into the console, I guess. So:

reactions += {
  case ButtonClicked(`button`) =>
    val selection = comboBox.item
    Console.out.println(selection)
    sys.exit(0)
}

Note that there is no need to nest two objects (GuiDemo and GUI), just use one:

import swing._

object GuiDemo extends SimpleSwingApplication {
  lazy val top = new MainFrame {
    val comboBox = new ComboBox(List("a", "b", "c"))
    val button   = Button("Go!") {
      val selection = comboBox.item
      Console.out.println(selection)
      sys.exit(0)
    }

    contents = new FlowPanel(comboBox, button)
  }
}

If you execute that through the interpreter, using scala GuiDemo.scala, you need to explicitly invoke the main method, by adding the following line at the end of the file:

GuiDemo.main(null)
0__
  • 66,707
  • 21
  • 171
  • 266
0

Here's a basic JOptionPane example with no parent specified which results in a lingering JFrame that has to be closed:

import swing._

object InputDialogExample extends SimpleSwingApplication {

  val ui = new BorderPanel {
    //content
  }

  def top = new MainFrame {
    title = "Main Frame"
    contents = ui
  }

  println("Before starting the GUI")

  val choices = List( "alpha", "beta", "gamma" )
  // the first arg is null which results in an empty dialog floating around after the choice is made ( not ideal ).
  val selection = Dialog.showInput( null, null, "Choose an option.", Dialog.Message.Plain, null, choices, choices( 0 ) )

  if( !selection.isEmpty )
    println("Now i have the selected value from the gui: " + selection.head )
}

based on the empty MainFrame from the "scala-swing-newbie" link I posed in the comment and your code. If no option is selected, the selection would be None.

I'm very new to Scala, so I don't know if it's possible or how to "cast" the MainFrame as the parent so the extra dialog isn't produced.

n0741337
  • 2,474
  • 2
  • 15
  • 15
  • well, the gui part will be more complex, so i cannot use a simple dialog. – nemoo Jun 25 '13 at 12:58
  • You can make an arbitrarily complicated JOptionPane when using it's constructor and a modal dialog that's not from one of it's showXXXDialog() methods (doesn't look like the scala libraries offer a canned version for this). Or you could try making any modal dialog and add components to it that know their selected states like ButtonGroups or Lists. After the dialog is closed, you can ask those components what their selected states are directly for your settings. – n0741337 Jun 25 '13 at 14:51
0

You may have gathered that the answer you will get is "don't do that". Instead, you can set up an Reactor to listen to the button and execute your code inside that.

However, if you really want to return a value to your main method, you can do it using Futures and Promises in Scala 2.10:

Some imports:

  import scala.concurrent._
  import duration._

In your main method:

  val p = promise[String]

  new Reactor {
    listenTo(GUI.button)
    reactions += {
      case e: ButtonClicked => p.success(GUI.comboBox.item)
    }
  }

  val myValue = Await.result(p.future, Duration.Inf)
    // this blocks until the future is complete
Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180