我正在使用 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?
每个 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 混合使用并不典型。您应该选择其中之一。