Saturday, 14 January 2012

Memory Pie Chart in TableView with Scala and JavaFX

In my series (Scala, JavaFX 2) - another code snippet to display a memory pie chart in a tableview with a custom renderer. Below is the code to achieve this:
The imports in used:
import javafx.application.Application
import javafx.beans.property.SimpleLongProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.scene.control.cell.PropertyValueFactory
import javafx.scene.control.TableColumn
import javafx.scene.control.TableView
import javafx.scene.layout.BorderPane
import javafx.scene.paint.Color
import javafx.scene.Scene
import javafx.stage.Stage
import javafx.util.Callback
import javafx.scene.control.TableCell
Then the class representing one line in the table (Note the use of a tuple2 what will later be used for the chart - not really needed as the row can be accessed in the custom renderer - but other posts will follow...):
sealed class MyTableRow {
  val totMem = new SimpleLongProperty(0L)
  def totMemProperty = totMem
  def getTotMem : Long = totMem.get
  def setTotMem(value: Long) = totMem.set(value)
  
  val maxMem = new SimpleLongProperty(0L)
  def maxMemProperty = maxMem
  def getMaxMem : Long = maxMem.get
  def setMaxMem(value: Long) = maxMem.set(value)
  
  val freeMem = new SimpleLongProperty(0L)
  def freeMemProperty = freeMem
  def getFreeMem : Long = freeMem.get
  def setFreeMem(value: Long) = freeMem.set(value)
  
  val usedMem = new SimpleLongProperty(0L)
  def usedMemProperty = usedMem
  def getUsedMem : Long = usedMem.get
  def setUsedMem(value: Long) = usedMem.set(value)
  
  val chartData = new SimpleObjectProperty((0L, 0L))
  def chartDataProperty = chartData
  def getChartData : Tuple2[Long, Long] = chartData.get
  def setChartData(value: Tuple2[Long, Long]) = chartData.set(value)
}
Then the main application:
sealed class MemTable extends Application {
  def runInJFXThread(f: () => Unit) = javafx.application.Platform.runLater(new Runnable() {def run() {f()}})
  
  val timer = new java.util.Timer
  val table = new TableView[MyTableRow]
  val tableData = javafx.collections.FXCollections.observableArrayList[MyTableRow]()
  table.setItems(tableData)
  
  val singleRow = new MyTableRow
  tableData.addAll(singleRow)
  
  override def start(stage: Stage) = {
    val borderPane = new BorderPane
    val scene = new Scene(borderPane, 400, 300, Color.WHITE)
    borderPane.setCenter(table)
    setupUI
    stage.setTitle("Memory Table")
    stage.setScene(scene)
    stage.show
    timer.schedule(new HeapSnapshotTask, 0L, 1000L)
  }
  
  sealed class HeapSnapshotTask extends java.util.TimerTask {
    val inKb = 1024L
    override def run = {
      val r = Runtime.getRuntime
      val totalMemory = r.totalMemory / inKb
      val freeMemory = r.freeMemory / inKb
      val maxMemory = r.maxMemory / inKb
      val usedMemory = totalMemory - freeMemory
      
      runInJFXThread{() => {
        singleRow.totMem.set(totalMemory)
        singleRow.maxMem.set(maxMemory)
        singleRow.freeMem.set(freeMemory)
        singleRow.usedMem.set(usedMemory)
        singleRow.chartData.set((freeMemory, usedMemory))
      }}
      
      println("T = " + totalMemory + ", F = " + freeMemory + ", M = " + maxMemory + ", U = " + usedMemory)
    }
  }
  
  private def setupUI = {
    val memColumns = new TableColumn[MyTableRow, AnyRef]("Memory")
    
    val maxMemColumn = new TableColumn[MyTableRow, Long]("Max")
    maxMemColumn.setCellValueFactory(new PropertyValueFactory[MyTableRow, Long]("maxMem"))
    val totalMemColumn = new TableColumn[MyTableRow, Long]("Total")
    totalMemColumn.setCellValueFactory(new PropertyValueFactory[MyTableRow, Long]("totMem"))
    val freeMemColumn = new TableColumn[MyTableRow, Long]("Free")
    freeMemColumn.setCellValueFactory(new PropertyValueFactory[MyTableRow, Long]("freeMem"))
    val usedMemColumn = new TableColumn[MyTableRow, Long]("Used")
    usedMemColumn.setCellValueFactory(new PropertyValueFactory[MyTableRow, Long]("usedMem"))
    val chartColumn = new TableColumn[MyTableRow, AnyRef]("Chart")
    chartColumn.setCellValueFactory(new PropertyValueFactory[MyTableRow, AnyRef]("chartData"))

    val chartRenderer = new Callback[TableColumn[MyTableRow, AnyRef], TableCell[MyTableRow, AnyRef]] {
      override def call(col : TableColumn[MyTableRow, AnyRef]) : TableCell[MyTableRow, AnyRef] = {
        new TableCell[MyTableRow, AnyRef] {
          import javafx.scene.chart.PieChart
          import javafx.collections.FXCollections
          import javafx.scene.layout.Pane
          import javafx.scene.control.Label
   
          val usedMemData = new PieChart.Data("Used", 0.0)
          val freeMemData = new PieChart.Data("Free", 0.0)
          val pieChartData = FXCollections.observableArrayList(usedMemData, freeMemData)
          val pieChart = new PieChart(pieChartData)
          pieChart.setPrefSize(150, 150)
          pieChart.setLabelsVisible(true)
          pieChart.setLegendVisible(false)
          val borderPane = new BorderPane
          val southPane = new Pane
          setGraphic(borderPane)
          borderPane.setCenter(pieChart)
          borderPane.setBottom(southPane)
          val southLabel = new Label
          southPane.getChildren().addAll(southLabel)
          override def updateItem(value : AnyRef, empty : Boolean) = {
            val row = getTableRow
            if (row != null && value != null) {
              val datum = value.asInstanceOf[Tuple2[Long, Long]]
              val free = datum._1
              val used = datum._2
              usedMemData.setPieValue(used)
              freeMemData.setPieValue(free)
              val desc = "F="+free+" U="+used
              southLabel.setText(desc)
            } // if
          } // updateItem
        }
      }
    } // chartRenderer

    chartColumn.setCellFactory(chartRenderer)
    
    memColumns.getColumns().addAll(maxMemColumn, totalMemColumn, freeMemColumn, usedMemColumn)
    
    table.getColumns().addAll(memColumns, chartColumn)
  }
}

object MemTable {
  def main(args: Array[String]): Unit = {
    javafx.application.Application.launch(classOf[MemTable])
  }
}
That's it for now

1 comment:

Kai Sellgren said...

I have this code:

c.setCellValueFactory(new PropertyValueFactory[RevCommit, _]("getFullMessage"))

I am using JGit. I get an error on that line and it says:

error: class type required but javafx.scene.control.cell.PropertyValueFactory[org.eclipse.jgit.revwalk.RevCommit, _] found

Do you happen to have ideas why your example works while this does not?

Blog Archive