gpt4 book ai didi

ScalaFX - 如何使用方法获取场景的标题

转载 作者:行者123 更新时间:2023-12-03 23:47:01 31 4
gpt4 key购买 nike

我正在使用 ScalaFX 并尝试了解它是如何工作的。作为一个实验(不是我将在生产中做的),我想要一种获取窗口标题的方法。

所以这是我的 Graph.scala 文件:

package graphing

import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.paint.Color

class Graph {
val app = new JFXApp {
stage = new JFXApp.PrimaryStage {
title = "First GUI"
scene = new Scene {
fill = Color.Coral
}
}
}

def getTitle() = {
app.stage.getTitle
}

def generateChart(args: Array[String]) = app.main(args)
}

这是我使用此 Graph 类的驱动程序对象:
package graphing

import graphing.Graph

object Driver extends App {
val graph = new Graph

println(graph.getTitle())

graph.generateChart(args)
}

但是,由于线路原因,这不起作用

println(graph.getTitle())



抛出的错误:
enter image description here

有人可以解释发生了什么以及我如何在这里实现我的目标吗?

最佳答案

这里的问题涉及JavaFX(以及ScalaFX)初始化。

初始化 JavaFX 是一项复杂的业务。 (事实上​​,我最近才知道它比我最初认为的还要复杂。有关更多背景信息,请参阅 StackOverflow 上的 this recent answer。幸运的是,您的问题更容易解决。)

ScalaFX 极大地简化了 JavaFX 初始化,但需要 JFXApp trait 被用作 object 定义的一部分.
JFXApp包含 main方法,它必须是你的应用程序的起点;正是这种方法为您处理了初始化 JavaFX 的复杂性。

在您的示例中,您有 Driver对象扩展 scala.App , 所以它是 App的(因此,Driver 的)main方法成为您自己的应用程序的起点。这对于常规命令行界面 (CLI) 应用程序来说很好,但如果没有大量额外的复杂性,它就不能与 ScalaFX/JavaFX 应用程序一起使用。

在您的代码中,JFXAppmain方法永远不会执行,因为它被定义为类成员,它不是 main Scala 的方法 object ,因此不是 JVM 自动执行的候选对象。您确实可以从 Graph.generateChart() 手动调用它。方法,但是直到您尝试获取场景的标题之后才调用该方法本身,因此作为舞台的 NPE 尚未初始化。

如果你把 graph.generateChart(args)println(graph.getTitle()) 之前调用陈述?那会修复它吗?可悲的是没有。

这就是为什么...
JFXApp还执行了另一项魔术:它执行其object 的构造代码。 (以及由该对象扩展的任何其他 class es,但不适用于扩展的 trait s)在 JavaFX 应用程序线程 (JAT) 上。这很重要:只有在 JAT 上执行的代码才能直接与 JavaFX 交互(即使通过 ScalaFX)。如果您尝试在任何其他线程(包括应用程序的主线程)上执行 JavaFX 操作,则会出现异常。

(这个魔法依赖于一个已弃用的 Scala 特征 scala.DelayedInit ,它已从 Scala 3.0 的库中删除,即 Dotty,因此 future 将需要不同的机制。但是,对于该特征,值得一读 the documentation了解更多背景信息。)

所以,当 Driver的构造代码调用graph.generateChart(args) ,它会导致 JavaFX 被初始化,启动 JAT,并执行 Graph的施工规范就可以了。然而,到了 Driver的构造函数调用 println(graph.getTitle()) ,它仍然在主线程上执行,有两个问题:

  • Graph的构造代码可能已执行,也可能未执行,因为它是在不同的线程上执行的。 (这个问题被称为竞态条件,因为在试图调用 println(graph.getTitle()) 的主线程和试图初始化 graph 实例的 JAT 之间存在竞态。)在某些情况下你可能会赢得比赛,但你是也会经常输。
  • 您正在尝试从主线程而不是 JAT 与 JavaFX 交互。

  • 这是您的应用程序工作的推荐方法:

    package graphing

    import scalafx.application.JFXApp
    import scalafx.scene.Scene
    import scalafx.scene.paint.Color

    object GraphDriver
    extends JFXApp {

    // This executes at program startup, automatically, on the JAT.
    stage = new JFXApp.PrimaryStage {
    title = "First GUI"
    scene = new Scene {
    fill = Color.Coral
    }
    }

    // Print the title. Works, because we're executing on the JAT. If we're NOT on the JAT,
    // Then getTitle() would need to be called via scalafx.application.Platform.runLater().
    println(getTitle())

    // Retrieve the title of the stage. Should equal "First GUI".
    //
    // It's guaranteed that "stage" will be initialized and valid when called.
    def getTitle() = stage.title.value
    }

    请注意,我已经结合了您的 Graph类和 Driver对象合并为单个对象, GraphDriver .虽然我不确定您的应用程序在架构上需要什么样的外观,但这对您来说应该是一个不错的起点。

    另请注意 scala.App根本不使用。

    打电话时要小心 GraphDriver.getTitle() :此代码需要在 JAT 上执行。执行任何可能在不同线程上运行的代码的标准解决方法是将其按名称传递给 scalafx.application.Platform.runLater()。 .例如:

    import scalafx.application.Platform

    // ...
    Platform.runLater(println(ObjectDriver.getTitle()))
    // ...

    关于ScalaFX - 如何使用方法获取场景的标题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62109858/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com