implement cancel operation for image tagging

This commit is contained in:
Denis-Cosmin Nutiu 2024-03-24 12:02:07 +02:00
parent 4ef219a8c1
commit 146db2c9c0
3 changed files with 84 additions and 30 deletions

View file

@ -17,6 +17,7 @@ class MainPage : Application() {
fxmlLoader = FXMLLoader(MainPage::class.java.getResource("main-window-view.fxml"))
val scene = Scene(fxmlLoader.load(), 640.0, 760.0)
fxmlLoader.getController<MainPageController>().initialize()
stage.title = "Image Tagger"
stage.scene = scene
stage.minWidth = 640.0

View file

@ -4,8 +4,11 @@ import dev.nuculabs.imagetagger.ai.ImageTagsPrediction
import dev.nuculabs.imagetagger.ui.controls.ImageTagsEntryControl
import javafx.application.Platform
import javafx.fxml.FXML
import javafx.scene.control.Button
import javafx.scene.control.ProgressBar
import javafx.scene.control.Separator
import javafx.scene.layout.HBox
import javafx.scene.layout.Priority
import javafx.scene.layout.VBox
import javafx.stage.FileChooser
import java.io.File
@ -50,6 +53,11 @@ class MainPageController {
*/
private val imageTagsPrediction = ImageTagsPrediction.getInstance()
/**
* A boolean that when set to true it will stop the current image tagging operation.
* When a new operation is started the boolean is reset to false.
*/
private var isCurrentTagsOperationCancelled: Boolean = false
@FXML
private lateinit var progressBar: ProgressBar
@ -57,6 +65,16 @@ class MainPageController {
@FXML
private lateinit var verticalBox: VBox
@FXML
private lateinit var cancelButton: Button
/**
* Initializes the controller. Needs to be called after the dependencies have been injected.
*/
fun initialize() {
HBox.setHgrow(progressBar, Priority.ALWAYS)
HBox.setHgrow(cancelButton, Priority.ALWAYS)
}
/**
* Prompts the user to select files then predicts tags for the selected image files.
*/
@ -66,7 +84,9 @@ class MainPageController {
val fileChooser = FileChooser().apply { title = "Choose images" }
val filePaths = fileChooser.showOpenMultipleDialog(null) ?: return
isCurrentTagsOperationCancelled = false
progressBar.isVisible = true
cancelButton.isVisible = true
progressBar.progress = 0.0
imageFilesTotal = filePaths.count()
processedImageFilesCount.set(0)
@ -74,8 +94,18 @@ class MainPageController {
Thread {
logger.info("Analyzing $imageFilesTotal files")
filePaths.forEach { filePath ->
if (isCurrentTagsOperationCancelled) {
logger.info("Cancelling current prediction operation.")
return@Thread
}
workerSemaphore.acquire()
workerPool.submit {
if (isCurrentTagsOperationCancelled) {
logger.info("Image prediction task is cancelled.")
workerSemaphore.release()
return@submit
}
predictImageTags(
filePath,
onError = {
@ -96,6 +126,16 @@ class MainPageController {
}
}
/**
* Cancels the prediction operation.
*/
@FXML
fun onCancelTagImagesClick() {
isCurrentTagsOperationCancelled = true
progressBar.isVisible = false
cancelButton.isVisible = false
}
/**
* Predicts an image tags and executes an action with it.
*
@ -140,6 +180,7 @@ class MainPageController {
((processedImageFilesCount.incrementAndGet() * 100) / imageFilesTotal).toDouble() / 100.0
if (processedImageFilesCount.get() == imageFilesTotal) {
progressBar.isVisible = false
cancelButton.isVisible = false
logger.info("Finished processing images.")
}
}
@ -148,6 +189,7 @@ class MainPageController {
* Shuts down the MainPageController.
*/
fun shutdown() {
isCurrentTagsOperationCancelled = true
workerPool.shutdownNow()
}
}

View file

@ -5,35 +5,46 @@
<?import javafx.scene.layout.*?>
<?import org.kordamp.ikonli.javafx.FontIcon?>
<BorderPane prefHeight="537.0" prefWidth="725.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/17.0.2-ea" fx:controller="dev.nuculabs.imagetagger.ui.MainPageController">
<padding>
<Insets bottom="20.0" left="30.0" right="30.0" top="20.0" />
</padding>
<top>
<HBox alignment="CENTER_LEFT">
<padding>
<Insets bottom="5" left="5" right="5"/>
</padding>
<Button onAction="#onTagImagesButtonClick" text="Tag Images">
<graphic>
<FontIcon iconLiteral="far-images" iconSize="16"/>
</graphic>
</Button>
<Separator orientation="VERTICAL" style="-fx-padding: 10px" />
</HBox>
</top>
<center>
<ScrollPane>
<VBox fx:id="verticalBox">
</VBox>
</ScrollPane>
</center>
<BorderPane prefHeight="537.0" prefWidth="725.0" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/17.0.2-ea" fx:controller="dev.nuculabs.imagetagger.ui.MainPageController">
<padding>
<Insets bottom="20.0" left="30.0" right="30.0" top="20.0"/>
</padding>
<top>
<HBox alignment="CENTER_LEFT">
<padding>
<Insets bottom="5" left="5" right="5"/>
</padding>
<Button onAction="#onTagImagesButtonClick" text="Tag Images">
<graphic>
<FontIcon iconLiteral="far-images" iconSize="16"/>
</graphic>
</Button>
<Separator orientation="VERTICAL" style="-fx-padding: 10px"/>
</HBox>
</top>
<center>
<ScrollPane fitToWidth="true" fitToHeight="true">
<VBox fx:id="verticalBox">
</VBox>
</ScrollPane>
</center>
<bottom>
<ProgressBar fx:id="progressBar" maxWidth="Infinity" visible="false">
<padding>
<Insets top="5" />
</padding>
</ProgressBar>
</bottom>
<bottom>
<HBox alignment="CENTER_LEFT" prefWidth="Infinity">
<padding>
<Insets top="5"/>
</padding>
<ProgressBar fx:id="progressBar" maxWidth="Infinity" visible="false">
<padding>
<Insets right="5"/>
</padding>
</ProgressBar>
<Button fx:id="cancelButton" onAction="#onCancelTagImagesClick" alignment="CENTER_RIGHT" visible="false">
<graphic>
<FontIcon iconLiteral="far-times-circle" iconColor="red" iconSize="16"/>
</graphic>
</Button>
</HBox>
</bottom>
</BorderPane>