Return a value from a Scala + Swing app

2019-07-01 19:49发布

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.

3条回答
霸刀☆藐视天下
2楼-- · 2019-07-01 20:24

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.

查看更多
时光不老,我们不散
3楼-- · 2019-07-01 20:28

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)
查看更多
兄弟一词,经得起流年.
4楼-- · 2019-07-01 20:30

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
查看更多
登录 后发表回答