Skip to content

Commit

Permalink
Add helper UI for running batch processing tasks #34
Browse files Browse the repository at this point in the history
  • Loading branch information
jpsacha committed Jul 12, 2024
1 parent 7ae35f3 commit 642a084
Show file tree
Hide file tree
Showing 8 changed files with 699 additions and 2 deletions.
4 changes: 3 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,9 @@ to parent size.

### Batch Processing and Progress Dialog

Work in progress, see `ProgressStatusDemoApp` for example of use
Work in progress
* Helper UI for running batch processing tasks, see `BatchRunnerProgressHelperDemoApp` for example of use
* Component for display of progress of batch processing tasks, see `ProgressStatusDemoApp` for example of use


Demos
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scala.xml.{Node => XmlNode, NodeSeq => XmlNodeSeq, _}
// JAR_BUILT_BY - Name to be added to Jar metadata field "Built-By" (defaults to System.getProperty("user.name")
//

val projectVersion = "0.9.0.3-SNAPSHOT"
val projectVersion = "0.9.0.5-SNAPSHOT"
val versionTagDir = if (projectVersion.endsWith("SNAPSHOT")) "master" else "v." + projectVersion
val _scalaVersions = Seq("3.3.3", "2.13.14", "2.12.19")
val _scalaVersion = _scalaVersions.head
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved.
* Author's e-mail: jpsacha at gmail.com
*/

package org.scalafx.extras.batch

import org.scalafx.extras.BusyWorker
import org.scalafx.extras.BusyWorker.SimpleTask
import scalafx.application.JFXApp3
import scalafx.application.JFXApp3.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.{Button, Label}
import scalafx.scene.layout.VBox
import scalafx.stage.Window

import scala.util.Random

object BatchRunnerProgressHelperDemoApp extends JFXApp3:

private lazy val busyWorker = new BusyWorker(Title, parentWindow)
private val Title = "Batch Processing / Progress Dialog Demo"

private val nTasks = 100
private val minTime = 500
private val maxTime = 1000

override def start(): Unit =
stage = new PrimaryStage:
title = Title
scene = new Scene:
content = new VBox:
padding = Insets(21)
spacing = 14
children = Seq(
new Label(
s"""Press "Run" to initiate processing.
|Wait till processing finished or press "Abort".
|There will be $nTasks processed.
|Tasks will have randomly generated time between $minTime and $maxTime ms.
|If task's time is divisible by 6, that task will fail.
|""".stripMargin
),
new Button("Run Sequential"):
onAction = (_) => onStart(false)
prefWidth = 120
,
new Button(" Run Parallel "):
onAction = (_) => onStart(false)
prefWidth = 120
)

private def onStart(useParallelProcessing: Boolean): Unit = busyWorker.doTask("Start") {
new SimpleTask[Unit]:
override def call(): Unit =
val helper =
new BatchRunnerProgressHelper[String]("Sample batch processing", parentWindow, useParallelProcessing):
override def createSampleTasks(): Seq[ItemTask[String]] =
val r = new Random()
(1 to nTasks).map { i =>
new ItemTask[String]:
override val name = s"Task #$i"

override def run(): String =
val t = minTime + r.nextInt(maxTime - minTime)
Thread.sleep(t)
if t % 6 == 0 then throw new Exception(s"Do not like ${t}")
s"name t = $t"
}
helper.run()
}

def parentWindow: Option[Window] = Option(stage)
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2011-2024, ScalaFX Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the ScalaFX Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.scalafx.extras.batch

import scala.util.{Failure, Success}


object ParallelBatchRunnerDemo {
class DemoTaskItem(n: Int) extends ItemTask[Int] {
val name = s"Demo TaskItem $n"

def run(): Int = {
// println(s"Item ${n} - start")
Thread.sleep(300)
if n == 7 then
// println(s"Item ${n} - error")
throw new IllegalArgumentException(s"Don't give me $n")

// println(s"Item ${n} - end")
n
}
}


def main(args: Array[String]): Unit = {
val items: Seq[DemoTaskItem] = Range(0, 10).map { i => new DemoTaskItem(i) }

val batchHelper = new ParallelBatchRunner(items, progressUpdate, useParallelProcessing = true)

val results = batchHelper.execute()

println()
println("Summarize processing")
results.foreach {
case (name, Success(r)) => println(s"$name: SUCCESS: $r")
case (name, Failure(e)) => println(s"$name: ERROR : ${e.getMessage}")
}
}

def progressUpdate(running : Long,
successful: Long,
failed : Long,
canceled : Long,
executed : Long,
total : Long,
isCanceled : Boolean,
perc : Double,
message : String) : Unit = {
val m =
f"R:$running%2d, S:$successful%2d, F:$failed%2d, E:$executed%2d, C:$canceled, T:$total%d, " +
f"C:$isCanceled, perc:${perc.toInt}%3d, $message"
println(m)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2011-2024, ScalaFX Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the ScalaFX Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.scalafx.extras.batch

object BatchRunner {

@FunctionalInterface
trait ProgressUpdater:
def update(
running: Long,
successful: Long,
failed: Long,
canceled: Long,
executed: Long,
total: Long,
isCanceled: Boolean,
perc: Double,
message: String
): Unit
}
trait BatchRunner[T, I <: ItemTask[T]] {

import BatchRunner.ProgressUpdater

// TODO: Is this trait needed

protected def itemTasks: Seq[I]

protected def progressUpdater: ProgressUpdater
}
Loading

0 comments on commit 642a084

Please sign in to comment.