即使 UI 话题似乎有响应,标签文本也不会更新

Label text isn't updating even though UI thread seems to be responsive

我正在 JDK 8u121 上使用 Kotlin 1.1.51 使用 TornadoFX 1.7.11 构建应用程序。

我正在尝试在单独的线程中执行一个长 运行 任务,并使用进度条和标签在 UI 中显示进度。 St运行gely,标签没有更新。我想也许我以某种方式 运行 UI 线程上的任务并且它被卡住了,但是进度条工作并且 UI 在其他方面是响应的(控制工作等)。

我也尝试过使用 ScenicView 手动编辑标签并且成功了。我没主意了,你们能帮忙吗?

这里有一些简化的代码片段:

MainView.kt
class MainView : View("") {
    private val controller: MainController by inject()

    override val root = borderpane {
        bottom(TasksView::class)
    }

    init {
        controller.reloadTranslations().completed.onChange {
            // do some lightweight UI stuff
        }
    }
}
MainController.kt
class MainController : Controller() {
    private val tasksController: TasksController by inject()

    fun reloadTranslations(): TaskStatus {
        val task = TaskStatus()
        tasksController.tasks.add(task)
        runAsync(task) {
            updateMessage(messages["loadingTranslations"])
            BibxCache.rebuild().subscribe {
                updateMessage(messages["loadingTranslations"] + " " + it.loaded)  // for debugging
                updateProgress(it.loaded.toLong(), it.total.toLong())
            }
        }
        return task
    }

    fun getTranslations() = BibxCache.values.toSortedSet()
}
TasksView.kt
class TasksView : View() {
    override val root = vbox()

    val controller: TasksController by inject()

    init {
        controller.tasks.onChange {
            root.clear()
            controller.tasks.map { TaskRow(it) }.forEach { root.add(it) }
        }
    }
}

class TaskRow(task: TaskStatus) : HBox() {
    init {
        val progressBar = ProgressBar(task.progress.get())
        progressBar.bind(task.progress)
        val label = Label(task.message.get())
        label.bind(task.message)
        task.message.onChange { println(it) }  // for debugging
        children.addAll(
                progressBar,
                Label(task.message.get())
        )
    }
}
TasksController.kt
class TasksController : Controller() {
    val tasks: ObservableList<TaskStatus> = FXCollections.observableArrayList()

    init {
        tasks.onChange { change ->
            change.next()
            change.addedSubList.forEach { added ->
                added.completed.onChange {
                    tasks.remove(added)
                }
            }
        }
    }
}

您的代码不可运行,但我基于这些概念使用更惯用的方法创建了一个最小示例。

class MainView : View("Tasks") {
    private val mainController: MainController by inject()

    override val root = borderpane {
        setPrefSize(600.0, 400.0)
        top {
            button("Start task").action {
                mainController.reloadTranslations()
            }
        }
        bottom(TasksView::class)
    }
}

class MainController : Controller() {
    private val tasksController: TasksController by inject()

    fun reloadTranslations(): TaskStatus {
        val task = TaskStatus()
        tasksController.tasks.add(task)
        runAsync(task) {
            updateMessage(messages["loadingTranslations"] + " $this...")
            Thread.sleep(Random().nextInt(2000).toLong())
            updateMessage(messages["loadingTranslations"] + " $this - half way")
            updateProgress(50.0, 100.0)
            Thread.sleep(Random().nextInt(2000).toLong())
        }
        return task
    }
}

class TasksView : View() {
    val controller: TasksController by inject()

    override val root = vbox {
        bindChildren(controller.tasks) { task ->
            hbox {
                progressbar(task.progress)
                label(task.message)
            }
        }
    }
}

class TasksController : Controller() {
    val tasks: ObservableList<TaskStatus> = FXCollections.observableArrayList()

    init {
        tasks.onChange { change ->
            change.next()
            change.addedSubList.forEach { added ->
                added.completed.onChangeOnce {
                    tasks.remove(added)
                }
            }
        }
    }
}

这也可以不那么大张旗鼓地完成,但我不知道你的应用程序的复杂性或要求,所以我尽可能少地改变它:)