new numass viewer components

This commit is contained in:
Alexander Nozik 2017-10-18 22:03:04 +03:00
parent 755a901d2e
commit 4904825bf2
8 changed files with 511 additions and 83 deletions

View File

@ -151,6 +151,7 @@ public class TimeAnalyzer extends AbstractAnalyzer {
} }
lastEvent.set(event); lastEvent.set(event);
//TODO remove autoboxing somehow
return new Pair<>(event, res); return new Pair<>(event, res);
}); });
} }

View File

@ -0,0 +1,123 @@
package inr.numass.viewer
import hep.dataforge.kodex.configure
import hep.dataforge.kodex.fx.dfIcon
import hep.dataforge.kodex.fx.plots.PlotContainer
import hep.dataforge.meta.Meta
import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Table
import hep.dataforge.tables.XYAdapter
import inr.numass.data.NumassDataUtils
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.api.NumassAnalyzer
import inr.numass.data.api.NumassPoint
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.scene.control.CheckBox
import javafx.scene.control.ChoiceBox
import javafx.scene.image.ImageView
import tornadofx.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
class AmplitudeView(
private val analyzer: NumassAnalyzer = SimpleAnalyzer(),
private val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
) : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure {
"title" to "Detector response plot"
node("xAxis") {
"axisTitle" to "ADC"
"axisUnits" to "channels"
}
node("yAxis") {
"axisTitle" to "count rate"
"axisUnits" to "Hz"
}
"legend.show" to false
}
val binningProperty = SimpleObjectProperty<Int>(20)
var binning by binningProperty
val normalizeProperty = SimpleBooleanProperty(true)
var normalize by normalizeProperty
private val container = PlotContainer(frame).apply {
val binnintSelector: ChoiceBox<Int> = ChoiceBox(FXCollections.observableArrayList(1, 2, 5, 10, 20, 50)).apply {
minWidth = 0.0
selectionModel.selectLast()
binningProperty.bind(this.selectionModel.selectedItemProperty())
}
val normalizeSwitch: CheckBox = CheckBox("Normalize").apply {
minWidth = 0.0
this.selectedProperty().bindBidirectional(normalizeProperty)
}
addToSideBar(0, binnintSelector, normalizeSwitch)
}
private val data: MutableMap<String, NumassPoint> = HashMap();
override val root = borderpane {
center = container.root
}
private fun getSpectrum(point: NumassPoint): Table {
return cache.computeIfAbsent(point) { analyzer.getSpectrum(point, Meta.empty()) }
}
private fun updateView() {
val valueAxis = if (normalize) {
NumassAnalyzer.COUNT_RATE_KEY
} else {
NumassAnalyzer.COUNT_KEY
}
val progress = AtomicInteger(0);
runLater { container.progress = 0.0 }
runAsync {
val totalCount = data.size
data.map { entry ->
val seriesName = String.format("%s: %.2f", entry.key, entry.value.voltage)
DataPlot.plot(
seriesName,
XYAdapter(NumassAnalyzer.CHANNEL_KEY, valueAxis),
NumassDataUtils.spectrumWithBinning(getSpectrum(entry.value), binning)
).configure {
"connectionType" to "step"
"thickness" to 2
"showLine" to true
"showSymbol" to false
"showErrors" to false
"JFreeChart.cache" to true
}.also {
runLater { container.progress = progress.incrementAndGet().toDouble() / data.size }
}
}
} ui { plots ->
frame.setAll(plots)
//detectorDataExportButton.isDisable = false
}
}
fun update(map: Map<String, NumassPoint>) {
synchronized(data) {
//Remove obsolete keys
data.keys.filter { !map.containsKey(it) }.forEach {
data.remove(it)
frame.remove(it);
}
this.data.putAll(map);
updateView()
}
}
}

View File

