Search code examples
scalajavafxscalafx

How to convert ScalaFX root to image and then save it in local


I am creating a "line chart" using scalaFX as below and want to save the scene as an image in the local.

I know there is one function in scalaFX - snapshot but I do not know how to use it. I could not find any examples anywhere.


Solution

  • Here's a relatively simple example (based upon the ScalaFX Catagory Line Chart Demo) that you should be able to adapt to your requirements:

    import java.awt.image.BufferedImage
    import java.io.File
    import javax.imageio.ImageIO
    import scalafx.application.JFXApp
    import scalafx.collections.ObservableBuffer
    import scalafx.embed.swing.SwingFXUtils
    import scalafx.geometry.Side
    import scalafx.Includes._
    import scalafx.scene.{Node, Scene}
    import scalafx.scene.chart.{CategoryAxis, LineChart, NumberAxis, XYChart}
    
    object TakeSnapshot
    extends JFXApp {
    
      val dataPairs = Seq(
        ("Alpha", 50),
        ("Beta", 80),
        ("RC1", 90),
        ("RC2", 30),
        ("1.0", 122),
        ("1.1", 10),
      )
    
      stage = new JFXApp.PrimaryStage {
        title.value = "Take a Snapshot"
        width = 640
        height = 400
        scene = new Scene {
          root = new LineChart(CategoryAxis("X Axis"), NumberAxis("Y Axis")) {
            title = "LineChart with Category Axis"
            legendSide = Side.Right
            data = XYChart.Series[String, Number](
              "Series 1",
              ObservableBuffer(dataPairs.map {case (x, y) => XYChart.Data[String, Number](x, y)})
            )
          }
        }
      }
    
      // Take the snapshot.
      takeSnapshot(stage.scene.root(), new File("MyLineChart.png"))
    
      // Take a snapshot of the specified node, writing it into the specified file. The dimensions of
      // the image will match the size of the node (and its child nodes) on the screen in pixels.
      def takeSnapshot(node: Node, file: File) {
    
        // Take the snapshot, which returns a WritableImage instance.
        //
        // Note:
        // 1. The first argument is a SnapshotParameters instance. If null, it will use the associated
        //    scene's settings. More here:
        //    https://docs.oracle.com/javase/8/javafx/api/javafx/scene/SnapshotParameters.html
        // 2. The second argument is a WritableImage instance. If null, it will creat a new
        //    WritableImage instance, with the dimensions of the associated node, will be created.
        //    More here:
        //    https://docs.oracle.com/javase/8/javafx/api/javafx/scene/image/WritableImage.html
        //
        // Scala frowns on the use of null values, but there's really no alternative here.
        val image = node.snapshot(null, null)
    
        // Convert the image to a buffered image. Passing null for the BufferedImage instance argument
        // will ensure one is created for you.
        val bufferedImage = SwingFXUtils.fromFXImage(image, null)
        assert(bufferedImage ne null)
    
        // Now write the buffered image into a file (in Portable Network Graphics, PNG, format.)
        // This may throw an exception if the file isn't writable, etc.
        ImageIO.write(bufferedImage, "png", file)
      }
    }
    

    After running the program, you should find a file called "MyLineChart.png" in your project's root directory.

    Further JavaFX documentation for snapshot() is available here.