当前位置: 首页 > 知识库问答 >
问题:

Scala Swing Box订阅多个事件

潘修为
2023-03-14

我正在用Scala编写一个GUI,在尝试在一个foreach语句中注册按钮事件时遇到了一个奇怪的问题:对于对象列表中的每个元素(对象0)…对象n),检索相应的按钮x=按钮i,并用代码框订阅给定框。listenTo(x)。按下按钮时,应执行与对象相关的一些操作(在这种情况下,println(“事件触发:”事件)):

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

共有2个答案

寇宏义
2023-03-14

@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 other”,因为我们使用反勾号来引用先前定义的变量的值。(如果变量以大写字母开头,也可以尝试此操作…)

所以你只需要添加反勾号,即。

case ButtonClicked(`button`) => {
马阳曦
2023-03-14

排队

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

您正在创建一个新的val keypadEventButton并将其分配给ButtonClicked()中的任何内容。将行更改为case Button点击(abstractButton)仍然可以工作并显示相同的问题。

我猜您希望这与前面几行中使用的按键事件按钮相匹配。您可能希望创建一个反应,然后使用abstractButton告诉您按下了什么按钮。

 类似资料:
  • Node.js应用程序可以使用composer-client.BusinessNetworkConnection.onAPI调用从业务网络订阅事件。事件在业务网络模型文件中定义,并由交易处理函数文件中的指定交易处理。有关发布事件的更多信息,请参阅发布事件。 在你开始之前 在应用程序可以订阅事件之前,你必须定义一些事件和发送它们的交易。还必须部署业务网络,并且必须具有可连接到该业务网络的连接配置文件

  • 由于Guava的留档很短,我在这里问它: 是否有办法将事件分派给多个订阅者,或者事件总是由第一个合适的订阅者使用? 如果是后者,为了添加这样的功能或在自己的应用程序中实现整个事件总线逻辑,是否最好扩展EventBus类?

  • 我正在寻找一种将多个订阅者附加到RxJava可观察流的方法,每个订阅者异步处理发出的事件。 我第一次尝试使用。flatMap(),但这似乎对任何后续订阅服务器都不起作用。所有订阅服务器都在同一线程上处理事件。 最终工作的是通过每次创建一个新的可观察的来消耗新线程中的每个事件: 输出: 以及多个订阅者的最终结果: 输出: 然而,这似乎有点笨拙。有没有更优雅的解决方案,或者RxJava不是一个很好的用

  • 我有一个类处理一个图像,这可能是一个缓慢的过程。当工作完成时,该类包含图像的一些特性,如主色。 我有许多其他的代码想知道主颜色,当他们要求它时,它可能是或可能没有准备好。 我还没有找到使用RXJava2实现这一点的简单方法。有人能帮帮我吗? null ReplaySubject似乎有一些我正在寻找的属性,但我不确定如何正确地实现它。

  • 本文向大家介绍如何在C#中订阅事件,我们可以在C#中为一个事件拥有多个订阅者吗?,包括了如何在C#中订阅事件,我们可以在C#中为一个事件拥有多个订阅者吗?的使用技巧和注意事项,需要的朋友参考一下 事件使类或对象在发生感兴趣的事件时通知其他类或对象。 引发事件的类称为发布者,而处理事件的类称为订阅者。 在事件中 一个事件可以有多个订阅者。订阅者可以处理来自多个发布者的多个事件。 没有订阅者的事件永远

  • Tendermint 会发出不同的事件,您可以通过Websocket订阅这些事件。这对于第三方应用程序(如 analysys)或检查状态非常有用。 事件列表 您可以通过 Websocket 调用 subscribe RPC 方法订阅上面的任何事件。 { "jsonrpc": "2.0", "method": "subscribe", "id": "0", "para