Note that there are some explanatory texts on larger screens.

plurals
  1. POScala Swing Box Subscribes Multiple Events
    primarykey
    data
    text
    <p>I'm writing a GUI in Scala, and I've run across a strange problem while trying to register Button Events in a <code>foreach</code> statement: It should be that for every element <i>object<sub>i</sub></i> in a list of objects (<i>object<sub>0</sub></i> ... <i>object<sub>n</sub></i>), a corresponding Button <i>x = button<sub>i</sub></i> is retrieved and a given Box is subscribed to it with <code>box.listenTo(x)</code>. When the Button is pushed, some action related to the <i>object<sub>i</sub></i> should be performed (in this case, <code>println("Event triggered: " + event)</code>):</p> <pre><code>import scala.swing.ComboBox import scala.collection.mutable.Buffer import scala.swing.Button import scala.swing.event.ButtonClicked import scala.swing.Action import scala.swing.SimpleSwingApplication import scala.swing.MainFrame import scala.swing.GridPanel import scala.swing.BorderPanel object EventSet extends SimpleSwingApplication { object PhoneKeyEvent extends Enumeration { val Key1 = Value("1") val Key2 = Value("2") } /* Constants */ private val DisplayHistory = Buffer[String]() private val KeypadKeyEvents = List( PhoneKeyEvent.Key1, PhoneKeyEvent.Key2) private val PhoneKeyEventButtonNames = Map( PhoneKeyEvent.Key1 -&gt; "1", PhoneKeyEvent.Key2 -&gt; "2" ) /* End constants */ private var PhoneKeyEventButtons = Map[PhoneKeyEvent.Value, Button]() private def createDisplay() : ComboBox[String] = { new ComboBox(DisplayHistory) { // Listen to keypad keys // Get the set of all keypad key events val keypadEvents = List(PhoneKeyEvent.Key1, PhoneKeyEvent.Key2) println("keypadEvents: " + keypadEvents) keypadEvents.foreach({ event =&gt; println("event: " + event) // Listen to each button representing a keypad key event var keypadEventButton = PhoneKeyEventButtons(event) println("keypadEventButton: " + keypadEventButton) listenTo(keypadEventButton) reactions += { case ButtonClicked(keypadEventButton) =&gt; { // TODO: fix strange bug here: adds all possible inputs println("Event triggered: " + event) // selection.item = selection.item + event } } }) } } private def createPhoneControllerPanel() : BorderPanel = { new BorderPanel() { val keypadControlPanel = createPhoneKeyEventTypeControlPanel(KeypadKeyEvents) add(keypadControlPanel, BorderPanel.Position.Center) add(createDisplay(), BorderPanel.Position.North) focusable = true requestFocus } } /** * Creates a new {@link Button} for a given {@link PhoneKeyEvent} and adds * the button to the global map of such buttons to their respective events; * that means multiple buttons cannot be created for the same key event. */ private def createPhoneKeyEventButton(phoneKeyEvent: PhoneKeyEvent.Value) : Button = { // Only one button can be created per key event require(!PhoneKeyEventButtons.contains(phoneKeyEvent), {System.err.println("A Button for the PhoneKeyEvent " + phoneKeyEvent + "has already been created.")}) val keyEventButtonName = PhoneKeyEventButtonNames(phoneKeyEvent) val result = new Button(Action(keyEventButtonName) { println("Key event button pressed: " + phoneKeyEvent) }) // Add the button to the map of all created key event buttons PhoneKeyEventButtons += phoneKeyEvent -&gt; result return result } private def createPhoneKeyEventTypeControlPanel(keyEvents : Iterable[PhoneKeyEvent.Value]) : GridPanel = { new GridPanel(4, 3) { // Get the intersection of all key events of the given type and the events with button names keyEvents.foreach(phoneKeyEvent =&gt; contents += createPhoneKeyEventButton(phoneKeyEvent)) } } override def top = new MainFrame { contents = createPhoneControllerPanel() } } </code></pre> <p>However, I get some very strange behaviour, where clicking any Button results in <i>all</i> such object actions are triggered -- See the program output:</p> <pre><code>keypadEvents: List(1, 2) event: 1 keypadEventButton: scala.swing wrapper scala.swing.Button$$anon$1[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border= javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@7633f09,flags=296,maximumSize=,minimumSize=,preferredSize=,de faultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14] ,paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,te xt=1,defaultCapable=true] event: 2 keypadEventButton: scala.swing wrapper scala.swing.Button$$anon$1[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border= javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@7633f09,flags=296,maximumSize=,minimumSize=,preferredSize=,de faultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14] ,paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,te xt=2,defaultCapable=true] Key event button pressed: 1 Event triggered: 1 Event triggered: 2 Key event button pressed: 2 Event triggered: 1 Event triggered: 2 </code></pre> <p>I'm completely at a loss as to why this is happening; I'm quite new at Scala anyway, so it's quite unfamiliar territory, but I've tried fiddling with a lot of stuff and snooped around in the Swing source code, and still clueless... how can <i>every</i> value of a reference inside of a loop be used in every iteration? or how can every event be triggered by Swing at once? or...?</p> <p><strong>Edit:</strong> Here are two minimised versions, both of which behave differently:</p> <pre><code>import scala.swing.SimpleSwingApplication object ButtonEvents extends SimpleSwingApplication { import scala.swing.Button import scala.swing.event.ButtonClicked import scala.swing.Action import scala.swing.MainFrame import scala.swing.FlowPanel override def top = new MainFrame { contents = new FlowPanel { val button1 = new Button(Action("1") { println("Button 1 pressed") }) contents += button1 val button2 = new Button(Action("2") { println("Button 2 pressed") }) contents += button2 val buttons = List(button1, button2) buttons.foreach({ button =&gt; listenTo(button) reactions += { case ButtonClicked(button) =&gt; { println("Event triggered: " + button.text) } } }) } } </code></pre> <p>}</p> <p>Prints:</p> <pre><code>Button 1 pressed Event triggered: 1 Event triggered: 1 Button 2 pressed Event triggered: 2 Event triggered: 2 </code></pre> <p>And a version which seems to behave correctly (but I'm not sure why):</p> <pre><code>import scala.swing.SimpleSwingApplication object ButtonEvents extends SimpleSwingApplication { import scala.swing.Button import scala.swing.event.ButtonClicked import scala.swing.Action import scala.swing.MainFrame import scala.swing.FlowPanel override def top = new MainFrame { contents = new FlowPanel { val button1 = new Button(Action("1") { println("Button 1 pressed") }) contents += button1 val button2 = new Button(Action("2") { println("Button 2 pressed") }) contents += button2 val buttons = Map("1" -&gt; button1, "2" -&gt; button2) buttons.foreach({ eventButton =&gt; listenTo(eventButton._2) reactions += { case ButtonClicked(eventButton._2) =&gt; { println("Event triggered: " + eventButton._1) } } }) } } } </code></pre> <p>Prints (correct):</p> <pre><code>Button 1 pressed Event triggered: 1 Button 2 pressed Event triggered: 2 </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload