Search code examples
tornadofx

TornadoFX: Label not updating


I have an application where I'm transforming an XML document using XSL transformation (XSLT). This is build using TornadoFX (source code can be found here). I'm trying to update a status label with the status of the transformation, which is done inside a class that extends Controller. But for some reason, the status label shows nothing.

The source for my transformer class:

class Transformer : Controller() {
    private val statusProperty = SimpleStringProperty("")
    var status by statusProperty

    fun transform(xml: File, xslt: File, result: StreamResult) {
        runLater { status = "" }

        // create the DOM Source
        val factory = DocumentBuilderFactory.newInstance()
        factory.isNamespaceAware = true
        val builder = factory.newDocumentBuilder()
        val bbcDoc = builder.parse(xml)
        val source = DOMSource(bbcDoc)

        // Create an instance of the TransformerFactory
        val transfomerFactory = TransformerFactory.newInstance()
        val transformer = transfomerFactory.newTransformer(StreamSource(xslt))
                .apply {
                    setOutputProperty(OutputKeys.INDENT, "yes")
                    setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4")
                }
        runLater {
            try {
                transformer.transform(source, result)
                status = "Completed successful"
            } catch (e: Exception) {
                status = e.message
            }
        }
    }
}

The mainscreen class:

class MainScreen : View("XSLT Transformer") {

    val status: TaskStatus by inject()
    val model: TransformerModel by inject()
    val transformer: Transformer by inject()

    private val xmlFilter = arrayOf(FileChooser.ExtensionFilter("XML Filer (*.xml)", "*.xml"))

    private val xsltFilter = arrayOf(FileChooser.ExtensionFilter("XSLT Filer (*.xslt)", "*.xslt"))
    private lateinit var xmlInput: TextField
    private lateinit var xsltInput: TextField

    override val root = form {
        fieldset(labelPosition = Orientation.VERTICAL) {
            field("XSLT fil") {
                ...
            }
            field("XML Input Fil") {
                ...
            }
            button("Konverter") {
                enableWhen(model.valid)
                isDefaultButton = true
                useMaxWidth = true
                action {
                    // An object to hold the results. It can be a file.
//                    val writer = System.out
                    val output = StreamResult(StringWriter())
                    runAsyncWithProgress {
                        try {
                            transformer.status = "Konverterer xml fil..."
                            transformer.transform(File(xmlInput.text), File(xsltInput.text), output)
                            transformer.status = "Færdig"
                        } catch (e: Exception) {
                            transformer.status = e.message
                        }
                    } ui {
                        showDialogResult(output)
                        transformer.status = "Completed"
                    }
                }
            }
        }
        label(transformer.status) {
            style {
                paddingTop = 10
                textFill = Color.RED
                fontWeight = FontWeight.BOLD
            }
        }
    }
}

Solution

  • You must bind to transformer.statusProperty, not to transformer.status. The status property is simply a getter/setter and have no way of updating the label. The statusProperty howevere is an observable, so changes to it will reflect in the label.

    You must also make sure not to perform long running tasks on the UI thread. runLater schedules work to be done on the UI thread. What you want to do is to perform the long running tasks in a runAsync block and add a ui block which receives the result. In the ui block you can update the UI.