监听器和反应Scala中摇摆 [英] Listeners and reactions in Scala Swing
问题描述
我已经做了搜索,并在日食一些试验和错误的公平一点,但似乎在我的听众和使用的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.Horizontal){}结果
上述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屋!