Текст метки не обновляется, хотя поток пользовательского интерфейса кажется отзывчивым

Я создаю приложение с TornadoFX 1.7.11, используя Kotlin 1.1.51 на JDK 8u121.

Я пытаюсь выполнить длительную задачу в отдельном потоке и показать прогресс в пользовательском интерфейсе, используя индикатор выполнения и метку. Странно, метка не обновляется. Я подумал, что, может быть, я каким-то образом запустил задачу в потоке пользовательского интерфейса, и она застряла, но индикатор выполнения работает, а в остальном пользовательский интерфейс реагирует (управляет работой и т. д.).

Снимок экрана с заполненным наполовину индикатором выполнения и сообщениями консоли, показывающими, что сообщение обновлено. Красная стрелка указывает на место, где должна быть метка, но там не видно текста.

Я также пытался вручную отредактировать метку с помощью 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)
                }
            }
        }
    }
}

person gronostaj    schedule 23.10.2017    source источник


Ответы (1)


Ваш код не работает, но я создал минимальный образец на основе этих концепций, используя более идиоматический подход.

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)
                }
            }
        }
    }
}

Это также можно сделать с меньшей помпой, но я не знаю сложности или требований вашего приложения, поэтому я изменил его как можно меньше :)

person Edvin Syse    schedule 23.10.2017
comment
Благодарю вас! Мне нравится ваш подход, он лаконичен, удобочитаем и работает так, как ожидалось. - person gronostaj; 23.10.2017
comment
Рад это слышать :)) - person Edvin Syse; 24.10.2017