Browse Source

Working on some cleanup. Moved setup logic for the image tile into it's own class. Reduced the logic in the factory. Need to work on context menus and selectable items in the pane next. Might have to be done with a custom TilePane that allows selection.

master
Drew Short 10 years ago
parent
commit
b5512f02f3
  1. 1
      cli/pom.xml
  2. 1
      engine/pom.xml
  3. 4
      engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
  4. 3
      engine/src/main/scala/com/sothr/imagetools/engine/hash/AHash.scala
  5. 3
      engine/src/main/scala/com/sothr/imagetools/engine/hash/PHash.scala
  6. 1
      gui/pom.xml
  7. 5
      gui/src/main/resources/fxml/mainapp/MainApp.fxml
  8. 68
      gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala
  9. 62
      gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTileFactory.scala
  10. 20
      gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala

1
cli/pom.xml

@ -9,7 +9,6 @@
<relativePath>../parent</relativePath>
</parent>
<groupId>com.sothr.imagetools</groupId>
<artifactId>ImageTools-CLI</artifactId>
<version>0.1.0</version>
<packaging>jar</packaging>

1
engine/pom.xml

@ -9,7 +9,6 @@
<relativePath>../parent</relativePath>
</parent>
<groupId>com.sothr.imagetools</groupId>
<artifactId>ImageTools-Engine</artifactId>
<version>0.1.2</version>
<packaging>jar</packaging>

4
engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala

