Scala GUI - 事件处理

时间:2014-02-12 17:18:53

标签: java swing scala events user-interface

我对Scala很新,但它似乎是一种非常有趣的语言,我想学习。目前,我一直致力于各种简单的应用程序来学习该语言。从基本语法到网络等,我只是想要牢牢掌握语言和库的工作方式。

截至目前,我正在使用一个简单的GUI计算器。构建GUI非常简单,我对它的实际可视组件没有任何疑虑。问题出在交互部分。出于某种原因,我无法弄清楚如何通过按键进行某种全局关注。这可能是一个很难说的方式,但这就是我的意思。除了程序首次打开时,我无法让程序响应我的按键操作。我相信问题在于焦点在哪里,但我无法弄明白。

这是我的(稍微剥离)代码:

package SimplePrograms

import scala.swing._
import javax.swing.{BorderFactory, UIManager}
import scala.swing.event.{Key, KeyPressed}

/**
 * Created by Tony on 2/9/14.
 */
object SimpleCalculator {
  def main(args: Array[String]){
    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel")
    val calculator = new CalcGrid
    val frame = new MainFrame{
      title = "Calculator"
      contents = calculator.CalcPanel()
      listenTo()
      reactions += {
        case KeyPressed(_,Key.Numpad1,_,_)
        => calculator.numTxt.text += "1"
      }
      size = new Dimension(200,270)
      centerOnScreen()
      resizable = false
    }
    frame.open()
  }
}

class CalcGrid(){
  var numTxt = new TextField(" "){
    font = new Font("Arial",0,40)
    background = new Color(200,130,20)
    opaque = true
    border = BorderFactory.createCompoundBorder(
      BorderFactory.createLoweredBevelBorder(),
      BorderFactory.createEmptyBorder(0,5,0,5))
    editable = false
    horizontalAlignment = Alignment.Right
  }
  val btn1 = new Button("1"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn2 = new Button("2"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn3 = new Button("3"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn4 = new Button("4"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn5 = new Button("5"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn6 = new Button("6"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn7 = new Button("7"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn8 = new Button("8"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn9 = new Button("9"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btn0 = new Button("0"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btnPeriod = new Button("."){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btnEqual = new Button("="){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,60)
  }
  val btnMinus = new Button("-"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }
  val btnPlus = new Button("+"){
    font = new Font("Arial",0,20)
    background = new Color(200,130,20)
    preferredSize = new Dimension(40,35)
  }

  def CalcPanel(): GridBagPanel = {
    val contents = new GridBagPanel(){
      var c = new Constraints()
      c.gridx = 0
      c.gridy = 0
      c.gridwidth = 4
      c.insets = new Insets(3,3,3,3)
      c.fill = GridBagPanel.Fill.Horizontal
      add(numTxt,c)

      c.gridwidth = 1
      c.fill = GridBagPanel.Fill.None

      c.gridx = 0
      c.gridy = 1
      add(btn7,c)

      c.gridx = 1
      c.gridy = 1
      add(btn8,c)

      c.gridx = 2
      c.gridy = 1
      add(btn9,c)

      c.gridx = 0
      c.gridy = 2
      add(btn4,c)

      c.gridx = 1
      c.gridy = 2
      add(btn5,c)

      c.gridx = 2
      c.gridy = 2
      add(btn6,c)

      c.gridx = 0
      c.gridy = 3
      add(btn1,c)

      c.gridx = 1
      c.gridy = 3
      add(btn2,c)

      c.gridx = 2
      c.gridy = 3
      add(btn3,c)

      c.gridx = 3
      c.gridy = 1
      add(btnMinus,c)

      c.gridx = 3
      c.gridy = 2
      add(btnPlus,c)

      c.gridx = 0
      c.gridy = 4
      c.gridwidth = 2
      c.fill = GridBagPanel.Fill.Horizontal
      add(btn0,c)
      c.gridwidth = 1
      c.fill = GridBagPanel.Fill.None

      c.gridx = 2
      c.gridy = 4
      add(btnPeriod,c)

      c.gridx = 3
      c.gridy = 3
      c.gridheight = 4
      c.fill = GridBagPanel.Fill.Vertical
      add(btnEqual,c)
    }
    contents
  }
}

我为草率的代码道歉,但正如我所说,只是快速地掀起了一些东西。通过测试各种代码选项并搜索互联网,我们已经开展了几天的工作。

任何帮助?

编辑:作为一个快速说明,我意识到listenTo方法没有填写其他一些问题的参数,但这是在经历了一些令人恼火的试错时刻之后,我想发布了多个版本可能不需要。

1 个答案:

答案 0 :(得分:2)

您的方法通常是正确的。但是,在使用listenTo方法时,您必须使用正确的发布者。例如,如果您编写以下内容,则当标记为“1”的按钮聚焦时,您可以键入“1”:

listenTo(calculator.btn1.keys)

关键事件由特殊发布者.keys发送,而不是组件本身。

所以这里最简单的方法是听取计算器的每个组成部分(所有按钮和面板)

listenTo(calculator.btn1.keys, calculator.btn2.keys, ...)

在Swing中,关键事件不会自动冒泡组件层次结构,而只会被调度到具有焦点的组件。 (Java)Swing中有一种替代方法,称为键绑定。有关概述,请参阅:

您可以使用第二种方法来监听活动窗口中任何位置按下的键。但是,作为Scala初学者,这可能会令人困惑,因为您将不得不使用Java Swing而不是Scala Swing包装层。为了完整起见,我将展示如何做到这一点:

import javax.swing.{JComponent, KeyStroke} // Java world

val calculator = new CalcGrid
val frame = new Frame{
  title = "Calculator"
  val panel = calculator.CalcPanel()      
  contents = panel
  val act1 = Action("key1") {
    calculator.numTxt.text += "1"
  }
  // the mapping is done in your top component, so `panel`.
  // you must use the input map which is active whenever the window
  // in which the panel is located has focus:
  val imap = panel.peer.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
  val amap = panel.peer.getActionMap
  // you need to map a KeyStroke to an (arbitrarily chosen) action name,
  // and that action name to the action itself.
  imap.put(KeyStroke.getKeyStroke(Key.Numpad1.id, 0), "key1")
  imap.put(KeyStroke.getKeyStroke(Key.Key1   .id, 0), "key1") // can map several keys
  amap.put("key1", act1.peer)

  size = new Dimension(200,270)
  centerOnScreen()
  resizable = false
}
frame.open()