Scala Swing 中的听众和反应

2023-12-03

我在 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 部分的末尾?
这还重要吗?

EDIT
我已经取得了显着的进步,并且掌握了许多我需要的东西,并且对我以前没有得到的概念有了更好的理解。

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!
    }
  }

需要澄清 Scala 文字标识符(反引号)

所以看来我的问题的答案是listenTo和reactions块属于MainFrame块,但应该出现在它的contents += { //contents }块之后。

Eclipse 中的额外试验和错误表明,虽然这个解决方案对我有用,但显然还有更多我不明白的地方。例如,虽然我无法让 KeyPress 事件的监听器正常工作,但如果我尝试在
val OtherPanel = new BoxPanel(Orientation.Horizo​​ntal) { }
上面代码的一部分,我能够注册一个按钮并像这样工作:

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 的回答,因为他的代码非常有帮助。


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 是一个 LazyPublisher,因为我记得那一巴掌。但如果我没有写下这条微不足道的评论,这些信息就会消失在古代历史中。

我认为有人想要支持 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
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala Swing 中的听众和反应 的相关文章

  • 通过 SSH 的 sbt (Scala) 结果找不到命令,但如果我自己这样做就可以工作

    所以我正在尝试做一些涉及跑步的事情sbt通过 SSH 命令 这就是我正在尝试的 ssh my username
  • Slick 中的 Scala 枚举(案例对象),良好实践

    假设我有一个代表一组几个有效状态的特征 将对象存储在数据库中是一个好习惯吗 存储 Int 并使用隐式函数 MappedColumnType base Int DoorState 将它们映射到 DoorState 会更好吗 trait Doo
  • 使用 lift-json 反序列化具有 Map[String,Any] 属性的案例类

    几天来我一直在努力解决一些通过 lift json 应该很简单的事情 将映射序列化为 JSON 我知道 我知道 根对象还不能是 List 或 Map 但我愿意暂时包装在一个案例类中 但我仍然无法让它工作 感谢一些堆栈溢出帮助 我已经可以进行
  • 如何使 JTable 中的列对于 Swing Java 不可见

    我设计了一种 GUI 其中使用了一个 JTable 我必须从中隐藏 2 列 我该怎么做呢 去除TableColumn来自TableColumnModel TableColumnModel tcm table getColumnModel t
  • 摆动刷新周期

    我试图了解何时使用重新验证 重绘 打包 令人惊讶的是 我没有找到详细的底层文档 请随意链接 到目前为止我已经明白这都是 RepaintManager 的责任 油漆 重新油漆指的是脏 干净的东西 pack validate revalidat
  • Xamarin Forms:单击重新启动按钮时网格按钮 UI 中断

    我正在使用一个button在 的里面grid用于显示字母以实施Word search game 最初 用户界面看起来不错 但是当单击play again按钮 UI 中断 截屏 网格内设置按钮的代码 void SetGridLayout ch
  • 如何使用 *non-case* 类通过 Scala 从 JSON 反序列化?

    我正在编写一个 Scala 应用程序 需要序列化和反序列化 JSON 某些 JSON 对象具有超过 22 个字段 因此我无法使用案例类 并且也无法更改格式 我找到的所有 Scala JSON 库都只能 轻松地 与案例类一起使用 而不是与普通
  • 具有定期更新的静态数据集的结构化流

    将流媒体与静态数据集合并是结构化流媒体的一个重要功能 但在每个批次中 数据集都会从数据源刷新 由于这些源并不总是那么动态 因此在指定的时间段 或批次数 内缓存静态数据集会提高性能 在指定的时间段 批次数之后 将从源重新加载数据集 否则从缓存
  • 在 JTable 中使用自定义 TablecellRenderer

    我是 Java 新手 我创建了一个JTable 就是这样addRow当我尝试向表中添加一行时 该方法有效 private void addTableRow String type String name String rank String
  • Spark JSON 文本字段到 RDD

    我有一个 cassandra 表 其中有一个名为 snapshot 的文本类型字段 其中包含 JSON 对象 identifier timestamp snapshot 我了解到 为了能够使用 Spark 对该字段进行转换 我需要将该 RD
  • 在 Traversable 视图上执行 FoldLeft 时,Scala 中出现类型方差错误

    我正在尝试连接一系列TraversableScala 中的视图使用foldLeft运算符并遇到我不明白的类型差异错误 我可以用reduce连接列表Traversable像这样的观点 val xs List 1 2 3 4 map Trave
  • Scala:不变性和路径依赖的类型兼容性

    我围绕这个主题提出了一些问题 但这次我想让它成为一个更一般性的讨论 因为在我看来 Scala 缺少一些非常重要的块 考虑以下代码 从我的真实项目中简化 trait World type State lt StateIntf def evol
  • SBT - 使用汇编时多项目合并策略和构建 sbt 结构

    我有一个由多个较小项目组成的项目 其中一些项目相互依赖 例如 有一个依赖于 commons 项目的实用程序项目 其他项目可能依赖于公用事业或公共设施 也可能不依赖于两者 在 build sbt 中 我在文件末尾有程序集合并策略 以及程序集中
  • 如何用 Java 制作 Windows 7 工具提示

    我一直在网上到处寻找 但没有找到这个小问题的答案 在 Windows 7 中 我认为在 Vista 中 您有一个漂亮的圆形银色工具提示 它看起来比旧的黄色盒装蹩脚工具提示要好得多 下面的 How do I make a Windows 7
  • 将括号子集映射到字符

    我正在尝试创建一个 Scala 方法 该方法将采用一个父括号组 表示为字符串 然后将每个括号子组映射到不同的字母 然后它应该将它们放入它返回的映射中 所以基本上我调用以下方法 如下所示 val s 2 x 3 6 val map mapPa
  • 这是一种在 Akka FSM 中内部监视到第一个状态的转换的方法吗?

    考虑从状态 Idle 开始的 FSM actor startWith Idle IdleData 我想监视到第一个状态的转换 从无状态 I tried onTransition case gt Idle gt Wasn t called 根
  • scalac 编译生成“对象 apache 不是包 org 的成员”

    我的代码是 import org apache spark SparkContext 它可以在交互模式下运行 但是当我使用 scalac 编译它时 出现以下错误消息 对象 apache 不是包 org 的成员 这似乎是路径的问题 但我不知道
  • Android - 带图像的按钮 - 禁用按钮时图像变暗

    在 Android 中 我有一个包含图像和文本的按钮 禁用该按钮时 文本会自动变灰 但图像保持不变 当按钮被禁用时是否可以使图像变暗而不需要两个单独的图像 在您的代码中 您还可以使用彩色滤光片 http developer android
  • 如何对 RDD 进行分区

    我有一个文本文件 其中包含大量由空格分隔的随机浮动值 我正在将此文件加载到 scala 中的 RDD 中 这个RDD是如何分区的 另外 是否有任何方法可以生成自定义分区 以便所有分区都具有相同数量的元素以及每个分区的索引 val dRDD
  • 隐式类中的 Scala 按名称调用构造函数参数

    下面的代码不编译 期望的是在隐式类中有一个按名称调用构造函数参数 如下所示 def f n Int 1 to n product implicit class RichElapsed A val f gt A extends AnyVal

随机推荐

  • Linux 中的 java.lang.UnsatisfiedLinkError

    我已经成功进入 Linux 机器来尝试中建议的 HotKey 库这个答案 我已经编译了示例代码 现在运行该程序并收到以下消息 oracle machine jxgrabkey 0 2 1 i386 java classpath lib JX
  • 在 Mercurial 中合并时排除文件

    我将 Mercurial 与 TortoiseHg 一起使用 我有两个分支 A and B 有两个文件 toto cs and titi cs 当我想合并时有办法吗B with A 排除titi cs不合并 并且仅合并toto cs 如果可
  • 为什么使用await后控制台仍然退出?

    using System using System Threading using System Threading Tasks namespace application public class Files public static
  • L5.6 - 数据透视表上的关系

    我有一个关系pivot桌子 我怎样才能扩展它 例如 shops id name products id name 产品商店 产品编号 shop id field 1 field 2 field 3 表A id table A id name
  • WebSphere Message Broker - 如何发送 PCF 消息

    我们需要从 MB 流发出一些 MQ 命令 解决方法是发送 PCF 命令 但我不知道如何创建它 有什么指点吗 塞巴斯蒂安 要通过 PCF 消息向 MQ 队列管理器发出命令 您可以查看 nix 上或安装 MQ 的位置的 opt mqm samp
  • 如何删除 Postgres 中两行重复数据之一?

    我正在使用 Postgres 9 5 我有以下查询 旨在查找表中相同的数据行 但具有唯一的 ID select e name e day e distance e created at e2 created at from events e
  • 在应用程序退出时保存变量

    我有一个简单的 iPhone 应用程序 具有多个视图和 5 个不同项目的列表 这些项目附加了变量 无论用户在应用程序中的哪个位置 这些变量都会传递到每个新视图 Example var Peron1 String var Person1Age
  • 没有数据库/框架的Asp.net mvc模型

    是否有关于如何创建 ASP NET MVC 应用程序而不由数据库管理模型 通过 linq2sql 或实体框架 的教程 示例 我必须为具有基于 json 的 api 的服务器创建一个前端 我想使用 mvc 3 或 2 并且保留 mvc 的大部
  • 如何重建全部?

    在 Visual Studio 中 如何重建完整的解决方案 包括所有配置 如果我选择 重建解决方案 它总是仅重建 调试 或 仅发布 但绝不会同时重建两者 使用批量构建选项 右键单击解决方案即可查看它 这是一个屏幕截图 VS2010 但我相信
  • [Code] 部分中的数组变量和动态访问

    我的安装程序有Components与可下载文件相关联 这些东西在不同的构建中都在变化 所以我正在使用 insert来创建 Components 部分以及相应的条目 Files 部分 其中一些组件依赖于常见的可下载文件 现在 为了在下载页面中
  • 自调用onUpgrade方法

    我怎样才能打电话给onUpgrade数据库的方法 我的问题背景 我在 SD 卡上做了完整数据库的备份 用户可以恢复该数据库 但是 如果与此同时 在备份和恢复之间 应用程序更新更改了数据库 我将在恢复时遇到问题 恢复后新列不存在 所以我想打电
  • 当字符串中存在重复字母时获取匹配项

    我在谷歌表格中有一个输入列表 Input Desired Output To demonstrate only not an input The repeated letters Outdoors Match o dog No Match
  • Glassfish 3 - 从静态服务器加载图像

    我正在尝试从部署到的 Web 应用程序外部的服务器加载图像 和其他静态内容 玻璃鱼 v3 我有以下配置web xml但它不适用于 Glassfish 但适用于 Tomcat
  • 在 C# 中调整图像大小并将其发送到 OpenCV 会导致图像扭曲

    这是一个相关的后续问题this one 基本上 我有一个 DLL 它使用 OpenCV 进行图像处理 有两种方法 一种是接受image Path 另一个接受cv Mat 与我们一起工作的人image path工作正常 接受一个image是有
  • 在Javascript中声明一个空的二维数组?

    我想在 Javascript 中创建一个二维数组 用于存储坐标 x y 我还不知道我将拥有多少对坐标 因为它们将由用户输入动态生成 预定义二维数组的示例 var Arr 1 2 3 4 5 6 我想我可以使用 PUSH 方法在数组末尾添加一
  • 如何强制回调方法等待 Google 标记创建

    我正在使用 Google 地图库制作一个应用程序 我的问题是创建标记时出现无法解释的延迟 或者出现我看不到的异步问题 解释 该代码沿着起点和终点之间的路线获取充电站位置 为获取返回的每个充电站创建 Google 标记 以 Json 格式 并
  • 调度程序 xps 内存泄漏

    我使用 com 互操作从 vb6 应用程序调用 net 4 0 dll 在 net中 我通过xaml固定文档创建一个xps文档并将其保存到磁盘 这会导致内存泄漏 我在这里找到了一个很好的解决方案 将固定文档保存到 XPS 文件会导致内存泄漏
  • RadioGroup 无法正常工作

    我想给用户四个选择 第一和第二个选择在一行上 第三个和第四个选择在另一行上 我的问题是 当应用程序启动时 我可以选择多个选项 但我不希望这样 这是我的 xml 布局
  • 如何从查找“type d”中排除此/当前/点文件夹

    find type d 可用于查找某个起点以下的所有目录 但它返回当前目录 也是如此 这可能是不受欢迎的 怎样才能排除呢 不仅是递归深度find可以通过控制 maxdepth参数 深度也可以从 top 开始限制 使用相应的 mindepth
  • Scala Swing 中的听众和反应

    我在 Eclipse 中做了相当多的搜索和一些试验和错误 但在使用 Swing 在 Scala 中编写 GUI 时 我对侦听器和反应的理解似乎存在差距 每个侦听器是否都会获得一个反应块 或者我是否在可能生成事件的所有组件上注册侦听器 并使用