监听器和反应Scala中摇摆 [英] Listeners and reactions in Scala Swing

查看:273
本文介绍了监听器和反应Scala中摇摆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经做了搜索,并在日食一些试验和错误的公平一点,但似乎在我的听众和使用的Swing在Scala中编写GUI时反应的理解的差距。

I've done a fair bit of searching and some trial and error in eclipse, but there seems to be a gap in my understanding of listeners and reactions when writing a GUI in Scala using Swing.

是否每个听者获得一个反应块,或者我就可能产生一个事件和应对每一个大的反应,与case语句块中的所有组件注册监听器?

Does each listener get a reactions block, or do I register listeners on all components that might generate an event and react to each on in a large reactions block with case statements?

确切位置在哪里做的听众和反应块属于

Where exactly do the listeners and reaction blocks belong.

下面是我的GUI code的缩写版本:

Here's an abbreviated version of my GUI code:

import scala.swing._
import scala.swing.event.ButtonClicked
import scala.swing.event.KeyTyped
import scala.swing.event.KeyPressed

object HumanGUI extends SimpleGUIApplication {

 val basicPane = new java.awt.Dimension(800, 200)
 val botPane = new java.awt.Dimension(400, 200)
 val felt = new java.awt.Color(35, 125, 35)

 def top = new MainFrame {

    title = "Blackjack GUI"

    val ConnectionPanel = new BoxPanel(Orientation.Vertical) {
      background = felt
      preferredSize = new java.awt.Dimension(155, 90)
      minimumSize = preferredSize
      maximumSize = preferredSize

      val ipAddressLabel = new Label("House IP:")
      ipAddressLabel.foreground = java.awt.Color.WHITE
      ipAddressLabel.horizontalTextPosition = scala.swing.Alignment.Left

      val portLabel = new Label("House port:")
      portLabel.foreground = java.awt.Color.WHITE
      portLabel.horizontalTextPosition = scala.swing.Alignment.Left

      val ipAddressTextField = new TextField
      val portTextField = new TextField

      contents += ipAddressLabel
      contents += ipAddressTextField
      contents += portLabel
      contents += portTextField
    }

    val DetailPanel = new BoxPanel(Orientation.Vertical) {
      background = felt
      preferredSize = new java.awt.Dimension(100, 160)
      minimumSize = preferredSize
      maximumSize = preferredSize

      val nameLabel = new Label("Your name:")
      nameLabel.foreground = java.awt.Color.WHITE
      nameLabel.horizontalTextPosition = scala.swing.Alignment.Left

      val bankrollLabel = new Label("Bankroll:")
      bankrollLabel.foreground = java.awt.Color.WHITE
      bankrollLabel.horizontalTextPosition = scala.swing.Alignment.Left

      val betLabel = new Label("Bet:")
      betLabel.foreground = java.awt.Color.WHITE
      betLabel.horizontalTextPosition = scala.swing.Alignment.Left

      val nameTextField = new TextField
      val bankrollTextField = new TextField
      val betTextField = new TextField

      val goButton = new Button("Go!")

      contents += nameLabel
      contents += nameTextField
      contents += bankrollLabel
      contents += bankrollTextField
      contents += betLabel
      contents += betTextField
      contents += goButton
    }

    val PlayPanel = new BoxPanel(Orientation.Vertical) {
      background = felt
      val hitButton = new Button("Hit")
      val stayButton = new Button("Stay")
      val doubleButton = new Button("Double")
      val quitButton = new Button("Quit")

      contents += hitButton
      contents += stayButton
      contents += doubleButton
      contents += quitButton
    }

    val playerPanel = new BoxPanel(Orientation.Horizontal) {
      background = felt
      border = new javax.swing.border.LineBorder(java.awt.Color.WHITE)
      preferredSize = basicPane
      minimumSize = basicPane
      maximumSize = basicPane
      opaque = true

      contents += ConnectionPanel
      contents += DetailPanel
      contents += PlayPanel
    }

    contents = new BoxPanel(Orientation.Vertical) {
      contents += playerPanel
    }
  }
}

