在 ScalaFX 中更改散点图绘图点的大小和符号
Changing the size and symbol of scatter chart plot points in 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
}
}
}
这肯定没有像它可能的那样得到很好的记录......:-(
样式表通常放在项目的资源目录中。如果您使用 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) 路径定义自定义形状的更多信息,请参阅 relevant section of the SVG specification.)
要让 ScalaFX (JavaFX) 使用此样式表,您有多种选择。要让它们在全球范围内应用,请将其添加到主场景(这是我在下面所做的)。或者,如果每个图表需要不同的样式,您可以向特定图表添加不同的样式表。 (顺便说一句,我还添加了标准 include import,因为这可以防止许多 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 Reference Guide。
结果如下所示:
我几乎不敢在 Mike Allens 解决方案中添加一些东西(一如既往地非常好),但这对我来说没有用,因为我无法让我的 scala 找到 and/or处理 .css 文件。
如果可能的话,我会这样做,但我就是无法让它工作。
这是我想出的:
假设我有一些数据要显示:
val xyExampleData: ObservableBuffer[(Double, Double)] = ObservableBuffer(Seq(
1 -> 1,
2 -> 4,
3 -> 9))
然后我将其转换为折线图的系列:
val DataPoints = ObservableBuffer(xyExampleData map { case (x, y) => XYChart.Data[Number, Number](x, y) })
val PointsToDisplay = XYChart.Series[Number, Number]("Points", DataPoints)
现在我再次将其放入缓冲区,可能还有来自不同系列的其他数据。
val lineChartBuffer = ObservableBuffer(PointsToDisplay, ...)
最后我创建了我的折线图,我称之为(缺乏创造力)折线图:
val lineChart = new LineChart(xAxis, yAxis, lineChartBuffer) {...}
数据点之间的线现在可以很容易地重新着色:
lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;")
这将更改 LineChartBuffer 中第一个数据集的线条颜色。
如果您想更改第二个行属性,请调用
lineChart.lookup(".default-color1.chart-series-line")...
还有“-fx-stroke-width: 3px;”设置行的with。
“-fx-不透明度:0.1;”
"-fx-stroke-dash-array: 10;"
-fx-填充:蓝色;
也很有用,但不要重复调用上面的行,因为第二次调用会覆盖第一次。
而是将所有字符串连接成一个:
lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;-fx-opacity: 0.1;-fx-stroke-dash-array: 10;-fx-fill: blue;")
现在每个数据点的符号格式:
不幸的是,除了单独格式化每个符号之外似乎没有其他方法:
lineChart.lookupAll(".default-color0.chart-line-symbol").asScala foreach { node => node.setStyle("-fx-background-color: blue, white;") }
为此 运行 你需要 import scala.collection.JavaConverters._
用于从 java 集到 Scala 集的转换。
也可以使来自一个数据集的所有数据点不可见,例如:
lineChart.lookupAll(".default-color1.chart-line-symbol").asScala foreach { node => node.setVisible(false) }
说这是一个很好的解决方案未免有些言过其实。
它有一个很大的缺点,即在将新数据点添加到 LineChartBuffer 中的一个系列后,您必须重新着色或重新格式化每个符号。如果不这样做,新符号将具有标准颜色和设置。
线条保留,重新着色,我不能说为什么。
但好的一面是,之后总是可以像这样重新格式化折线图中的曲线!
我想制作一个线性回归程序,将数据可视化给用户。我使用 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
}
}
}
这肯定没有像它可能的那样得到很好的记录......:-(
样式表通常放在项目的资源目录中。如果您使用 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) 路径定义自定义形状的更多信息,请参阅 relevant section of the SVG specification.)
要让 ScalaFX (JavaFX) 使用此样式表,您有多种选择。要让它们在全球范围内应用,请将其添加到主场景(这是我在下面所做的)。或者,如果每个图表需要不同的样式,您可以向特定图表添加不同的样式表。 (顺便说一句,我还添加了标准 include import,因为这可以防止许多 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 Reference Guide。
结果如下所示:
我几乎不敢在 Mike Allens 解决方案中添加一些东西(一如既往地非常好),但这对我来说没有用,因为我无法让我的 scala 找到 and/or处理 .css 文件。 如果可能的话,我会这样做,但我就是无法让它工作。 这是我想出的:
假设我有一些数据要显示:
val xyExampleData: ObservableBuffer[(Double, Double)] = ObservableBuffer(Seq(
1 -> 1,
2 -> 4,
3 -> 9))
然后我将其转换为折线图的系列:
val DataPoints = ObservableBuffer(xyExampleData map { case (x, y) => XYChart.Data[Number, Number](x, y) })
val PointsToDisplay = XYChart.Series[Number, Number]("Points", DataPoints)
现在我再次将其放入缓冲区,可能还有来自不同系列的其他数据。
val lineChartBuffer = ObservableBuffer(PointsToDisplay, ...)
最后我创建了我的折线图,我称之为(缺乏创造力)折线图:
val lineChart = new LineChart(xAxis, yAxis, lineChartBuffer) {...}
数据点之间的线现在可以很容易地重新着色:
lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;")
这将更改 LineChartBuffer 中第一个数据集的线条颜色。 如果您想更改第二个行属性,请调用
lineChart.lookup(".default-color1.chart-series-line")...
还有“-fx-stroke-width: 3px;”设置行的with。
“-fx-不透明度:0.1;”
"-fx-stroke-dash-array: 10;"
-fx-填充:蓝色;
也很有用,但不要重复调用上面的行,因为第二次调用会覆盖第一次。 而是将所有字符串连接成一个:
lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;-fx-opacity: 0.1;-fx-stroke-dash-array: 10;-fx-fill: blue;")
现在每个数据点的符号格式:
不幸的是,除了单独格式化每个符号之外似乎没有其他方法:
lineChart.lookupAll(".default-color0.chart-line-symbol").asScala foreach { node => node.setStyle("-fx-background-color: blue, white;") }
为此 运行 你需要 import scala.collection.JavaConverters._
用于从 java 集到 Scala 集的转换。
也可以使来自一个数据集的所有数据点不可见,例如:
lineChart.lookupAll(".default-color1.chart-line-symbol").asScala foreach { node => node.setVisible(false) }
说这是一个很好的解决方案未免有些言过其实。 它有一个很大的缺点,即在将新数据点添加到 LineChartBuffer 中的一个系列后,您必须重新着色或重新格式化每个符号。如果不这样做,新符号将具有标准颜色和设置。 线条保留,重新着色,我不能说为什么。
但好的一面是,之后总是可以像这样重新格式化折线图中的曲线!