Java tutorial
/* * Copyright (C) 2016 Arne Weigenand * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package view; import controller.FXBatchController; import controller.FXScatterPlot; import controller.FXEvaluationWindowController; import controller.FXHypnogrammController; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.ResourceBundle; import model.FXViewModel; import model.DataModel; import model.FeatureModel; import controller.DataController; import controller.FeatureController; import controller.ModelReaderWriterController; import controller.ClassificationController; import gnu.trove.iterator.TIntIterator; import gnu.trove.list.array.TIntArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Set; import java.util.logging.*; import javafx.application.Platform; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Scene; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.ProgressBar; import javafx.scene.control.TextField; import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.Line; import javafx.scene.shape.Rectangle; import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.stage.WindowEvent; import org.apache.commons.math.stat.StatUtils; import tools.Signal; import tools.Util; import tools.KCdetection; public final class FXApplicationController implements Initializable { private File projectFile = null; private String classifier; private ArrayList<String> classifierList = new ArrayList(); private final KCdetection kcDetector; private FXHypnogrammController hypnogram; private FXEvaluationWindowController evaluationWindow; private final FXScatterPlot scatterPlot; private final float[][] displayBuffer; private FXPopUp popUp = new FXPopUp(); private final FXViewModel viewModel; private final DataController dataController; private final DataModel dataModel; private final FeatureModel featureModel; private final FeatureController featureController; private FXElectrodeConfiguratorController electrodeConfigurator; private final boolean recreateModelMode; private int currentEpoch = 0; private final String[] channelNames; private HashMap<String, Double[]> allChannels = new HashMap(); TIntArrayList activeChannels = new TIntArrayList(); private LinkedList<Line> lines = new LinkedList(); private Stage primaryStage; private BorderPane mainGrid; final private Scene scene; private DoubleProperty space = new SimpleDoubleProperty(1.); private DoubleProperty scale = new SimpleDoubleProperty(1e-1); private DoubleProperty mouseX = new SimpleDoubleProperty(0.); private DoubleProperty mouseY = new SimpleDoubleProperty(0.); private boolean recompute = false; private boolean help1Flag = false; private boolean kComplexFlag = false; ObservableList<String> choices; @FXML private ToggleButton awakeButton; @FXML private ToggleButton s1Button; @FXML private ToggleButton s2Button; @FXML private ToggleButton s3Button; @FXML private ToggleButton remButton; @FXML private ToggleButton artefactButton; @FXML private ToggleButton arousalButton; @FXML private ToggleButton stimulationButton; @FXML private Button clearButton; @FXML private ToggleButton help1; @FXML private ToggleButton kComplex; @FXML private ToggleButton electrodeConfiguratorButton; @FXML private ToggleButton classifyButton; @FXML private ToggleButton visualizeButton; @FXML private ToggleButton kcMarkersButton; @FXML private ToggleButton dcRemoveButton; @FXML private ToggleButton filterButton; @FXML private ToggleButton hypnogramButton; @FXML private Label statusBarLabel1; @FXML private Label statusBarLabel2; @FXML private Label kComplexLabel; @FXML private Label infoLabel; @FXML private ProgressBar progressBar; @FXML private TextField toolBarGoto; @FXML private GridPane statusBarGrid; @FXML private Pane overlay; @FXML private Pane overlay2; @FXML private Pane overlay3; @FXML private Pane overlay4; @FXML private StackPane stackPane; @FXML private HBox statusBarHBox; @FXML private MenuItem showAdtVisualization; @FXML private LineChart<Number, Number> lineChart; @FXML private NumberAxis yAxis; @FXML private NumberAxis xAxis; @FXML private Line line1; @FXML private Line line2; @FXML ChoiceBox<String> choiceBox; @FXML ChoiceBox<String> choiceBoxModel; public FXApplicationController(DataController dataController, FeatureModel featureModel, FXViewModel viewModel, boolean recreateModelMode) { primaryStage = new Stage(); // Creating FXML Loader FXMLLoader loader = new FXMLLoader(FXStartController.class.getResource("Application.fxml")); loader.setController(this); // Try to load fxml file try { mainGrid = loader.load(); } catch (IOException e) { Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "Error during loading Application.fxml file!", e); } this.dataController = dataController; this.dataModel = dataController.getDataModel(); //initialize important variables channelNames = dataModel.getChannelNames(); displayBuffer = dataModel.data.clone(); // Set Choice Box for the channels //Set properties for the channels for (int i = 0; i < channelNames.length; i++) { if (i < 6) { //The first value represents wheater the channel is shown //The second value represents the current zoom level Double[] channelProp = new Double[2]; channelProp[0] = 1.0; channelProp[1] = 1.0; allChannels.put(channelNames[i], channelProp); } else { //The first value represents wheater the channel is shown //The second value represents the current zoom level Double[] channelProp = new Double[2]; channelProp[0] = 0.0; channelProp[1] = 1.0; allChannels.put(channelNames[i], channelProp); } } // Create stage with mainGrid scene = new Scene(mainGrid); primaryStage.setScene(scene); //Properties for stage primaryStage.setResizable(true); primaryStage.show(); primaryStage.setTitle(dataModel.getFile().getName()); //////////// this.viewModel = viewModel; this.featureModel = featureModel; this.featureController = new FeatureController(featureModel, dataModel); this.recreateModelMode = recreateModelMode; kcDetector = featureController.kcDetector; kcDetector.setHighpassCoefficients(featureController.getDisplayHighpassCoefficients()); kcDetector.setLowpassCoefficients(featureController.getLowpassCoefficients()); currentEpoch = featureModel.getCurrentEpoch(); loadEpoch(currentEpoch); showEpoch(); paintSpacing(); //Configure lineChart lineChart.setSnapToPixel(false); choices = FXCollections.observableArrayList(); updateChoiceBox(); choiceBox.getSelectionModel().select(0); ObservableList<String> choicesModel = FXCollections.observableArrayList(); File folder = new File("./Classifiers").getAbsoluteFile(); for (File file : folder.listFiles()) { if (file.getName().contains("model")) { choicesModel.add(file.getName().replace("[model]", "").replace(".jo", "")); classifierList.add(file.getAbsolutePath()); } } choiceBoxModel.setItems(choicesModel); choiceBoxModel.getSelectionModel().select(0); tooltips(); scatterPlot = new FXScatterPlot(this, dataController, dataModel, featureModel, featureController, viewModel); hypnogram = new FXHypnogrammController(dataModel, featureModel, viewModel, this); Platform.runLater(new Runnable() { @Override public void run() { if (recreateModelMode) { hypnogram.updateAll(); hypnogram.hide(); } updateWindows(); } }); if (((int) (dataModel.getSrate()) % 50) != 0) { showPopUp("Sampling rate not supported. Must be a multiple of 50 Hz and > 100 Hz."); } } @Override public void initialize(URL arg0, ResourceBundle arg1) { System.out.println("initialize called"); overlay3.addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouse) { mouseX.set(mouse.getX()); mouseY.set(mouse.getY()); if (kComplexFlag) { if (mouse.getEventType() == MouseEvent.MOUSE_PRESSED) { Line line = new Line(); line.setStyle("-fx-stroke: red;"); overlay3.getChildren().add(line); line.setStartX(0); line.setStartY(0); line.setLayoutX(mouseX.get()); line.setLayoutY(mouseY.get()); lines.add(line); } if (mouse.isPrimaryButtonDown() && (!lines.isEmpty())) { Line line = lines.getLast(); double endXPos = mouseX.get() - line.getLayoutX(); double endYPos = mouseY.get() - line.getLayoutY(); line.setEndX(endXPos); line.setEndY(endYPos); calculatePercentageKComplex(); } } if (help1Flag) { double offsetSize = 1. / (activeChannels.size() + 1.); double posOnOverlay = mouse.getY(); double zoom = 1.0; for (int i = 0; i < activeChannels.size(); i++) { double realOffset = (1 - (i + 1.) * offsetSize); double upperBound = yAxis.getDisplayPosition( (realOffset - offsetSize / 2.) * yAxis.getUpperBound()) + yAxis.getLayoutY(); double lowerBound = yAxis.getDisplayPosition( (realOffset + offsetSize / 2.) * yAxis.getUpperBound()) + yAxis.getLayoutY(); if ((posOnOverlay <= upperBound) && (posOnOverlay > lowerBound)) { zoom = getZoomFromChannel(activeChannels.get(i)); } } space.set(75.0 * zoom / yAxis.getUpperBound() * yAxis.getHeight()); line1.setVisible(true); line2.setVisible(true); } else { line1.setVisible(false); line2.setVisible(false); } } }); //Key Listener lineChart.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() { @Override public void handle(KeyEvent ke) { keyAction(ke); } }); toolBarGoto.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() { @Override public void handle(KeyEvent ke) { if (ke.getCode() == KeyCode.ENTER) { int valueTextField = -1; try { valueTextField = Integer.parseInt(toolBarGoto.getText()); } catch (NumberFormatException e) { toolBarGoto.setText((currentEpoch + 1) + ""); valueTextField = currentEpoch + 1; Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Error during handling enter key.", e); } if (valueTextField > dataModel.getNumberOf30sEpochs()) { valueTextField = dataModel.getNumberOf30sEpochs(); } else if (valueTextField < 1) { valueTextField = 1; } currentEpoch = valueTextField - 1; goToEpoch(currentEpoch); if (kComplexFlag) { calculatePercentageKComplex(); } } } }); choiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { System.out.println(newValue); if (newValue != null) { featureModel.setFeatureChannel(Arrays.asList(channelNames).indexOf(newValue)); System.out.println("########" + Arrays.asList(channelNames).indexOf(newValue)); System.out.println(featureModel.getFeatureChannel()); featureModel.setTsneComputed(false); featureModel.setFeaturesComputed(false); featureModel.setClassificationDone(false); featureModel.setReadinDone(false); classifyButton.setDisable(false); recompute = true; if (viewModel.isKcMarkersActive()) { computeKCfeatures(); } else { overlay4.getChildren().clear(); } } } }); choiceBoxModel.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { classifier = classifierList.get(newValue.intValue()); System.out.println(classifier); featureModel.setClassificationDone(false); featureModel.setReadinDone(false); classifyButton.setDisable(false); recompute = true; } }); primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent event) { System.out.println("RandomAccessFile closed"); dataModel.getReader().close(); Platform.exit(); } }); kComplexOnAction(); help1OnAction(); } @SuppressWarnings("static-access") private void updateProbabilities() { double[] probabilities = featureModel.getPredictProbabilities(currentEpoch); if (probabilities != null) { double total = StatUtils.sum(probabilities); int wake = (int) Math.round((probabilities[0] / total * 100)); int n1 = (int) Math.round((probabilities[1] / total * 100)); int n2 = (int) Math.round((probabilities[2] / total * 100)); int n3 = (int) Math.round((probabilities[3] / total * 100)); int rem = (int) Math.round((probabilities[4] / total * 100)); String print = "W: " + wake + "% N1: " + n1 + "% N2: " + n2 + "% N3: " + n3 + "% REM: " + rem + "%"; statusBarLabel2.setText(print); statusBarHBox.setHgrow(statusBarLabel2, Priority.ALWAYS); } else { statusBarLabel2.setText("Manual Mode"); } } @FXML protected void help1OnAction() { if (help1Flag) { help1Flag = false; line1.setVisible(false); line2.setVisible(false); if (help1.isSelected()) { help1.setSelected(false); } } else { help1Flag = true; line1.setVisible(true); line2.setVisible(true); if (!help1.isSelected()) { help1.setSelected(true); } } lineChart.requestFocus(); } @FXML protected void kComplexOnAction() { if (kComplexFlag) { kComplexFlag = false; overlay3.getChildren().clear(); lines.clear(); if (kComplex.isSelected()) { kComplex.setSelected(false); } } else { kComplexFlag = true; kComplexLabel.setVisible(true); if (!kComplex.isSelected()) { kComplex.setSelected(true); } } lineChart.requestFocus(); } private void calculatePercentageKComplex() { double percentageSum = 0.0; RangeSet<Double> rangeset = TreeRangeSet.create(); for (int i = 0; i < lines.size(); i++) { Line line = lines.get(i); double lengthOfLine; Range r = Range.closed( Math.min(line.getLayoutX(), line.getEndX() + line.getLayoutX()) / xAxis.getWidth() * 100. - 1e-9, Math.max(line.getLayoutX(), line.getEndX() + line.getLayoutX()) / xAxis.getWidth() * 100. + 1e-9); rangeset.add(r); } percentageSum = rangeset.asRanges().stream().mapToDouble(e -> (e.upperEndpoint() - e.lowerEndpoint())) .sum(); kComplexLabel.setText("K-Complex: " + Math.round(percentageSum) + "%"); } @FXML protected void help1MenuItemOnAction() { help1OnAction(); } @FXML protected void kComplexMenuItemOnAction() { kComplexOnAction(); } @FXML protected void closeAction() { System.exit(0); } @FXML protected void aboutAction() { Stage stage = new Stage(); AnchorPane addGrid = new AnchorPane(); // Creating FXML Loader FXMLLoader loader = new FXMLLoader(FXStartController.class.getResource("About.fxml")); loader.setController(this); // Try to load fxml file try { addGrid = loader.load(); } catch (IOException e) { System.err.println("Error during loading About.fxml file!"); } Scene scene = new Scene(addGrid); stage.setResizable(false); stage.setScene(scene); stage.show(); stage.setTitle("About"); } @FXML protected void importHypnogrammAction() { FileChooser fileChooser = new FileChooser(); // Set extension filter FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt"); fileChooser.getExtensionFilters().add(extFilter); // Show open file dialog final File file = fileChooser.showOpenDialog(primaryStage); if (file != null) { try { openFile(file); hypnogram.updateAll(); updateWindows(); System.out.println("Finished importing Hypnogramm!"); } catch (IOException e) { popUp.createPopup("Error during importing Hypnogramm!"); Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "Error during importing Hypnogramm!", e); } } } @FXML protected void exportHypnogrammAction() { FileChooser fileChooser = new FileChooser(); //Open directory from existing directory File dir = null; dir = tools.Util.loadDir(new File( new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile(), "LastDirectory.txt")); if (dir != null) { fileChooser.setInitialDirectory(dir); } //Set extension filter FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt"); fileChooser.getExtensionFilters().add(extFilter); //Show save file dialog File file = fileChooser.showSaveDialog(primaryStage); Util.saveDir(file, new File( new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile(), "LastDirectory.txt")); if (file != null) { featureController.saveFile(file); } } @FXML protected void saveAsAction() { FileChooser fileChooser = new FileChooser(); //Open directory from existing directory File dir = null; dir = tools.Util.loadDir(new File( new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile(), "LastDirectory.txt")); if (dir != null) { fileChooser.setInitialDirectory(dir); } //Set extension filter FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("AS files (*.as)", "*.as"); fileChooser.getExtensionFilters().add(extFilter); //Show save file dialog File file = fileChooser.showSaveDialog(primaryStage); Util.saveDir(file, new File( new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile(), "LastDirectory.txt")); if (file != null) { featureModel.setProjectFile(file); ModelReaderWriterController modelReaderWriter = new ModelReaderWriterController(featureModel, file, true); modelReaderWriter.start(); } } @FXML protected void awakeButtonOnAction() { featureModel.setLabel(currentEpoch, 0); updateWindows(); } @FXML protected void s1ButtonOnAction() { featureModel.setLabel(currentEpoch, 1); updateWindows(); } @FXML protected void s2ButtonOnAction() { featureModel.setLabel(currentEpoch, 2); updateWindows(); } @FXML protected void s3ButtonOnAction() { featureModel.setLabel(currentEpoch, 3); updateWindows(); } @FXML protected void remButtonOnAction() { featureModel.setLabel(currentEpoch, 5); updateWindows(); } @FXML protected void artefactButtonOnAction() { featureModel.setArtefact(currentEpoch, featureModel.getArtefact(currentEpoch) == 1 ? 0 : 1); updateWindows(); } @FXML protected void arousalButtonOnAction() { featureModel.setArousal(currentEpoch, featureModel.getArousal(currentEpoch) == 1 ? 0 : 1); updateWindows(); } @FXML protected void stimulationButtonOnAction() { featureModel.setStimulation(currentEpoch, featureModel.getStimulation(currentEpoch) == 1 ? 0 : 1); updateWindows(); } @FXML protected void clearButtonOnAction() { featureController.clearProperties(currentEpoch); updateWindows(); } @FXML protected void classifyButtonAction() { progressBar.setVisible(true); if (!featureModel.isClassificationDone()) { System.out.println("Classifiy!"); classify(); } classifyButton.setDisable(true); updateWindows(); } @FXML protected void visualizeButtonAction() { if (featureModel.isClassificationDone()) { if (viewModel.isScatterPlotActive()) { visualizeButton.setSelected(false); viewModel.setScatterPlotActive(false); } else { visualizeButton.setSelected(true); viewModel.setScatterPlotActive(true); if (!featureModel.isFeaturesComputed()) { computeFeatures(); } scatterPlot.paintScatterChart(); scatterPlot.show(); } } updateWindows(); } @FXML protected void electrodeConfiguratorButtonAction() { if (viewModel.isElectrodeConfiguratorActive()) { viewModel.setElectrodeConfiguratorActive(false); } else { viewModel.setElectrodeConfiguratorActive(true); } updateWindows(); } @FXML protected void showEvaluationWindowAction() { System.out.println("showEvaluationWindow called"); if (viewModel.isEvaluationWindowActive()) { viewModel.setEvaluationWindowActive(false); } else { viewModel.setEvaluationWindowActive(true); } updateWindows(); } @FXML protected void showScatterPlot() { if (viewModel.isScatterPlotActive()) { if (scatterPlot.isPainted) { scatterPlot.changeCurrentEpochMarker(); scatterPlot.show(); } } else { scatterPlot.hide(); } } @FXML private void hypnogramAction() { System.out.println("hypnogramAction called"); if (viewModel.isHypnogrammActive()) { hypnogramButton.setSelected(false); viewModel.setHypnogrammActive(false); } else { hypnogramButton.setSelected(true); viewModel.setHypnogrammActive(true); } updateWindows(); } @FXML private void dcRemoveButtonAction() { if (viewModel.isDcRemoveActive()) { dcRemoveButton.setSelected(false); viewModel.setDcRemoveActive(false); loadEpoch(currentEpoch); updateEpoch(); } else { dcRemoveButton.setSelected(true); viewModel.setDcRemoveActive(true); loadEpoch(currentEpoch); updateEpoch(); } lineChart.requestFocus(); } @FXML private void kcMarkersButtonAction() { if (viewModel.isKcMarkersActive()) { kcMarkersButton.setSelected(false); viewModel.setKcMarkersActive(false); } else { kcMarkersButton.setSelected(true); viewModel.setKcMarkersActive(true); } loadEpoch(currentEpoch); updateEpoch(); lineChart.requestFocus(); } @FXML private void filterButtonAction() { if (viewModel.isFiltersActive()) { filterButton.setSelected(false); viewModel.setFiltersActive(false); loadEpoch(currentEpoch); updateEpoch(); } else { filterButton.setSelected(true); viewModel.setFiltersActive(true); loadEpoch(currentEpoch); updateEpoch(); } lineChart.requestFocus(); } @FXML protected void saveAction() { projectFile = featureModel.getProjectFile(); if (projectFile == null) { saveAsAction(); } else { Util.saveDir(projectFile, new File(new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()) .getParentFile(), "LastDirectory.txt")); ModelReaderWriterController modelReaderWriter = new ModelReaderWriterController(featureModel, projectFile, true); modelReaderWriter.start(); } } private void tooltips() { help1.setTooltip(new Tooltip("75V bars (L)")); kComplex.setTooltip(new Tooltip("K-complex measurement tool (K)")); classifyButton.setTooltip(new Tooltip("Perform automatic sleep stage classification")); visualizeButton.setTooltip(new Tooltip("Show cluster plot (F6)")); electrodeConfiguratorButton.setTooltip(new Tooltip("Select electrodes for display... (F12)")); filterButton.setTooltip(new Tooltip("Filters on/off (F7)")); kcMarkersButton.setTooltip(new Tooltip("Highlight K-complexes on/off (F9)")); dcRemoveButton.setTooltip(new Tooltip("DC remove on/off (F8)")); s1Button.setTooltip(new Tooltip("Sleep stage N1 (1)")); s2Button.setTooltip(new Tooltip("Sleep stage N2 (2)")); s3Button.setTooltip(new Tooltip("Sleep stage N3 (3)")); awakeButton.setTooltip(new Tooltip("Wake (W)")); remButton.setTooltip(new Tooltip("REM (R)")); arousalButton.setTooltip(new Tooltip("Movement arousal (M)")); artefactButton.setTooltip(new Tooltip("Artefact (A)")); stimulationButton.setTooltip(new Tooltip("Stimulation (D)")); clearButton.setTooltip(new Tooltip("Clear (C)")); hypnogramButton.setTooltip(new Tooltip("Show hypnogram (H)")); choiceBox.setTooltip(new Tooltip("Select the channel to classify from")); choiceBoxModel.setTooltip(new Tooltip("Select classifier")); } private void computeFeatures() { if (!featureModel.isReadinDone()) { dataController.readAll(featureModel.getFeatureChannel()); featureModel.setReadinDone(true); } if (!featureModel.isFeaturesComputed()) { featureController.start(); featureModel.setFeaturesComputed(true); } } public final void updateChoiceBox() { choices.clear(); returnActiveChannels(); for (TIntIterator it = activeChannels.iterator(); it.hasNext();) { choices.add(channelNames[it.next()]); } choiceBox.setItems(choices); choiceBox.getSelectionModel().select(0); } private void showHypnogram() { Platform.runLater(new Runnable() { @Override public void run() { hypnogram.update(); hypnogram.changeCurrentEpochMarker(); } }); if (viewModel.isHypnogrammActive()) { hypnogram.show(); } else { hypnogram.hide(); } } public void keyAction(KeyEvent ke) { if (ke.getCode() == KeyCode.RIGHT) { goToEpoch(currentEpoch + 1); } if (ke.getCode() == KeyCode.LEFT) { goToEpoch(currentEpoch - 1); } if (ke.getCode() == KeyCode.H) { hypnogramAction(); } if (ke.getCode() == KeyCode.E) { showEvaluationWindowAction(); } if (ke.getCode() == KeyCode.L) { help1OnAction(); } if (ke.getCode() == KeyCode.K) { kComplexOnAction(); } if (ke.getCode() == KeyCode.UP) { refreshZoom(+1); } if (ke.getCode() == KeyCode.DOWN) { refreshZoom(-1); } if (ke.getCode() == KeyCode.W) { awakeButtonOnAction(); if (featureModel.getLabel(currentEpoch) == -1) { goToEpoch(currentEpoch + 1); } } if (ke.getCode() == KeyCode.R) { remButtonOnAction(); if (featureModel.getLabel(currentEpoch) == -1) { goToEpoch(currentEpoch + 1); } } if (ke.getCode() == KeyCode.DIGIT1) { s1ButtonOnAction(); if (featureModel.getLabel(currentEpoch) == -1) { goToEpoch(currentEpoch + 1); } } if (ke.getCode() == KeyCode.DIGIT2) { s2ButtonOnAction(); if (featureModel.getLabel(currentEpoch) == -1) { goToEpoch(currentEpoch + 1); } } if (ke.getCode() == KeyCode.DIGIT3) { s3ButtonOnAction(); if (featureModel.getLabel(currentEpoch) == -1) { goToEpoch(currentEpoch + 1); } } if (ke.getCode() == KeyCode.A) { artefactButtonOnAction(); } if (ke.getCode() == KeyCode.M) { arousalButtonOnAction(); } if (ke.getCode() == KeyCode.D) { stimulationButtonOnAction(); } if (ke.getCode() == KeyCode.C) { clearButtonOnAction(); } if (ke.getCode() == KeyCode.PAGE_DOWN) { goToEpoch(currentEpoch - 10); } if (ke.getCode() == KeyCode.PAGE_UP) { goToEpoch(currentEpoch + 10); } if (ke.getCode() == KeyCode.END) { goToEpoch(dataModel.getNumberOf30sEpochs() - 1); } if (ke.getCode() == KeyCode.HOME) { goToEpoch(0); } if (ke.getCode() == KeyCode.F6) { visualizeButtonAction(); } if (ke.getCode() == KeyCode.F7) { filterButtonAction(); } if (ke.getCode() == KeyCode.F8) { dcRemoveButtonAction(); } if (ke.getCode() == KeyCode.F9) { kcMarkersButtonAction(); } if (ke.getCode() == KeyCode.F12) { electrodeConfiguratorButtonAction(); } } public void updateWindows() { updateStage(); showHypnogram(); showScatterPlot(); showEvaluationWindow(); showElectrodeConfigurator(); lineChart.requestFocus(); } private void refreshZoom(double zoom) { if (zoom == 1.) { scale.set(scale.get() * 1.1); } if (zoom == -1.) { scale.set(scale.get() / 1.1); } updateEpoch(); lineChart.requestFocus(); } public void bringToFront() { primaryStage.toFront(); } public void classify() { final long startTime = System.nanoTime(); Task<Void> task = new Task<Void>() { @Override protected Void call() throws Exception { updateProgress(-1, featureModel.getNumberOfEpochs()); computeFeatures(); ClassificationController.classify(classifier, featureModel); Platform.runLater(new Runnable() { @Override public void run() { progressBar.setVisible(false); hypnogram.updateAll(); updateWindows(); } }); System.out.println("Elapsed time: " + (System.nanoTime() - startTime) / 1e6); return null; } }; progressBar.progressProperty().bind(task.progressProperty()); Thread thread = new Thread(task); thread.setDaemon(true); thread.start(); } private void checkProp() { for (int i = 0; i < channelNames.length; i++) { Double[] prop = allChannels.get(channelNames[i]); System.out.println(channelNames[i] + " " + prop[0] + " " + prop[1]); } System.out.println("----------------------------"); lineChart.requestFocus(); } public int getCurrentEpoch() { return currentEpoch; } public void requestFocus() { lineChart.requestFocus(); } private void showEvaluationWindow() { if (viewModel.isEvaluationWindowActive()) { if (evaluationWindow == null) { evaluationWindow = new FXEvaluationWindowController(dataModel, featureModel, viewModel); } evaluationWindow.reloadEvaluationWindow(); evaluationWindow.show(); } else { if (evaluationWindow != null) { evaluationWindow.hide(); } } lineChart.requestFocus(); } private void showElectrodeConfigurator() { if (viewModel.isElectrodeConfiguratorActive()) { if (electrodeConfigurator == null) { electrodeConfigurator = new FXElectrodeConfiguratorController(this.dataModel, this.allChannels, this.viewModel); } electrodeConfigurator.show(); } else { if (electrodeConfigurator != null) { electrodeConfigurator.hide(); } } lineChart.requestFocus(); } // First Column: 0 -> W, 1 -> N1, 2 -> N2, 3 -> N3, 5 -> REM // Second Column: 0 -> Nothing, 1 -> Movement arrousal // Third Column: 0 -> Nothing, 1 -> Artefact // Fourth Column: 0 -> Nothing, 1 -> Stimulation private void openFile(File file) throws IOException { BufferedReader in = new BufferedReader(new FileReader(file)); String row = null; int epoch = 0; int k = 0; while (((row = in.readLine()) != null) && (epoch < dataModel.getNumberOf30sEpochs() - 1)) { if (k == 0) { // k = 1; continue; } String[] rowArray = row.split(" "); int label = Integer.parseInt(rowArray[0]); featureModel.setLabel(epoch, label); if (Integer.parseInt(rowArray[1]) == 1) { featureModel.setArousal(epoch, 1); } if (Integer.parseInt(rowArray[2]) == 1) { featureModel.setArtefact(epoch, 1); } if (Integer.parseInt(rowArray[3]) == 1) { featureModel.setStimulation(epoch, 1); } epoch++; } in.close(); } private void updateStage() { // (1: W, 2: N1, 3: N2, 4: N3, 5: REM) int label = featureModel.getLabel(currentEpoch); switch (label) { case 0: awakeButton.setSelected(true); s1Button.setSelected(false); s2Button.setSelected(false); s3Button.setSelected(false); remButton.setSelected(false); break; case 1: awakeButton.setSelected(false); s1Button.setSelected(true); s2Button.setSelected(false); s3Button.setSelected(false); remButton.setSelected(false); break; case 2: awakeButton.setSelected(false); s1Button.setSelected(false); s2Button.setSelected(true); s3Button.setSelected(false); remButton.setSelected(false); break; case 3: awakeButton.setSelected(false); s1Button.setSelected(false); s2Button.setSelected(false); s3Button.setSelected(true); remButton.setSelected(false); break; case 5: awakeButton.setSelected(false); s1Button.setSelected(false); s2Button.setSelected(false); s3Button.setSelected(false); remButton.setSelected(true); break; case -1: awakeButton.setSelected(false); s1Button.setSelected(false); s2Button.setSelected(false); s3Button.setSelected(false); remButton.setSelected(false); break; } if (featureModel.getArtefact(currentEpoch) == 1) { artefactButton.setSelected(true); } else { artefactButton.setSelected(false); } if (featureModel.getArousal(currentEpoch) == 1) { arousalButton.setSelected(true); } else { arousalButton.setSelected(false); } if (featureModel.getStimulation(currentEpoch) == 1) { stimulationButton.setSelected(true); } else { stimulationButton.setSelected(false); } kcMarkersButton.setSelected(viewModel.isKcMarkersActive()); filterButton.setSelected(viewModel.isFiltersActive()); dcRemoveButton.setSelected(viewModel.isDcRemoveActive()); visualizeButton.setSelected(viewModel.isScatterPlotActive()); electrodeConfiguratorButton.setSelected(viewModel.isElectrodeConfiguratorActive()); hypnogramButton.setSelected(viewModel.isHypnogrammActive()); toolBarGoto.setText((currentEpoch + 1) + ""); statusBarLabel1.setText("/" + (dataModel.getNumberOf30sEpochs())); updateProbabilities(); if (featureModel.isClassificationDone()) { visualizeButton.setDisable(false); } else { visualizeButton.setDisable(true); } } public TIntArrayList returnActiveChannels() { activeChannels.clear(); for (int i = 0; i < channelNames.length; i++) { Double[] tempProp = allChannels.get(channelNames[i]); if (tempProp[0] == 1.0) { activeChannels.add(i); } } return activeChannels; } private double getZoomFromChannel(int channelNumber) { String channel = channelNames[channelNumber]; Double[] tempProp = allChannels.get(channel); double channelZoom = tempProp[1]; return channelZoom; } public void showLabelsForEpoch() { overlay.getChildren().clear(); double offsetSize = 1. / (activeChannels.size() + 1); for (int i = 0; i < activeChannels.size(); i++) { double realOffset = (i + 1.) * offsetSize; Label label = new Label(channelNames[activeChannels.get(i)]); label.setTextFill(Color.GRAY); label.setStyle("-fx-font-family: sans-serif;"); label.setLayoutX(1); label.layoutYProperty().bind(yAxis.heightProperty().multiply(realOffset).add(yAxis.layoutYProperty())); overlay.getChildren().add(label); } } public void goToEpoch(int epoch) { if (epoch < 0) { epoch = 0; } if (epoch > (dataModel.getNumberOf30sEpochs() - 1)) { epoch = dataModel.getNumberOf30sEpochs() - 1; } currentEpoch = epoch; overlay3.getChildren().clear(); lines.clear(); loadEpoch(currentEpoch); updateEpoch(); updateWindows(); //11 (N1 vs rest) 19(wach vs rem/rest)! 32/35 (wach vs rem) 50 (N3) if (featureModel.isFeaturesComputed()) { float[] f = featureModel.getFeatureVector(currentEpoch); infoLabel.setText(String.format("N3:%5$.2f PE/N1:%3$.2f sk/N2: %1$.2f hf:%2$.2f hf2:%4$.2f", f[1], f[3], f[10], f[9], f[49])); } lineChart.requestFocus(); } private void paintSpacing() { System.out.println("paintSpacing called"); line1.layoutYProperty().bind(scale.multiply(space).multiply(1 / 2.).add(mouseY)); line1.endXProperty().bind(overlay3.widthProperty()); line2.layoutYProperty().bind(scale.multiply(space).multiply(-1 / 2.).add(mouseY)); line2.endXProperty().bind(overlay3.widthProperty()); } final public void loadEpoch(int numberOfEpoch) { returnActiveChannels(); for (int i = 0; i < activeChannels.size(); i++) { dataController.read(activeChannels.get(i), numberOfEpoch, dataModel.data[activeChannels.get(i)]); } if (dataModel.getSrate() != 100) { for (int i = 0; i < activeChannels.size(); i++) { displayBuffer[activeChannels.get(i)] = featureController.getResampler() .resample(dataModel.data[activeChannels.get(i)]); } } else { for (int i = 0; i < activeChannels.size(); i++) { displayBuffer[activeChannels.get(i)] = dataModel.data[activeChannels.get(i)]; } } if (viewModel.isKcMarkersActive()) { computeKCfeatures(); } else { overlay4.getChildren().clear(); } if (viewModel.isFiltersActive() == true) { filterEpoch(); } if ((viewModel.isDcRemoveActive() == true) && (viewModel.isFiltersActive() == false)) { removeDcOffset(); } featureModel.setCurrentEpoch(currentEpoch); } final public void filterEpoch() { for (int i = 0; i < activeChannels.size(); i++) { Signal.filtfilt(displayBuffer[activeChannels.get(i)], featureController.getDisplayHighpassCoefficients()); } } final public void removeDcOffset() { for (int i = 0; i < activeChannels.size(); i++) { Signal.removeDC(displayBuffer[activeChannels.get(i)]); } } final public void showEpoch() { lineChart.getData().clear(); float[] epoch; double offsetSize = 0; if (!activeChannels.isEmpty()) { offsetSize = 1. / (activeChannels.size() + 1.); } for (int i = 0; i < activeChannels.size(); i++) { epoch = displayBuffer[activeChannels.get(i)]; double zoom = getZoomFromChannel(activeChannels.get(i)); // in local yAxis-coordinates double realOffset = (1 - (i + 1.) * offsetSize) * yAxis.getUpperBound(); @SuppressWarnings("rawtypes") XYChart.Series series = new XYChart.Series(); double epochSize = epoch.length; double xAxis = 0; for (int j = 0; j < epoch.length; j++) { double tmp = xAxis / epochSize; tmp = tmp * this.xAxis.getUpperBound(); double value = epoch[j]; // double value = Math.sin(2 * Math.PI * j / 100.) * 75 / 2.; //test signal value = value * zoom * scale.get(); value = value + realOffset; XYChart.Data dataItem = new XYChart.Data(tmp, value); series.getData().add(dataItem); xAxis++; } lineChart.getData().add(series); } showLabelsForEpoch(); lineChart.requestFocus(); } public void updateEpoch() { // works on list of XYChart.series returnActiveChannels(); double offsetSize = 0; if (!activeChannels.isEmpty()) { offsetSize = 1. / (activeChannels.size() + 1.); } float[] epoch; double zoom; double realOffset; for (int i = 0; i < activeChannels.size(); i++) { epoch = displayBuffer[activeChannels.get(i)]; zoom = getZoomFromChannel(activeChannels.get(i)); // in local yAxis-coordinates realOffset = (1 - (i + 1.) * offsetSize) * yAxis.getUpperBound(); int k = 0; for (int j = 0; j < epoch.length; j++) { // epoch[j] = Math.sin(2 * Math.PI * j / 100.) * 75 / 2.; //test signal lineChart.getData().get(i).getData().get(k).setYValue(epoch[j] * zoom * scale.get() + realOffset); k++; } } } final public void computeKCfeatures() { overlay4.getChildren().clear(); kcDetector.detect(displayBuffer[featureModel.getFeatureChannel()]); double percentageSum = kcDetector.getPercentageSum(); Set<Range<Integer>> kcPlotRanges = kcDetector.getKcRanges(); kComplexLabel.setVisible(true); kComplexLabel.setText("K-Complex: " + Math.round(percentageSum) + "%"); //draw yellow rectangles for every pair of coordinates in kcPlotRanges double start; double stop; for (Range<Integer> next : kcPlotRanges) { start = next.lowerEndpoint(); stop = next.upperEndpoint(); Rectangle r = new Rectangle(); r.layoutXProperty() .bind(this.xAxis.widthProperty().multiply((start + 1.) / (double) this.displayBuffer[0].length) .add(this.xAxis.layoutXProperty())); r.setLayoutY(0); r.widthProperty() .bind(xAxis.widthProperty().multiply((stop - start) / (double) this.displayBuffer[0].length)); r.heightProperty().bind(overlay4.heightProperty()); r.fillProperty().setValue(Color.LIGHTBLUE); r.opacityProperty().set(0.5); overlay4.getChildren().add(r); } lineChart.requestFocus(); } public void showPopUp(String message) { FXPopUp popUp = new FXPopUp(); popUp.showPopupMessage(message, primaryStage); Logger log = Logger.getLogger(this.getClass().getName()); log.setLevel(Level.ALL); log.info(message); } }