Reputation: 2282
I'm building an application with TornadoFX 1.7.11 using Kotlin 1.1.51 on JDK 8u121.
I'm trying to execute a long-running task in a separate thread and show progress in the UI using a progressbar and a label. Strangely, the label isn't updating. I thought that maybe I somehow ran the task on UI thread and it's stuck, but the progressbar works and UI is otherwise responsive (controls work etc.).
I've also tried to manually edit the label with ScenicView and it worked. I'm out of ideas, can you guys help?
Here are somewhat simplified code snippets:
MainView.ktclass 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)
}
}
}
}
}
Upvotes: 1
Views: 396
Reputation: 7297
Your code isn't runnable, but I created a minimal sample based on these concepts, using a more idiomatic approach.
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)
}
}
}
}
}
This can also be done with less fanfare, but I don't know the complexity or requirements of your app, so I changed it as little as possible :)
Upvotes: 3