BusyWorker helps running UI tasks a separate threads (other than the JavaFX Application thread).
BusyWorker helps running UI tasks a separate threads (other than the JavaFX Application thread).
It will show busy cursor and disable specified nodes while task is performed.
It gives an option to show progress and status messages.
BusyWorker
run tasks and takes care of handling handling exceptions and displaying error dialogs.
There is also option to perform custom finish actions after task is completed.
While task is performed property busy
is set to true.
Only one task, for a given worker, can be run at the time.
When a task in being performed busyDisabledNode
will be disabled and its cursor will be set to Wait
/Busy
cursor.
Progress and messages from the running task can be monitored using progressValue
and progressMessage
properties.
Below is an example of using using BusyWorker that updates a progress message and progress indicator.
The full example can be found in the BusyWorkerDemo
of the ScalaFX Extras Demo project.
val buttonPane: Pane = ... val progressLabel: Label = ... val progressBar: ProgressBar = ... val busyWorker = new BusyWorker("BusyWorker Demo", buttonPane) { progressLabel.text <== progressMessage progressBar.progress <== progressValue } val button = new Button("Click Me") { onAction = () => busyWorker.doTask("Task 1")( new SimpleTask[String] { override def call(): String = { val maxItems = 10 for (i <- 1 to maxItems) { println(i) message() = s"Processing item $i/$maxItems" progress() = (i - 1) / 10.0 Thread.sleep(250) } progress() = 1 "Done" } } ) }
Mixin that adds ability to easily show message dialogs.
Mixin that adds ability to easily show message dialogs. A messageLogger can be provided, so when the error or warning dialogs are shown, they are also logged.
A ShowMessage mixin will typically be used with the ModelFX.
Logging operations used by ShowMessage.
Logging operations used by ShowMessage. Enables ShowMessage to use loggers preferred by the user.
Attempt to initialize JavaFX Toolkit.
Attempt to initialize JavaFX Toolkit. This is only needed when application is not
started by JFXApp
or JavaFX Application
.
When JavaFX toolkit is not initialized and you attempt to use JavaFX components you will get exception:
java.lang.IllegalStateException: Toolkit not initialized
.
In JavaFX 9 and newer you can use Platform.startup(() -> {})
.
Package mvcfx
helps in implementation of Model-View-Controller-like patters, we call it MVCfx.
Package mvcfx
helps in implementation of Model-View-Controller-like patters, we call it MVCfx.
The pattern is build around use of views defined in FXML (the view), with binding to ScalaFX using ScalaFXML library.
There are two cooperating classes ControllerFX
for binding FXML to Scala code
and ModelFX
that contains logic for the component.
An additional helper MVCfx
class is provided to instantiate the ControllerFX
and the corresponding ModelFX
.
The structure of the UI component is defined in a standard JavaFX FXML file.
The Scala side of the FXML is in a class ControllerFX
.
Part of the ControllerFX
is automatically generated by ScalaFXML macro, the rest can be customized as needed,
including binding of the UI to appropriate parts of the component logic represented by the ModelFX
.
Use of the MVCfx
is optional and primarily intended to simplify instantiation of the ControllerFX
and the ModelFX
.
Below is an example using the classes in mvcfx
. The code implements a simple Stop Watch.
The complete code is in demos part of the ScalaFX Extras project.
Note the recommended naming convention used: * StopWatch extends MVCfx * StopWatchModel extends ModelFX * StopWatchController extends ControllerFX * StopWatch.fxml the FXVM declaration of the view
First we have the FXML definitions representing the structure of the user interface
(org/scalafx/extras/mvcfx/stopwatch/StopWatch.fxml
)
<?import javafx.geometry.Insets?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.text.*?> <BorderPane xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.scalafx.extras.mvcfx.stopwatch.StopWatchController"> <bottom> <ButtonBar BorderPane.alignment="CENTER"> <buttons> <Button fx:id="startButton" mnemonicParsing="false" text="Start"/> <Button fx:id="stopButton" mnemonicParsing="false" text="Stop"/> <Button fx:id="resetButton" mnemonicParsing="false" text="Reset"/> </buttons> </ButtonBar> </bottom> <center> <HBox> <children> <Label fx:id="minutesLabel" alignment="CENTER" contentDisplay="CENTER" text="99" textAlignment="CENTER" BorderPane.alignment="CENTER"> </Label> <Label text=":"> </Label> <Label fx:id="secondsLabel" alignment="CENTER" contentDisplay="CENTER" text="99" textAlignment="CENTER"> </Label> <Label text="."> </Label> <Label fx:id="fractionLabel" alignment="CENTER" contentDisplay="CENTER" text="99" textAlignment="CENTER"> </Label> </children> </HBox> </center> </BorderPane>
The ControllerFX
creates connection of the FXML to Scala code and underlying ModelFX
for the application logic.
Note the @sfxml
annotation and the constructor arguments corresponding to controls defined in FXML,
like minutesLabel
. The last argument to the constructor is the ModelFX
.
(org.scalafx.extras.mvcfx.stopwatch.StopWatchController
)
package org.scalafx.extras.mvcfx.stopwatch import org.scalafx.extras.mvcfx.ControllerFX import scalafx.Includes._ import scalafx.scene.control.{Button, Label} import scalafxml.core.macros.sfxml @sfxml class StopWatchController(minutesLabel: Label, secondsLabel: Label, fractionLabel: Label, startButton: Button, stopButton: Button, resetButton: Button, model: StopWatchModel) extends ControllerFX { minutesLabel.text.value = format2d(model.minutes.longValue) model.minutes.onChange { (_, _, newValue) => minutesLabel.text.value = format2d(newValue.longValue) } secondsLabel.text.value = format2d(model.seconds.longValue()) model.seconds.onChange { (_, _, newValue) => secondsLabel.text.value = format2d(newValue.longValue()) } fractionLabel.text.value = format2d(model.secondFraction.longValue() / 10) model.secondFraction.onChange { (_, _, newValue) => fractionLabel.text.value = format2d(newValue.longValue() / 10) } startButton.disable <== model.running stopButton.disable <== !model.running resetButton.disable <== model.running startButton.onAction = handle { model.onStart() } stopButton.onAction = handle { model.onStop() } resetButton.onAction = handle { model.onReset() } private def format2d(t: Number) = f"${t.longValue()}%02d" }
The ModelFX
implements the logic for the operation of the Stop Watch.
Notice that there are no direct references to UI controls.
The connection to the UI is through the properties (like minutes
).
The ModelFX
is not aware how the ControllerFX
is implemented.
(org.scalafx.extras.mvcfx.stopwatch.StopWatchModel
)
package org.scalafx.extras.mvcfx.stopwatch import javafx.{concurrent => jfxc} import org.scalafx.extras._ import org.scalafx.extras.mvcfx.ModelFX import scalafx.Includes._ import scalafx.beans.property.{LongProperty, ReadOnlyBooleanProperty, ReadOnlyBooleanWrapper} class StopWatchModel extends ModelFX { private val _running = ReadOnlyBooleanWrapper(false) val running: ReadOnlyBooleanProperty = _running.readOnlyProperty private val counterService = new CounterService() counterService.period = 10.ms val minutes = new LongProperty() val seconds = new LongProperty() val secondFraction = new LongProperty() counterService.elapsedTime.onChange { (_, _, newValue) => val t = newValue.longValue() secondFraction.value = t % 1000 seconds.value = (t / 1000) % 60 minutes.value = t / 1000 / 60 } def onStart(): Unit = { counterService.doResume() _running.value = true } def onStop(): Unit = { counterService.doPause() _running.value = false } def onReset(): Unit = counterService.doReset() private class CounterService extends jfxc.ScheduledService[Long] { private var timeAccumulator: Long = 0 private var restartTime: Long = 0 val elapsedTime = new LongProperty() override def createTask(): jfxc.Task[Long] = { new jfxc.Task[Long]() { override protected def call(): Long = { val ct = System.currentTimeMillis() val et = timeAccumulator + (ct - restartTime) onFX {elapsedTime.value = et} et } } } def doPause(): Unit = { val ct = System.currentTimeMillis() timeAccumulator += (ct - restartTime) onFX {elapsedTime.value = timeAccumulator} this.cancel() } def doResume(): Unit = { restartTime = System.currentTimeMillis() this.restart() } def doReset(): Unit = { timeAccumulator = 0 onFX {elapsedTime.value = 0} } } }
The MVCfx
implementation is very simple,
it only needs instance of the model and information about location of the FXML resource.
(org.scalafx.extras.mvcfx.stopwatch.StopWatch
)
package org.scalafx.extras.mvcfx.stopwatch import org.scalafx.extras.mvcfx.MVCfx class StopWatch(val model: StopWatchModel = new StopWatchModel()) extends MVCfx("/org/scalafx/extras/mvcfx/stopwatch/StopWatchView.fxml")
The MVCfx
can be easily used in an Application class to create UI.
(org.scalafx.extras.mvcfx.stopwatch.StopWatchApp
)
package org.scalafx.extras.mvcfx.stopwatch import scala.language.implicitConversions import scalafx.application.JFXApp import scalafx.application.JFXApp.PrimaryStage import scalafx.scene.Scene import scalafx.scene.layout.BorderPane object StopWatchApp extends JFXApp { stage = new PrimaryStage { scene = new Scene { title = "StopWatch" root = new BorderPane { center = new StopWatch().view } } } }
Runs an operation op
on a separate thread.
Runs an operation op
on a separate thread. Exceptions during execution are ignored.
Similar to org.scalafx.extras#run, with default name for the thread: "offFX".
operation to be performed.
Run operation op
off FX application thread and wait for completion.
Run operation op
off FX application thread and wait for completion.
If the current thread is not the FX application, the operation will be run on it (no new thread will ne created).
operation to be performed.
java.lang.InterruptedException
- if the current thread was interrupted while waiting
java.util.concurrent.CancellationException
- if the computation was cancelled
java.util.concurrent.ExecutionException
- if the computation threw an exception
Run operation op
on FX application thread.
Run operation op
on FX application thread.
If on FX Application thread it will wait for operation to compete,
if not on FX Application thread it will return without waiting for the operation to complete.
operation to be performed.
Run operation op
on FX application thread and wait for completion.
Run operation op
on FX application thread and wait for completion.
If the current thread is the FX application, the operation will be run on it.
operation to be performed.
java.lang.InterruptedException
- if the current thread was interrupted while waiting
java.util.concurrent.CancellationException
- if the computation was cancelled
java.util.concurrent.ExecutionException
- if the computation threw an exception
Runs an operation op
on a separate thread.
Runs an operation op
on a separate thread. Exceptions during execution are ignored.
operation to run
name for the thread to run the operation. Useful for debugging.
Run task on a named daemon thread.
Run task on a named daemon thread.
to run
name for the thread to run the operation. Useful for debugging.
Show a modal dialog with an expandable details about an exception (stack trace).
Show a modal dialog with an expandable details about an exception (stack trace).
dialog title
message shown in the dialog header.
exception.
owner window that will be blacked by the dialog.
Show a modal dialog with an expandable details about an exception (stack trace).
Show a modal dialog with an expandable details about an exception (stack trace).
dialog title
message shown in the dialog header.
exception.
owner window that will be blacked by the dialog. Can be null
to match JavaFX convention.
Show a modal dialog with an expandable details about an exception (stack trace).
Show a modal dialog with an expandable details about an exception (stack trace).
dialog title
message shown in the dialog header.
exception.
owner window that will be blacked by the dialog. Can be null
.
Helper methods for working with ScalaFX.