2012-08-30 110 views
1

我寫Scala中的一個圖形用戶界面,同時試圖註冊在foreach聲明按鈕事件我經歷了一個奇怪的問題,運行:這應該是每一個元素對象在對象的列表(對象 ... 對象ñ),相應的按鈕 X =按鈕被檢索和給定的框訂閱了它與box.listenTo(x)。當按鈕被按下,有些動作有關對象應進行(在這種情況下,println("Event triggered: " + event)):斯卡拉鞦韆盒訂閱次數多活動

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 -> "1", 
     PhoneKeyEvent.Key2 -> "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 => 
       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) => { 
        // 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 -> 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 => contents += createPhoneKeyEventButton(phoneKeyEvent)) 
     } 

    } 

    override def top = new MainFrame { 

     contents = createPhoneControllerPanel() 


    } 

} 

不過,我得到一些非常奇怪的行爲,其中點擊任何按鈕導致所有這樣的對象觸發操作 - 請參閱該程序的輸出:

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= 
[email protected]09,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= 
[email protected]09,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 

我不知所措,爲什麼發生這種情況完全是;無論如何,我在Scala方面相當新穎,所以這是一個相當陌生的領域,但我試過擺弄了很多東西,並且在Swing源代碼中窺探過,並且仍然無能爲力...... 值如何引用循環內部用於每次迭代?或者Swing如何一次觸發每個事件?要麼...?

編輯:這裏有兩個最小的版本,兩者的表現不同:

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 => 
      listenTo(button) 
      reactions += { 
       case ButtonClicked(button) => { 
        println("Event triggered: " + button.text) 
       } 
      } 
     }) 


    } 


} 

}

打印:

Button 1 pressed 
Event triggered: 1 
Event triggered: 1 
Button 2 pressed 
Event triggered: 2 
Event triggered: 2 

而且這似乎是正確的行爲版本(但我不知道爲什麼):

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" -> button1, "2" -> button2) 
      buttons.foreach({ eventButton => 
       listenTo(eventButton._2) 
       reactions += { 
        case ButtonClicked(eventButton._2) => { 
         println("Event triggered: " + eventButton._1) 
        } 
       } 
      }) 


     } 


    } 

} 

打印(正確):

Button 1 pressed 
Event triggered: 1 
Button 2 pressed 
Event triggered: 2 
+0

TL;博士 - 你應該歸結代碼的本質問題 –

+0

問題是,當我試圖「煮沸代碼」時,我得到了不同的(但相關的)行爲,我不知道它們在哪裏有所不同:下面是兩個不同的版本,其行爲與上述不同: – errantlinguist

回答

1

在行

reactions += { 
    case ButtonClicked(keypadEventButton) => { 

要創建一個新的val keypadEventButton和任何在裏面ButtonClicked()其分配給。將行更改爲case ButtonClicked(abstractButton)仍然可以正常工作並顯示相同的問題。

我猜你期待這個匹配keypadEventButton在前面的行上的使用。您可能想要創建一個反應,然後使用abstractButton來說明按下了哪個按鈕。

+0

感謝很多的幫助:我得到它的行爲,我希望它通過取代'反應'塊與 '反應+ = { case ButtonClicked(abstr actButton)=> { \t如果(AbstractButton中== keypadEventButton){ \t \t的println( 「事件觸發:」 +事件) \t \t}} }' – errantlinguist

1

@andy是正確的。像IDEA這樣的好IDE會突出顯示「可變模式下的可疑陰影」,因爲您在模式匹配中綁定了一個新變量。 Scala允許您在嵌套代碼塊中儘可能多地隱藏變量:

scala> val a = 1; {val a = 2; println(a)}; println(a) 
2 
1 
a: Int = 1 

那麼以下返回值是什麼?

val a = 1 
2 match { 
    case a => "it was 1" 
    case _ => "something else" 
} 

它返回"it was 1"因爲a被遮蔽。現在試試:

2 match { 
    case `a` => "it was 1" 
    case _ => "something else" 
} 

這將返回"something else"因爲我們使用反引號來指代他先前定義的變量的值。 (也可以嘗試這其中變量以大寫字母開頭...)

所以你只需要添加反引號,即

case ButtonClicked(`button`) => { 
+0

非常感謝遮蔽尖端;然而,在ButtonClicked給定的情況下,我得到一個「需要穩定的標識符,但找到keypadEventButton」消息。不過,我找到了一個使用這個信息和安迪的解決方案(見上文)。 – errantlinguist

+0

@errantlinguist這是因爲您將'keypadEventButton'聲明爲'var'。除非你真的需要,否則不要使用'var'。將它聲明爲'val',它應該是可以的。你可以用guard來替代你寫的更好:'case b:ButtonClicked if b == keypadEventButton => ...' –