1

私はScalaでGUIを書いていますが、foreachステートメントにボタンイベントを登録しようとしているときに、奇妙な問題に遭遇しました。オブジェクトのリスト内のすべての要素オブジェクトi (オブジェクト0 ...オブジェクトn)、対応するボタンx =ボタンiが取得され、指定されたボックスが。でサブスクライブされbox.listenTo(x)ます。ボタンが押されたときに、オブジェクトiに関連するアクションを実行する必要があります(この場合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=
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

なぜこれが起こっているのか、私は完全に途方に暮れています。とにかく私はScalaに慣れていないので、なじみのない領域ですが、Swingのソースコードをいじってみましたが、それでも無知です...内部の参照のすべての値をどのようにしたらよいでしょうか。ループはすべての反復で使用されますか?または、Swingによってすべてのイベントを一度にトリガーするにはどうすればよいですか?また...?

編集:ここに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 = 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
4

2 に答える 2

1

行で

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

新しいものを作成し、val keypadEventButtonそれを内部にあるものに割り当てButtonClicked()ます。行をに変更して case ButtonClicked(abstractButton)も機能し、同じ問題が表示されます。

keypadEventButtonこれが前の行の使用と一致することを期待していると思います。おそらく、1つのリアクションを作成してから、を使用しabstractButtonて、どのボタンが押されたかを確認する必要があります。

于 2012-08-30T13:56:35.170 に答える
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`) => {
于 2012-08-30T17:45:14.330 に答える