修复JavaFX应用程序无法启动多次的问题

问题描述 投票:0回答:1

我正在使用 Kotlin 和 Furhat 构建一个对话代理。代理向用户询问问题,如果输出不是用户所说的内容,我会显示一个 GUI,用户可以在其中输入正确的文本。我正在使用 javafx 创建 GUI。

var guiText = ""

class MyGUI: Application() {
    private lateinit var textField: TextField
    override fun start(primaryStage: Stage) {

        primaryStage.title = "User input"
        textField = TextField()

        val submitButton = Button("Submit")
        submitButton.setOnAction {
            guiText = textField.text
            Platform.exit()
        }

        val vbox = VBox(10.0)
        vbox.children.addAll(textField, submitButton)

        val scene = Scene(vbox, 300.0, 100.0)
        primaryStage.scene = scene

        primaryStage.show()
    }
}

这是我创建的 GUI,它由一个文本框和一个提交按钮组成。我最初在“开始”状态下调用它,看起来像这样

val Start : State = state(Parent) {
    onEntry {
        furhat.ask("Hello, what is your name?")
    }
    
    onResponse {
        var userName = callPythonScript(it.text, "extractName")
        if (userName == "Sorry I could not catch that. Perhaps you can type it down for me..."){
            Application.launch(MyGUI::class.java)
            println("Output from GUI : $guiText")
            userName = guiText
        } else {
            val isNameCorrect = furhat.askYN("If I heard that right, your name is $userName")
            if (!isNameCorrect){
                furhat.say("I am sorry I misunderstood, can you write it down for me?")
                Application.launch(MyGUI::class.java)
                println("Output from GUI : $guiText")
                userName = guiText
            }
        }

在下一个状态,我想获取用户最喜欢的艺术家

val LikesArtists : State = state(Parent) {
    onEntry {
        furhat.ask("What are some of the artists you like?")
    }

    onResponse {
        var artistName = callPythonScript(it.text, "extractPreferences", "likes", "f")
        // Error handling
        if (artistName == "No entity found sorry!"){
            Application.launch(MyGUI::class.java)
            println("Output from GUI : $guiText")
            artistName = guiText
        } else {
            val isArtistNameCorrect = furhat.askYN("If I heard that right, the artist you just said is $artistName correct?")
            if (!isArtistNameCorrect){
                furhat.say("I am sorry I misunderstood, can you write it down for me?")
                Application.launch(MyGUI::class.java)
                println("Output from GUI : $guiText")
                artistName = guiText
            }
        }

在这里,当我再次调用 Application.launch 时,它给出一个错误,指出 应用程序启动不能被调用多次。有没有什么方法可以在按下提交按钮时隐藏 GUI,而不是 Application.launch,而是在 Kotlin 代码中显示 GUI?

java kotlin javafx
1个回答
0
投票

每个 JVM 实例只能启动一次 JavaFX 框架。一旦退出,您就必须重新启动整个应用程序才能再次启动 JavaFX。否则,尝试再次启动框架(或尝试在框架已经运行时启动它)将导致异常。

默认情况下,一旦最后一个窗口关闭,JavaFX 将退出。您可以通过将

false
传递给
Platform::setImplicitExit
来更改此行为。但请注意,这意味着您现在负责关闭框架,方法是关闭 JVM 或调用
Platform::exit

如前所述,JavaFX 每个 JVM 实例只能启动一次。启动JavaFX主要有两种方式:

Application::launch
Platform::startup
。请注意,前者将阻塞调用线程直到框架退出,而后者则不会。鉴于您的代码和既定目标,我建议使用后者。无论如何,这两个方法只能被调用一次。启动 JavaFX 后,其他线程与 JavaFX 的进一步交互必须通过
Platform::runLater
完成。不过,由于您使用的是 Kotlin,因此使用协程可以使这变得更容易。

这是一个小例子(没有 Furhat):

import kotlin.coroutines.*
import kotlinx.coroutines.*
import kotlinx.coroutines.javafx.*
import java.util.Scanner
import javafx.application.Platform
import javafx.scene.control.TextInputDialog

/**
 * Starts the JavaFX framework, returning once the startup is complete. An
 * `IllegalStateException` will be thrown if JavaFX has already been started.
 */
suspend fun startJavaFX(): Unit = suspendCoroutine { cont ->
    Platform.startup {
        Platform.setImplicitExit(false)
        cont.resume(Unit)
    }
}

/**
 * Displays a JavaFX `TextInputDialog` and returns the user's input, or
 * `null` if user cancels the dialog.
 */
suspend fun getUserInput(): String? = withContext(Dispatchers.JavaFx) {
    val dialog = TextInputDialog().apply {
        title = "User Input"
        headerText = null
        contentText = "Enter a message:"
    }
    dialog.showAndWait().orElse(null)
}

fun printOptions() {
    println("Options")
    println("-------------------")
    println("    1) Show JavaFX dialog")
    println("    2) Exit")
    println()
    print("Enter your choice: ")
}

fun main(): Unit = runBlocking {
    val scanner = Scanner(System.`in`)
    startJavaFX()
    while (true) {
        printOptions()
        when (scanner.nextLine().toInt()) {
            1 -> getUserInput()?.run { println("You entered: '$this'") }
            2 -> break
            else -> println("Please enter either '1' or '2'.")
        }
        println()
    }
    Platform.exit()
}

话虽如此,我对 Furhat 不熟悉,所以我不知道它的用途或工作原理。但像上面的示例那样将 CLI 与 GUI 混合使用并不典型。您应该选择其中之一。

© www.soinside.com 2019 - 2024. All rights reserved.