所以,问题是我在哪里把我的听众,反应块?结果
我想在PlayPanel的按钮,并在这两个ConnectionPanel和DetailPanel文本字段反应。结果
难道我把听众和反应块尽可能接近,我很感兴趣地,还是我把听众和反应的大块在主机段末端的元素呢?结果
它甚至关系呢?

So the question is where do I put my listeners and reaction blocks?
I want to react to the buttons in PlayPanel, and the text fields in both ConnectionPanel and DetailPanel.
Do I put the listeners and reaction blocks as close to the elements that I'm interested as possible, or do I put a big block of listeners and reactions at the end of the MainFrame section?
Does it even matter?

修改结果
我做了显著的进步,并有很多东西,我需要工作,以更好地了解我以前没有得到的概念一起。

EDIT
I've made significant progress and have much of what I need working, along with a better understanding of the concepts I wasn't getting before.

这Odersky的的在Scala编程这是摘录什么是最帮助我。具体来说,从这个页面的例子:

This excerpt from Odersky's "Programming in Scala" was what helped me the most. Specifically, the example from this page:

http://www.artima.com/pins1ed/gui-programming.html

在code是文本的第一个版本,所以我怀疑是否有在斯卡拉2.9一个更好的办法,但它是一个清晰简洁,总结了一下,我被误解。

The code is from the first edition of the text, so I question whether or not there's a better way in Scala 2.9, but it was clear an concise and summed up what I was misunderstanding.

从这个例子中,这是一个简单的华氏摄氏度转换器,我才明白,监听器和反应块属于后的内容阻塞大型机上。

From the example, which is a simple fahrenheit to celsius converter, I came to understand that the listener and the reactions blocks belongs after the contents block for the MainFrame.

所以我结束了:

object HumanGUI extends SimpleSwingGUIApplication {
  def top = new MainFrame {
    title = "My Blackjack GUI"

    //The fields I want to work with are instantiated as object
    object ipAddressTextField extends TextField { columns = 15 }
    object portNumberTextField extends TextField {columns = 5 }

    //other panels, objects, etc would go here
    val OtherPanel = new BoxPanel(Orientation.Horizontal) {
       label = "Other Panel"
    }

    //and here we have the contents += block for the mainframe, other panels, etc from 
    //above would be added to the main frame here
    contents = new BoxPanel(Orientation.Vertical) {
      contents += ipAddressTextField
      contents += portNumberTextField
    }

    //here's the listen to, listening on the object created above, and it's enclosed in 
    //in backticks, a good explanation of that is found in the link below
    listenTo(`ipAddressTextField`)
    reactions += {
      case EditDone('ipAddressTextField`) =>
        //do something!
    }
  }

<一个href=\"http://stackoverflow.com/questions/6576594/need-clarification-on-scala-literal-identifiers-backticks\">Need澄清斯卡拉字面标识符(反引号)

如此看来,在回答我的问题是,listenTo和反应块属于在大型机块,但它的内容之后应该会出现+ = {//内容}块。

So it seems that the answer to my question is that the listenTo and reactions blocks belong in the MainFrame block, but should appear after it's contents += { //contents } block.

在Eclipse中的其他反复试验表明,虽然这种解决方案对我的作品,显然更多,我不明白。例如,虽然我无法得到密钥preSS事件侦听器来工作,如果我试图倾听和结果中的反应,他们
    VAL OtherPanel =新BoxPanel(Orientation.Horizo​​ntal){}结果
上述code的一部分,我能得到一个按钮,注册这样的工作:

Additional trial and error in eclipse shows that while this solution works for me, there is clearly much more that I don't understand. For example, while I was unable to get listeners for KeyPress events to work if I tried to listen and react to them in within the
val OtherPanel = new BoxPanel(Orientation.Horizontal) { }
portion of the above code, I was able to get a button registered and working like this:

val OtherPanel = new BoxPanel(Orientation.Horizontal) {
  val betLabel = new Label("Bet:")
  val betTextField = new TextField
  val goButton = new Button("Go!")

  listenTo(goButton)
  reactions += {
    case ButtonClicked(b) =>
      betTextField.text = "Go!"
  }

  contents += betLabel
  contents += betTextField
  contents += goButton
}

为什么这个工作,但我尝试做沿着

Why this worked but my attempts to do something along the lines of

val OtherPanel = new BoxPanel(Orientation.Horizontal) {
  val betLabel = new Label("Bet:")
  val betTextField = new TextField
  val goButton = new Button("Go!")

listenTo(betTextField)
reactions += {
  case KeyTyped(betTextField, Enter, _, _) => {
    println("Caught enter")
  }

  contents += betLabel
  contents += betTextField
  contents += goButton
}

没有工作,仍然令人费解我。我假设它应该工作,我只是做了什么。也许这melding这种方法与案例EditDone,而不是案件的keyTyped( 的,_,_)会的工作,但我有点太烧毁,现在跟进的。

didn't work is still baffling me. I'm assuming that it should work and I'm just doing something wrong. Perhaps that melding that approach with a case EditDone instead of a case KeyTyped(,,_,_) would have worked but I'm a little too burnt out right now to follow up on that.

因为我希望有人谁看到这能澄清我还是不明白点我没有接受的答案。应该说不会发生,问题几天我可能会接受@ SOM-snytt的回答仍然没有答案作为他的code是非常有益的。

I haven't accepted an answer yet because I'm hoping that someone who sees this can clarify the points I still don't understand. Should that not happen and the question remain unanswered for a few days I will likely accept @som-snytt's answer as his code was very helpful.

推荐答案

Swing是教育和斯卡拉-Swing是教育。特别是如果课程是摇摆的历史:兴衰

Swing is educational, and Scala-Swing is educational. Especially if the course is "History of Swing: The Rise and Fall."

我的第一个斯卡拉程序也使用摇摆。我忘了细节,但我会分享我可以在源看到的。

My first Scala program also used Swing. I've forgotten the details, but I'll share what I can see in the source.

显然,我有一个主UI组件调用该灯箱一些处理UI事件,同时又有协调调解员组成LightBoxMediator。

Apparently, I had a main UI component called LightBox that handled some UI events, and also a mediator component LightBoxMediator that coordinated.

有趣的部分是,用蛋糕图案的组成和移动业务逻辑(或游戏逻辑)的相互作用成​​介导为适当的UI组件。灯箱发布事件了。

The interesting part would be, using cake pattern for composition, and moving business logic (or game logic) interaction into a component that "mediates" for the UI proper. The LightBox publishes events, too.

所以回答你的问题是:利用出版商的框架,而是从应用程序事件区分UI事件。 (这个小游戏也有基于角色的控制器。)

So the answer to your question would be: exploit the publisher framework, but distinguish UI events from application events. (This little game also had actor-based controllers.)

也许这已经足够说明关注点分离:

Maybe this suffices to illustrate the separation of concerns:

/**
 * Draws the House of Mirrors.
 * The LightBox is just a list of rays (line segments) and gates (various objects).
 * The UI emits requests to move and rotate gates.
 */
class LightBox extends Panel {

  this.peer.addComponentListener(
    new ComponentAdapter {
      override def componentResized(e: ComponentEvent) {
        if (e.getID == ComponentEvent.COMPONENT_RESIZED && e.getComponent == LightBox.this.peer) {
          calculateScale()
        }
      }
    }
  )

  listenTo(mouse.clicks, mouse.moves, mouse.wheel, keys)

  reactions += {
    case KeyPressed(_, Key.N, _, _) => highlightNextMoveableGate()
    case KeyPressed(_, Key.P, _, _) => highlightPreviousMoveableGate()
    case e: MousePressed => startDrag(e)
    case e: MouseDragged => doDrag(e)
    case e: MouseReleased => endDrag(e)
    case e: MouseWheelMoved => wheeling(e)
    case _ => null // println ("Unreacted event")
  }

和中保

trait ViewComponents {
  this: ControllerComponents with ModelComponents =>

  val lightBoxMediator: LightBoxMediator
  val statusBarMediator: StatusBarMediator
  val statusIconMediator: StatusIconMediator
  val applicationMediator: ApplicationMediator

  /**
   * Handles update notifications from the application
   * and user input from the LightBox.
   */
  class LightBoxMediator(val ui: LightBox) extends Reactor with Observing {

    /** Attempt to track our selection across updates: the point is where the gate should end up. */
    private var selectionContinuity: (Option[Gate], Option[Point]) = (None, None)

    listenTo(ui, ui.keys, ui.mouse.clicks)

    reactions += {
      case KeyPressed(_, Key.Q, _, _) => sys.exit()
      case KeyPressed(_, Key.Space, _, _) => rotateSelectedGate()
      case KeyPressed(_, Key.Enter, _, _) => rotateOtherwiseSelectedGate()
      case KeyPressed(_, Key.Up, _, _) => moveUp()
      case KeyPressed(_, Key.Down, _, _) => moveDown()
      case KeyPressed(_, Key.Left, _, _) => moveLeft()
      case KeyPressed(_, Key.Right, _, _) => moveRight()
      case KeyPressed(_, Key.PageUp, _, _) => previousLevel()
      case KeyPressed(_, Key.PageDown, _, _) => nextLevel()
      case DragEvent(from, to) => handleDrag(from, to)
      case ClickEvent(where, button) => handleClick(where, button)
      //case x => println("Unreacted event " + x)
    }

    observe(controller.modelEvents) { e => e match {
        case LevelLoaded(v) => onLevelLoaded(v)
        case TraceResult(s) => onTrace(s)
        case unknown => println("Lightbox mediator ignored: "+ unknown)
      }
      true
    }

只注意到了其他问题。巧合的是,我被清理旧code,实际上是一个很小的应用程序,以抓住从sfgate.com图像(其中停止时,他们改变的部位,当然工作,但通常你可以用鼠标右键单击保存至今),以及我碰巧注意到有关重新订阅以下评论。我依稀记得的UIElement是一个LazyPublisher位,因为我记得头一巴掌。但是,如果我没有写的微薄评论,这些信息就会丢失,以古老的历史。

Just noticed the additional questions. By coincidence, I was cleaning up old code, actually a tiny app to grab images from sfgate.com (which stopped working when they changed the site, of course; but usually you can right-click-save now), and I happened to notice the following comment about resubscribing. I vaguely remember the bit about UIElement being a LazyPublisher, because I remember the head slap. But if I hadn't written the meager comment, that info would have been lost to ancient history.

我觉得有人想支持斯卡拉摆动,并可能会搭头巴掌的照顾。

I think somebody wants to support scala-swing and will probably take care of the head slaps.

package com.maqicode.sfg.jfc

import java.awt.Color
import java.awt.Color.{WHITE => White, RED => Red}
import java.net.{URI, URISyntaxException}
import javax.swing._

import swing.TextField
import swing.event.{EditDone, MouseEntered, ValueChanged}

import com.maqicode.sfg.BadGateURLException
import com.maqicode.sfg.GateUrlTranslator.translate

abstract class URIField extends TextField {

  reactions += {
    case e: EditDone => editDone(e)
    case other: ValueChanged => editing(other)
    case m: MouseEntered => onMouseEntered()
    case _ => null
  }
  // necessary to resubscribe this so that onFirstSubscribe registers ActionListener
  listenTo(this, mouse.moves)

  def onMouseEntered() {
    val t: Option[String] = ClipboardInput.contents
    if (t.isDefined && t.get != this.text) {
      this.text = t.get
      submitURL(t.get)
    }
  }

  def editing(e: ValueChanged) {
    clearError()
  }

  def editDone(e: EditDone) {
    submitURL(this.text)
  }

  def submitURL(s: String) {
    val u = s.trim
    if (!u.isEmpty)
      try {
        submitURI(translate(new URI(u)))
        clearError()
      } catch {
        case t: BadGateURLException => flagError()
        case t: URISyntaxException => flagError()
      }
  }

  def flagError() {
    colorCode(Red)
  }

  def clearError() {
    colorCode(White)
  }

  private def colorCode(c: Color) {
    if (this.background != c) this.background = c
  }

  def submitURI(uri: URI): Unit
}

这篇关于监听器和反应Scala中摇摆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