@ -0,0 +1,66 @@
package inr.numass.viewer
import hep.dataforge.kodex.configure
import hep.dataforge.kodex.fx.dfIcon
import hep.dataforge.kodex.fx.plots.PlotContainer
import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.data.TimePlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame
import inr.numass.data.api.NumassSet
import javafx.scene.image.ImageView
import tornadofx.*
import java.util.concurrent.atomic.AtomicInteger
/**
* View for hv
*/
class HVView : View(title = "High voltage time plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure {
"xAxis.axisTitle" to "time"
"xAxis.type" to "time"
"yAxis.axisTitle" to "HV"
}
private val container = PlotContainer(frame);
override val root = borderpane {
center = PlotContainer(frame).root
}
fun update(vararg sets: NumassSet) {
frame.plots.clear()
container.sideBarExpanded = false
val progress = AtomicInteger(0);
runLater { container.progress = -1.0 }
sets.forEach { data ->
runAsync {
val res = data.hvData
runLater { container.progress = progress.incrementAndGet().toDouble() / sets.size }
res
} ui { hvData ->
hvData.ifPresent {
for (dp in it) {
val blockName = dp.getString("block", "default").replace(".", "_");
//val opt = frame.opt(blockName)
val plot = frame.opt(blockName).orElseGet {
TimePlot(blockName).configure {
"connectionType" to "step"
"thickness" to 2
"showLine" to true
"showSymbol" to false
"showErrors" to false
}
.apply { frame.add(this) }
} as TimePlot;
plot.put(dp.getValue("timestamp").timeValue(), dp.getValue("value"))
}
}
container.progress = 1.0;
}
}
}
}

View File