@ -234,7 +234,7 @@ class ConcurrentEngineProcessingController extends Actor with ActorLogging {
*/
def checkForResults() = {
try {
listener ! SubmitMessage(s"Finished Processing ${processed}/${processed} images")
listener ! SubmitMessage(s"Finished Processing $processed/$processed images")
processorsFinished = 0
toProcess = 0
processed = 0
@ -396,7 +396,7 @@ class ConcurrentEngineSimilarityController extends Actor with ActorLogging {
*/
def checkForResults() = {
try {
listener ! SubmitMessage(s"Finished Scanning ${processed}/${processed} images")
listener ! SubmitMessage(s"Finished Scanning $processed/$processed images")
processorsFinished = 0
toProcess = 0
processed = 0

3
engine/src/main/scala/com/sothr/imagetools/engine/hash/AHash.scala

@ -3,6 +3,9 @@ package com.sothr.imagetools.engine.hash
import grizzled.slf4j.Logging
/**
* Speedy AHash Perceptual Hashing
* Uses the average value of the pixels as a baseline
*
* Created by dev on 1/22/14.
*/
object AHash extends PerceptualHasher with Logging {

3
engine/src/main/scala/com/sothr/imagetools/engine/hash/PHash.scala

@ -4,6 +4,9 @@ import edu.emory.mathcs.jtransforms.dct.FloatDCT_2D
import grizzled.slf4j.Logging
/**
* Complex perceptual hash
* Uses FFT to get
*
* Created by dev on 1/22/14.
*/
object PHash extends PerceptualHasher with Logging {

1
gui/pom.xml

@ -9,7 +9,6 @@
<relativePath>../parent</relativePath>
</parent>
<groupId>com.sothr.imagetools</groupId>
<artifactId>ImageTools-GUI</artifactId>
<version>0.1.0</version>
<packaging>jar</packaging>

5
gui/src/main/resources/fxml/mainapp/MainApp.fxml

@ -65,6 +65,11 @@
<bottom>
<FlowPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="60.0" prefWidth="220.0" BorderPane.alignment="CENTER">
<children>
<CheckBox fx:id="doRecursiveProcessing" mnemonicParsing="false" text="Recursive">
<FlowPane.margin>
<Insets bottom="5.0" />
</FlowPane.margin>
</CheckBox>
<Button maxWidth="1.7976931348623157E308" minWidth="220.0" mnemonicParsing="false" onAction="#showAllImages" text="Show All Images">
<FlowPane.margin>
<Insets bottom="5.0" />

68
gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala

@ -1,22 +1,76 @@
package com.sothr.imagetools.ui.component
import java.io.FileInputStream
import javafx.event.EventHandler
import javafx.geometry.Pos
import javafx.scene.control.{Tooltip, Label}
import javafx.scene.image.{ImageView}
import javafx.scene.input.MouseEvent
import javafx.scene.layout.VBox
import com.sothr.imagetools.engine.image.Image
import grizzled.slf4j.Logging
import resource._
/**
* ImageTile class that is a special VBox
*
* Created by drew on 8/22/14.
*/
class ImageTile extends VBox {
var imageData: Image = null
class ImageTile(thumbnailWidth: Integer, image: com.sothr.imagetools.engine.image.Image) extends VBox with Logging {
val imageData: com.sothr.imagetools.engine.image.Image = image
val preferedTileSize = (thumbnailWidth + 32).toDouble
//set tile size
this.setPrefSize(preferedTileSize, preferedTileSize)
this.setMinSize(preferedTileSize, preferedTileSize)
this.setMaxSize(preferedTileSize, preferedTileSize)
def getImageData: Image = {
imageData
this.setAlignment(Pos.TOP_CENTER)
this.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler[MouseEvent] {
override def handle(event: MouseEvent): Unit = {
if (event.isPrimaryButtonDown) {
//double click
if (event.getClickCount == 2) {
// Look into http://stackoverflow.com/questions/228477/how-do-i-programmatically-determine-operating-system-in-java
// for proper multi-platform opening support
//Desktop.getDesktop.open(new File(image.getImagePath))
} else {
}
} else if (event.isSecondaryButtonDown) {
//right click context menu
}
}
})
def setImageData(image: Image) = {
this.imageData = image
// Image
val genImageView = new ImageView()
debug(s"Getting thumbnail from: ${image.getThumbnailPath}")
managed(new FileInputStream(image.getThumbnailPath)) acquireAndGet {
thumbSource =>
val thumbnail = new javafx.scene.image.Image(thumbSource)
genImageView.setImage(thumbnail)
if (thumbnail.getHeight > thumbnail.getWidth) {
genImageView.setFitHeight(128.0)
} else {
genImageView.setFitWidth(128.0)
}
}
genImageView.setPreserveRatio(true)
this.getChildren.add(genImageView)
//Label
val imageLabel = new Label()
imageLabel.setText(s"${image.getHeight}x${image.getWidth}")
imageLabel.setWrapText(true)
//Tooltip
val tooltip = new Tooltip()
tooltip.setText(s"${image.getName}")
imageLabel.setTooltip(tooltip)
this.getChildren.add(imageLabel)
def getImageData: com.sothr.imagetools.engine.image.Image = {
imageData
}
}

62
gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTileFactory.scala

@ -1,15 +1,9 @@
package com.sothr.imagetools.ui.component
import java.awt.Desktop
import java.io.{File, FileInputStream}
import javafx.event.EventHandler
import javafx.geometry.{Insets, Pos}
import javafx.scene.control.{Label, Tooltip}
import javafx.scene.image.{Image, ImageView}
import javafx.scene.input.MouseEvent
import javafx.geometry.Insets
import com.sothr.imagetools.engine.util.PropertiesService
import grizzled.slf4j.Logging
import resource._
/**
* Created by drew on 8/6/14.
@ -19,58 +13,10 @@ import resource._
object ImageTileFactory extends Logging {
def get(image: com.sothr.imagetools.engine.image.Image): ImageTile = {
val imageTile = new ImageTile()
imageTile.setImageData(image)
//set tile size
imageTile.setPrefSize(160.0d, 160.0d)
imageTile.setMinSize(160.0d, 160.0d)
imageTile.setMaxSize(160.0d, 160.0d)
val thumbnailWidth = PropertiesService.get("app.thumbnail.size","128").toInt
val imageTile = new ImageTile(thumbnailWidth, image)
//set padding
imageTile.setPadding(new Insets(2, 2, 2, 2))
//imageTile.setSpacing(5.0d)
imageTile.setAlignment(Pos.TOP_CENTER)
imageTile.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler[MouseEvent] {
override def handle(event: MouseEvent): Unit = {
if (event.isPrimaryButtonDown) {
//double click
if (event.getClickCount == 2) {
Desktop.getDesktop.open(new File(image.getImagePath))
} else {
}
} else if (event.isSecondaryButtonDown) {
//right click context menu
}
}
})
// Image
val genImageView = new ImageView()
debug(s"Getting thumbnail from: ${image.getThumbnailPath}")
managed(new FileInputStream(image.getThumbnailPath)) acquireAndGet {
thumbSource =>
val thumbnail = new Image(thumbSource)
genImageView.setImage(thumbnail)
if (thumbnail.getHeight > thumbnail.getWidth) {
genImageView.setFitHeight(128.0)
} else {
genImageView.setFitWidth(128.0)
}
}
genImageView.setPreserveRatio(true)
imageTile.getChildren.add(genImageView)
//Label
val imageLabel = new Label()
imageLabel.setText(s"${image.getHeight}x${image.getWidth}")
imageLabel.setWrapText(true)
//Tooltip
val tooltip = new Tooltip()
tooltip.setText(s"${image.getName}")
imageLabel.setTooltip(tooltip)
imageTile.getChildren.add(imageLabel)
imageTile
}

20
gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala

@ -1,13 +1,12 @@
package com.sothr.imagetools.ui.controller
import java.io.{File, IOException}
import java.util.ArrayList
import java.util.Scanner
import javafx.application.Platform
import javafx.event.ActionEvent
import javafx.fxml.FXML
import javafx.scene.control._
import javafx.scene.layout.{VBox, TilePane, AnchorPane}
import javafx.scene.layout.{AnchorPane, TilePane, VBox}
import javafx.scene.text.{Text, TextAlignment}
import javafx.scene.web.WebView
import javafx.scene.{Group, Node, Scene}
@ -15,17 +14,17 @@ import javafx.stage.{DirectoryChooser, Stage, StageStyle}
import javafx.util.Callback
import akka.actor._
import com.sothr.imagetools.engine._
import com.sothr.imagetools.engine.image.{Image, SimilarImages}
import com.sothr.imagetools.engine.util.{PropertiesService, ResourceLoader}
import com.sothr.imagetools.engine._
import com.sothr.imagetools.ui.component.ImageTileFactory
import grizzled.slf4j.Logging
import org.markdown4j.Markdown4jProcessor
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.util.{Failure, Success}
import ExecutionContext.Implicits.global
/**
* Main Application controller
@ -48,6 +47,7 @@ class AppController extends Logging {
// Others
@FXML var progressBar: ProgressBar = null
@FXML var paginator: Pagination = null
@FXML var doRecursiveProcessing: CheckBox = null
// Engine
val engine: Engine = new ConcurrentEngine()
@ -164,9 +164,9 @@ class AppController extends Logging {
@FXML
def showAllImages(event: ActionEvent) = {
resetPaginator()
imageTilePane.getChildren.setAll(new ArrayList[Node]())
imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
val f: Future[List[Image]] = Future {
engine.getImagesForDirectory(currentDirectory)
engine.getImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
}
f onComplete {
@ -187,10 +187,10 @@ class AppController extends Logging {
@FXML
def showSimilarImages(event: ActionEvent) = {
resetPaginator()
imageTilePane.getChildren.setAll(new ArrayList[Node]())
imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
val f: Future[List[SimilarImages]] = Future {
engine.getSimilarImagesForDirectory(currentDirectory)
engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
}
f onComplete {
@ -234,9 +234,9 @@ class AppController extends Logging {
def showPage(pageIndex: Integer) = {
val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "50").toInt
val startIndex = pageIndex * itemsPerPage
val endIndex = if ((startIndex + itemsPerPage) > this.currentImages.size) this.currentImages.length else (startIndex + itemsPerPage)
val endIndex = if ((startIndex + itemsPerPage) > this.currentImages.size) this.currentImages.length else startIndex + itemsPerPage
//clear and populate the scrollpane
imageTilePane.getChildren.setAll(new ArrayList[Node]())
imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
val images = this.currentImages.slice(startIndex, endIndex)
Platform.runLater(new Runnable() {
override def run() {

Loading…
Cancel
Save