我在eclipse中做了一些搜索和一些试验和错误,但是当我使用Swing在Scala中编写GUI时,我对听众和反应的理解似乎存在差距。
每个侦听器是否都获得了一个反应块,或者我是否在所有可能生成事件的组件上注册了侦听器,并对带有case语句的大型反应块中的每个组件做出反应?
听众和反应块究竟属于哪里。
这是我的GUI代码的缩写版本:
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中的文本字段做出反应 我是否尽可能地将听众和反应块放在我感兴趣的元素附近,或者在MainFrame部分的末尾放置一大块听众和反应? 它甚至重要吗?
修改
我已经取得了重大进展,并且拥有了我需要的大部分工作,并且更好地理解了我以前没有得过的概念。
摘自Odersky的“Scala编程”对我的帮助最大。具体来说,这个页面的例子是:
http://www.artima.com/pins1ed/gui-programming.html
代码来自文本的第一版,所以我怀疑Scala 2.9中是否有更好的方法,但很清楚简洁并总结了我误解的内容。
从一个简单的华氏温度到摄氏温度转换的例子,我开始明白监听器和反应块属于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!
}
}
Need clarification on Scala literal identifiers (backticks)
所以似乎我的问题的答案是listenTo和react块属于MainFrame块,但应该出现在它的内容+ = {// contents}块之后。
eclipse中的其他试验和错误表明,虽然这个解决方案适用于我,但显然还有更多我不理解的内容。例如,如果我试图在内部听取并对它们做出反应,我无法让KeyPress事件的监听器工作
val OtherPanel = new BoxPanel(Orientation.Horizontal){}
上面代码的一部分,我能够得到一个注册按钮并按此工作:
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
}
为什么这有效,但我尝试按照
的方式做一些事情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
}
没有工作仍然令我困惑。我假设它应该工作,我只是做错了。也许用一个案例EditDone而不是一个案例KeyTyped(,,,)来融合这种方法会有效但我现在有点太过于无法跟进在那。
我还没有接受答案,因为我希望看到这个的人可以澄清我仍然不理解的观点。如果这种情况没有发生且问题仍未得到解决,我可能会接受@ som-snytt的回答,因为他的代码非常有用。
答案 0 :(得分:4)
Swing很有教育意义,Scala-Swing很有教育意义。特别是如果课程是“摇摆历史:兴衰”。
我的第一个Scala程序也使用了Swing。我已经忘记了细节,但我会分享我在源头上看到的内容。
显然,我有一个名为LightBox的主UI组件处理一些UI事件,还有一个协调的中介组件LightBoxMediator。
有趣的部分是,使用蛋糕模式进行合成,并将业务逻辑(或游戏逻辑)交互转移到一个“适当”调整UI的组件中。 LightBox也发布了事件。
因此,您的问题的答案将是:利用发布者框架,但将UI事件与应用程序事件区分开来。 (这个小游戏也有基于演员的控制器。)
也许这足以说明关注点的分离:
/**
* 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
}
刚刚注意到其他问题。巧合的是,我正在清理旧代码,实际上是一个小应用程序从sfgate.com获取图像(当然,当他们更改网站时停止工作;但通常你可以右击 - 现在保存),我碰巧请注意以下关于重新订阅的评论。我依旧记得关于UIElement是一个懒惰的发布者的一点,因为我记得头脑一巴掌。但如果我没有写出微薄的评论,那么这些信息就会在古代历史中丢失。
我认为有人想要支持scala-swing并且可能会照顾头部。
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
}