@ -7,10 +7,10 @@ import hep.dataforge.kodex.buildMeta
import hep.dataforge.kodex.fx.plots.PlotContainer import hep.dataforge.kodex.fx.plots.PlotContainer
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.plots.PlotGroup
import hep.dataforge.plots.XYPlotFrame import hep.dataforge.plots.XYPlotFrame
import hep.dataforge.plots.data.DataPlot import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.data.PlotDataUtils import hep.dataforge.plots.data.DataPlotUtils
import hep.dataforge.plots.data.PlottableGroup
import hep.dataforge.plots.data.TimePlot import hep.dataforge.plots.data.TimePlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.storage.commons.JSONMetaWriter import hep.dataforge.storage.commons.JSONMetaWriter
@ -45,7 +45,7 @@ import java.util.concurrent.atomic.AtomicInteger
import java.util.logging.Level import java.util.logging.Level
import java.util.stream.Collectors import java.util.stream.Collectors
@Suppress("UNNECESSARY_LATEINIT")
/** /**
* Numass loader view * Numass loader view
* *
@ -84,7 +84,7 @@ class NumassLoaderView : View() {
private val spectra = HashMap<Double, Table>();//spectra cache private val spectra = HashMap<Double, Table>();//spectra cache
val spectrumData = DataPlot("spectrum") val spectrumData = DataPlot("spectrum")
val hvPlotData = PlottableGroup<TimePlot>() val hvPlotData = PlotGroup("hv")
//private var points = FXCollections.observableArrayList<NumassPoint>() //private var points = FXCollections.observableArrayList<NumassPoint>()
val detectorPlotFrame = JFreeChartFrame( val detectorPlotFrame = JFreeChartFrame(
@ -224,7 +224,7 @@ class NumassLoaderView : View() {
} }
} else { } else {
spectrumData.clear() spectrumData.clear()
hvPlotData.forEach { it.clear() } hvPlotData.clear()
} }
} }
@ -246,7 +246,7 @@ class NumassLoaderView : View() {
} }
private fun updateHV(data: NumassSet) { private fun updateHV(data: NumassSet) {
hvPlotData.forEach { it.clear() } hvPlotData.clear()
runAsync { runAsync {
data.hvData data.hvData
} ui { hvData -> } ui { hvData ->
@ -256,9 +256,10 @@ class NumassLoaderView : View() {
if (!hvPlotData.has(block)) { if (!hvPlotData.has(block)) {
hvPlotData.add(TimePlot(block)) hvPlotData.add(TimePlot(block))
} }
hvPlotData.get(block).put(dp.getValue("timestamp").timeValue(), dp.getValue("value")) (hvPlotData.opt(block).orElseThrow{RuntimeException()} as TimePlot)
.put(dp.getValue("timestamp").timeValue(), dp.getValue("value"))
} }
hvPlot.plot.addAll(hvPlotData) hvPlot.plot.add(hvPlotData)
} }
} }
@ -400,7 +401,7 @@ class NumassLoaderView : View() {
fileChooser.initialFileName = data!!.name + "_detector.out" fileChooser.initialFileName = data!!.name + "_detector.out"
val destination = fileChooser.showSaveDialog(detectorPlotPane.scene.window) val destination = fileChooser.showSaveDialog(detectorPlotPane.scene.window)
if (destination != null) { if (destination != null) {
val detectorData = PlotDataUtils.collectXYDataFromPlot(detectorPlot.plot as XYPlotFrame, true) val detectorData = DataPlotUtils.collectXYDataFromPlot(detectorPlot.plot as XYPlotFrame, true)
try { try {
ColumnedDataWriter.writeTable( ColumnedDataWriter.writeTable(
destination, destination,

View File

@ -1,7 +1,7 @@
package inr.numass.viewer package inr.numass.viewer
import hep.dataforge.kodex.buildMeta
import hep.dataforge.kodex.configure import hep.dataforge.kodex.configure
import hep.dataforge.kodex.fx.dfIcon
import hep.dataforge.kodex.fx.plots.PlotContainer import hep.dataforge.kodex.fx.plots.PlotContainer
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.plots.Plot import hep.dataforge.plots.Plot
@ -13,23 +13,24 @@ import hep.dataforge.tables.ListTable
import hep.dataforge.tables.Table import hep.dataforge.tables.Table
import hep.dataforge.tables.XYAdapter import hep.dataforge.tables.XYAdapter
import hep.dataforge.values.Values import hep.dataforge.values.Values
import javafx.scene.image.ImageView
import tornadofx.* import tornadofx.*
/** /**
* Created by darksnake on 18.06.2017. * Created by darksnake on 18.06.2017.
*/ */
class SlowControlView : View("My View") { class SlowControlView : View(title = "Numass slow control view", icon = ImageView(dfIcon)) {
private val plotMeta = buildMeta("plot") {
private val plot = JFreeChartFrame().configure {
"xAxis.type" to "time" "xAxis.type" to "time"
"yAxis.type" to "log" "yAxis.type" to "log"
} }
val plot = JFreeChartFrame(plotMeta)
override val root = borderpane { override val root = borderpane {
center = PlotContainer(plot).root center = PlotContainer(plot).root
} }
//TODO add multiple loaders
fun load(loader: PointLoader) { fun load(loader: PointLoader) {
runAsync { runAsync {
val data = getData(loader) val data = getData(loader)

View File

@ -0,0 +1,177 @@
package inr.numass.viewer
import hep.dataforge.kodex.configure
import hep.dataforge.kodex.fx.dfIcon
import hep.dataforge.kodex.fx.plots.PlotContainer
import hep.dataforge.meta.Meta
import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Table
import hep.dataforge.tables.XYAdapter
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.api.NumassAnalyzer
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import javafx.beans.property.SimpleIntegerProperty
import javafx.geometry.Orientation
import javafx.scene.image.ImageView
import javafx.util.converter.IntegerStringConverter
import org.controlsfx.control.RangeSlider
import tornadofx.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
import java.util.stream.Collectors
/**
* View for energy spectrum
* @param analyzer
* @param cache - optional global point cache
*/
class SpectrumView(
val analyzer: NumassAnalyzer = SimpleAnalyzer(),
val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
) : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure {
"xAxis.axisTitle" to "U"
"xAxis.axisUnits" to "V"
"yAxis.axisTitle" to "count rate"
"yAxis.axisUnits" to "Hz"
//"legend.show" to false
}
private val container = PlotContainer(frame);
private val loChannelProperty = SimpleIntegerProperty(0).apply {
addListener { _ -> updateView() }
}
private var loChannel by loChannelProperty
private val upChannelProperty = SimpleIntegerProperty(4000).apply {
addListener { _ -> updateView() }
}
private var upChannel by upChannelProperty
private val data: MutableMap<String, NumassSet> = HashMap()
/*
<BorderPane fx:id="spectrumPlotPane" prefHeight="200.0" prefWidth="200.0"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<VBox>
<Label text="Lo channel"/>
<TextField fx:id="lowChannelField" prefWidth="60.0"/>
</VBox>
<RangeSlider fx:id="channelSlider" accessibleRole="SLIDER"
highValue="1900.0" lowValue="300.0" majorTickUnit="500.0"
max="4000.0" minorTickCount="5" prefHeight="38.0"
prefWidth="276.0" showTickLabels="true" showTickMarks="true">
<padding>
<Insets left="10.0" right="10.0"/>
</padding>
</RangeSlider>
<VBox>
<Label text="Up channel"/>
<TextField fx:id="upChannelField" prefWidth="60.0"/>
</VBox>
<Separator orientation="VERTICAL"/>
<VBox>
<Label text="Dead time (us)"/>
<TextField fx:id="dTimeField" prefHeight="25.0" prefWidth="0.0"
text="7.2"/>
</VBox>
<Separator orientation="VERTICAL"/>
<Pane minWidth="0.0" HBox.hgrow="ALWAYS"/>
<Button fx:id="spectrumExportButton" mnemonicParsing="false" text="Export"/>
</ToolBar>
</top>
</BorderPane>
*/
override val root = borderpane {
top {
toolbar {
prefHeight = 40.0
vbox {
label("Lo channel")
textfield {
textProperty().bindBidirectional(loChannelProperty.asObject(), IntegerStringConverter())
}
}
items += RangeSlider().apply {
highValueProperty().bindBidirectional(upChannelProperty)
lowValueProperty().bindBidirectional(loChannelProperty)
majorTickUnit = 500.0
max = 4000.0
minorTickCount = 5
prefHeight = 38.0
isShowTickLabels = true
isShowTickMarks = true
}
vbox {
label("Up channel")
textfield {
textProperty().bindBidirectional(upChannelProperty.asObject(), IntegerStringConverter())
}
}
separator(Orientation.VERTICAL)
}
}
center = container.root
}
private fun getSpectrum(point: NumassPoint): Table {
return cache.computeIfAbsent(point) { analyzer.getSpectrum(point, Meta.empty()) }
}
private fun updateView() {
runLater { container.progress = 0.0 }
val progress = AtomicInteger(0)
val totalProgress = data.values.stream().mapToLong() { it.points.count() }.sum()
data.forEach { name, set ->
val plot = frame.opt(name).orElseGet {
DataPlot(name).apply {
frame.add(this)
}
} as DataPlot
runAsync {
set.points.map { point ->
val count = NumassAnalyzer.countInWindow(getSpectrum(point), loChannel.toShort(), upChannel.toShort());
val seconds = point.length.toMillis() / 1000.0;
runLater {
container.progress = progress.incrementAndGet().toDouble() / totalProgress
}
XYAdapter.DEFAULT_ADAPTER.buildXYDataPoint(
point.voltage,
(count / seconds),
Math.sqrt(count.toDouble()) / seconds
)
}.collect(Collectors.toList())
} ui { points ->
plot.fillData(points)
container.progress = 1.0
//spectrumExportButton.isDisable = false
}
}
}
fun update(map: Map<String, NumassSet>) {
synchronized(data) {
//Remove obsolete keys
data.keys.filter { !map.containsKey(it) }.forEach {
data.remove(it)
frame.remove(it);
}
this.data.putAll(map.mapValues { NumassDataCache(it.value) });
updateView()
}
}
}

View File

@ -0,0 +1,71 @@
package inr.numass.viewer.test
import hep.dataforge.kodex.fx.dfIcon
import hep.dataforge.tables.Table
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import inr.numass.data.storage.NumassStorageFactory
import inr.numass.viewer.AmplitudeView
import inr.numass.viewer.HVView
import inr.numass.viewer.SpectrumView
import javafx.application.Application
import javafx.scene.image.ImageView
import tornadofx.*
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import java.util.stream.Collectors
class ViewerTestApp : App(ViewerTest::class)
class ViewerTest : View(title = "Numass viewer test", icon = ImageView(dfIcon)) {
//val rootDir = File("D:\\Work\\Numass\\data\\2017_05\\Fill_2")
//val set: NumassSet = NumassStorageFactory.buildLocal(rootDir).provide("loader::set_8", NumassSet::class.java).orElseThrow { RuntimeException("err") }
private val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap();
val amp = AmplitudeView(cache = cache)
val sp = SpectrumView(cache = cache)
val hv = HVView()
override val root = borderpane {
top {
button("Click me!") {
action {
runAsync {
val rootDir = File("D:\\Work\\Numass\\data\\2017_05\\Fill_2")
val set: NumassSet = NumassStorageFactory.buildLocal(rootDir).provide("loader::set_3", NumassSet::class.java)
.orElseThrow { RuntimeException("err") }
update(set);
}
}
}
}
center {
tabpane {
tab("amplitude") {
content = amp.root
}
tab("spectrum") {
content = sp.root
}
tab("hv") {
content = hv.root
}
}
}
}
fun update(set: NumassSet) {
amp.update(set.points.filter { it.voltage != 16000.0 }.collect(Collectors.toMap({ "point_${it.voltage}" }, { it })));
sp.update(mapOf("test" to set));
hv.update(set)
}
}
fun main(args: Array<String>) {
Application.launch(ViewerTestApp::class.java, *args);
}

View File

@ -20,73 +20,61 @@ limitations under the License.
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import org.controlsfx.control.RangeSlider?> <?import org.controlsfx.control.RangeSlider?>
<AnchorPane id="AnchorPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"> <AnchorPane id="AnchorPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"
<children> xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<TabPane fx:id="tabPane" layoutX="200.0" layoutY="100.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <TabPane fx:id="tabPane" layoutX="200.0" layoutY="100.0" minHeight="-Infinity" minWidth="-Infinity"
<tabs> prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0"
<Tab text="Info"> AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<content> <Tab text="Info">
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children> <TextArea fx:id="infoTextBox" layoutY="30.0" prefHeight="200.0" prefWidth="200.0"
<TextArea fx:id="infoTextBox" layoutY="30.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="30.0" /> AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
</children> AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="30.0"/>
</AnchorPane> </AnchorPane>
</content> </Tab>
</Tab> <Tab fx:id="detectorTab" text="Detector">
<Tab fx:id="detectorTab" text="Detector"> <BorderPane fx:id="detectorPlotPane" minHeight="0.0" minWidth="0.0" prefHeight="180.0"
<content> prefWidth="200.0"/>
<BorderPane fx:id="detectorPlotPane" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" /> </Tab>
</content> <Tab fx:id="hvTab" text="HV">
</Tab> <BorderPane fx:id="hvPane" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"/>
<Tab fx:id="hvTab" text="HV"> </Tab>
<content> <Tab fx:id="spectrumTab" text="Spectrum">
<BorderPane fx:id="hvPane" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" /> <AnchorPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0">
</content> <BorderPane fx:id="spectrumPlotPane" prefHeight="200.0" prefWidth="200.0"
</Tab> AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
<Tab fx:id="spectrumTab" text="Spectrum"> AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<content> <top>
<AnchorPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"> <ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children> <VBox>
<BorderPane fx:id="spectrumPlotPane" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <Label text="Lo channel"/>
<top> <TextField fx:id="lowChannelField" prefWidth="60.0"/>
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER"> </VBox>
<items> <RangeSlider fx:id="channelSlider" accessibleRole="SLIDER"
<VBox> highValue="1900.0" lowValue="300.0" majorTickUnit="500.0"
<children> max="4000.0" minorTickCount="5" prefHeight="38.0"
<Label text="Lo channel" /> prefWidth="276.0" showTickLabels="true" showTickMarks="true">
<TextField fx:id="lowChannelField" prefWidth="60.0" /> <padding>
</children> <Insets left="10.0" right="10.0"/>
</VBox> </padding>
<RangeSlider fx:id="channelSlider" accessibleRole="SLIDER" highValue="1900.0" lowValue="300.0" majorTickUnit="500.0" max="4000.0" minorTickCount="5" prefHeight="38.0" prefWidth="276.0" showTickLabels="true" showTickMarks="true"> </RangeSlider>
<padding> <VBox>
<Insets left="10.0" right="10.0" /> <Label text="Up channel"/>
</padding> <TextField fx:id="upChannelField" prefWidth="60.0"/>
</RangeSlider> </VBox>
<VBox> <Separator orientation="VERTICAL"/>
<children> <VBox>
<Label text="Up channel" /> <Label text="Dead time (us)"/>
<TextField fx:id="upChannelField" prefWidth="60.0" /> <TextField fx:id="dTimeField" prefHeight="25.0" prefWidth="0.0"
</children> text="7.2"/>
</VBox> </VBox>
<Separator orientation="VERTICAL" /> <Separator orientation="VERTICAL"/>
<VBox> <Pane minWidth="0.0" HBox.hgrow="ALWAYS"/>
<children> <Button fx:id="spectrumExportButton" mnemonicParsing="false" text="Export"/>
<Label text="Dead time (us)" /> </ToolBar>
<TextField fx:id="dTimeField" prefHeight="25.0" prefWidth="0.0" text="7.2" /> </top>
</children> </BorderPane>
</VBox> </AnchorPane>
<Separator orientation="VERTICAL" /> </Tab>
<Pane minWidth="0.0" HBox.hgrow="ALWAYS" /> </TabPane>
<Button fx:id="spectrumExportButton" mnemonicParsing="false" text="Export" />
</items>
</ToolBar>
</top>
</BorderPane>
</children>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane> </AnchorPane>