我在Scala中编写GUI,在尝试在foreach
语句中注册Button事件时遇到了一个奇怪的问题:应该是每个元素 object i 在对象列表中(对象 0 ... 对象 n ),检索相应的Button x = button i ,并使用box.listenTo(x)
订阅给定的Box。按下按钮时,应执行与对象 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触发每个事件?或...?
编辑:这是两个最小化版本,两者的行为都不同:
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 :(得分:1)
在第
行reactions += {
case ButtonClicked(keypadEventButton) => {
您正在创建新的val keypadEventButton
并将其分配给ButtonClicked()
内的任何内容。将行更改为case ButtonClicked(abstractButton)
仍然有效,并显示相同的问题。
我猜你希望这与前面几行keypadEventButton
的使用相匹配。您可能想要创建一个反应,然后使用abstractButton
来告诉按下了什么按钮。
答案 1 :(得分: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`) => {