首页 > 解决方案 > 在 ScalaFX 中更改散点图绘图点的大小和符号

问题描述

我想制作一个线性回归程序,将数据可视化给用户。我使用 EJML 进行计算,使用 ScalaFX 进行前端。一切都很好,但是当我使用散点图绘制数据时,从数据中绘制的线被设置为覆盖原始数据点的矩形。我想知道如何更改绘制点的大小、形状和透明度等。

几乎所有关于 JavaFX 的指南都说我应该修改 CSS 文件(它不会自动存在)以便为我的图表设置样式。我不知道如何在 ScalaFX 中做到这一点,甚至可以这样做。我搜索所有可能的教程的结果都没有结果。

import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.chart.ScatterChart
import scalafx.collections.ObservableBuffer
import scalafx.scene.chart.NumberAxis
import scalafx.scene.chart.XYChart
import scalafx.scene.shape.Line
import org.ejml.simple.SimpleMatrix
import scala.math.pow
import scala.collection.mutable.Buffer


object Plotting extends JFXApp {
  /*
   * Below are some arbitrary x and y values for a regression line
   */
  val xValues = Array(Array(1.0, 1.0, 1.0, 1.0, 1.0, 1.0), Array(14.0, 19.0, 22.0, 26.0, 31.0, 43.0))
  val yValues = Array(Array(51.0, 57.0, 66.0, 71.0, 72.0, 84.0))
  val temp    = yValues.flatten
  val wrapper = xValues(1).zip(temp)
  /*
   * In the lines before stage what happens is that matrices for the x and y values are created, coefficients
   * for the regression line are calculated with matrix operations and (x, y) points are calculated for the 
   * regression line.
   */
  val X       = new SimpleMatrix(xValues).transpose
  val Y       = new SimpleMatrix(yValues).transpose
  val secondX = new SimpleMatrix(xValues(0).size, 2)
  for (i <- 0 until xValues(0).size) {
    secondX.set(i, 0, xValues(0)(i))
    secondX.set(i, 1, xValues(1)(i))
  }
  val invertedSecondX = secondX.pseudoInverse()
  val B = invertedSecondX.mult(Y)
  val graphPoints = Buffer[(Double, Double)]()
  for (i <- 0 to xValues(1).max.toInt) {
    graphPoints.append((i.toDouble, B.get(0, 0) + i * B.get(1, 0)))
  }

  stage = new JFXApp.PrimaryStage {
    title = "Demo"
    scene = new Scene(400, 400) {
      val xAxis = NumberAxis()
      val yAxis = NumberAxis()
      val pData = XYChart.Series[Number, Number](
        "Data",
        ObservableBuffer(wrapper.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val graph = XYChart.Series[Number, Number](
        "RegressionLine",
        ObservableBuffer(graphPoints.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val plot = new ScatterChart(xAxis, yAxis, ObservableBuffer(graph, pData))
      root     = plot
    }
  }
}

标签: scalascalafx

解决方案


这当然没有像它可能的那样有据可查...... :-(

样式表通常放在项目的资源目录中。如果您使用的是SBT(推荐),这将是src/main/resources.

在此示例中,我添加了一个名为MyCharts.css该目录的样式表,其中包含以下内容:

/* Blue semi-transparent 4-pointed star, using SVG path. */
.default-color0.chart-symbol {
    -fx-background-color: blue;
    -fx-scale-shape: true;
    -fx-shape: "M 0.0 10.0 L 3.0 3.0 L 10.0 0.0 L 3.0 -3.0 L 0.0 -10.0 L -3.0 -3.0 L -10.0 0.0 L -3.0 3.0 Z ";
    -fx-opacity: 0.5;
}

/* Default shape is a rectangle. Here, we round it to become a red circle with a white
 * center. Change the radius to control the size.
 */
.default-color1.chart-symbol {
    -fx-background-color: red, white;
    -fx-background-insets: 0, 2;
    -fx-background-radius: 3px;
    -fx-padding: 3px;
}

color0将用于第一个数据系列(回归线),color1用于第二个(您的散点数据)。所有其他系列都使用默认的JavaFX样式。

(有关使用可缩放矢量图形( SVG ) 路径定义自定义形状的更多信息,请参阅SVG规范的相关部分。)

要让 ScalaFX ( JavaFX )使用此样式表,您可以选择多个选项。要让它们全局应用,请将其添加到主场景(这是我在下面所做的)。或者,如果每个图表需要不同的样式,您可以为特定图表添加不同的样式表。(顺便说一句,我还添加了标准包括导入,因为这可以防止许多JavaFX - ScalaFX元素转换问题;否则,我没有对您的源代码进行任何更改。)

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.chart.ScatterChart
import scalafx.collections.ObservableBuffer
import scalafx.scene.chart.NumberAxis
import scalafx.scene.chart.XYChart
import scalafx.scene.shape.Line
import org.ejml.simple.SimpleMatrix
import scala.math.pow
import scala.collection.mutable.Buffer

object Plotting extends JFXApp {
  /*
   * Below are some arbitrary x and y values for a regression line
   */
  val xValues = Array(Array(1.0, 1.0, 1.0, 1.0, 1.0, 1.0), Array(14.0, 19.0, 22.0, 26.0, 31.0, 43.0))
  val yValues = Array(Array(51.0, 57.0, 66.0, 71.0, 72.0, 84.0))
  val temp    = yValues.flatten
  val wrapper = xValues(1).zip(temp)
  /*
   * In the lines before stage what happens is that matrices for the x and y values are created, coefficients
   * for the regression line are calculated with matrix operations and (x, y) points are calculated for the 
   * regression line.
   */
  val X       = new SimpleMatrix(xValues).transpose
  val Y       = new SimpleMatrix(yValues).transpose
  val secondX = new SimpleMatrix(xValues(0).size, 2)
  for (i <- 0 until xValues(0).size) {
    secondX.set(i, 0, xValues(0)(i))
    secondX.set(i, 1, xValues(1)(i))
  }
  val invertedSecondX = secondX.pseudoInverse()
  val B = invertedSecondX.mult(Y)
  val graphPoints = Buffer[(Double, Double)]()
  for (i <- 0 to xValues(1).max.toInt) {
    graphPoints.append((i.toDouble, B.get(0, 0) + i * B.get(1, 0)))
  }

  stage = new JFXApp.PrimaryStage {
    title = "Demo"
    scene = new Scene(400, 400) {

      // Add our stylesheet.
      stylesheets.add("MyCharts.css")

      val xAxis = NumberAxis()
      val yAxis = NumberAxis()
      val pData = XYChart.Series[Number, Number](
        "Data",
        ObservableBuffer(wrapper.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val graph = XYChart.Series[Number, Number](
        "RegressionLine",
        ObservableBuffer(graphPoints.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val plot = new ScatterChart(xAxis, yAxis, ObservableBuffer(graph, pData))
      root     = plot
    }
  }
}

有关可用的 CSS 格式化选项(更改形状、颜色、透明度等)的更多信息,请参阅JavaFX CSS 参考指南

结果如下所示: 图表图像,带有自定义点。


推荐阅读