In my Programm I have a Main class, witch is an Application, and a Second Class witch is for Communicating with an Arduino. It is also a seperat Thread. The second class needs to Manipulate some Labels in the Application, but it gives me back an Error, that it is Not in the Java FX thread. How can I handel this?
Exception in thread "Thread-6" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-6
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:444)
at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$0(BehaviorSkinBase.java:197)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.scene.control.Labeled.setText(Labeled.java:145)
at Main$Fader.setValue(Main.java:132)
at ArduinoController.run(ArduinoController.java:50)
My Main Class:
import GUI.NormalController;
import com.fazecast.jSerialComm.SerialPort;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
* isi_kos Handheld Lightning Console
* @author isi_ko
* @version 2.1
*/
public class Main extends Application{
Fader[] faders;
final int baudRate = 1000000; //The Speed that is Used to Communicate with the Arduino
private int physicalFaderAmount; //Amount of Physical Faders, counted from 1
private int faderPages; //Amount of Faderpages, counted from 1
private int activeFaderpage; //The Currently active "physical" Faderpage
private ArduinoController arduinoController; //The Class that communicates with the Arduino
private EosOscController oscController; //The Class that Communicates with Eos
private FXMLLoader normalLoader;
private NormalController normalController;
//region Getters and Setters
public void setPhysicalFaderAmount(int physicalFaderAmount) {
this.physicalFaderAmount = physicalFaderAmount;
}
public void setFaderPages(int faderPages) {
this.faderPages = faderPages;
}
public void setActiveFaderpage(int activeFaderpage) {
this.activeFaderpage = activeFaderpage;
}
public void setArduinoController(ArduinoController arduinoController) {
this.arduinoController = arduinoController;
}
public void setOscController(EosOscController oscController) {
this.oscController = oscController;
}
public int getPhysicalFaderAmount() {
return physicalFaderAmount;
}
public int getFaderPages() {
return faderPages;
}
public int getActiveFaderpage() {
return activeFaderpage;
}
public int getBaudRate() {
return baudRate;
}
public ArduinoController getArduinoController() {
return arduinoController;
}
public EosOscController getOscController() {
return oscController;
}
//endregion
public Main() {
//Todo Redo this part when UI done
//at the Moment here are just some default values
activeFaderpage = 0;
physicalFaderAmount = 5;
faderPages = 4;
faders = new Fader[physicalFaderAmount];
for (int i = 0; i < physicalFaderAmount; i++){
faders[i] = new Fader(faderPages, i);
}
oscController = new EosOscController(8001, 8000, "192.168.178.133", this);
arduinoController = new ArduinoController(SerialPort.getCommPort("COM4"), this); // For PC
// arduinoController = new ArduinoController(SerialPort.getCommPort("ttyACM0"), this); //For Raspberry Pi
}
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
normalLoader = new FXMLLoader(getClass().getResource("GUI/normalLayout.fxml"));
Parent normalParent = normalLoader.load();
normalController = normalLoader.getController();
primaryStage.setTitle("Handheld Lightning Console Version 2.1");
primaryStage.setScene(new Scene(normalParent, primaryStage.getWidth(), primaryStage.getHeight()));
primaryStage.show();
}
@Override
public void stop() throws Exception {
oscController.stop();
}
// This Class is for Storing Information about a particular physical Fader
public class Fader {
public int[][] values;
// The last 3 Falues the Fader has on the Different Faderpages.
// The First Koordinate is for the Page, the second coordinate is for wich of the last 3 Values you whant to use.
// The First Value of Every page is the Current value of the Fader
// The Others are for detecting inconsistant Potivalues
private int faderID; //The number of the Physical Fader, starting from 0.
// Uses local values, not OSC
// Sets the Value of the Fader
// Can send the new Value direcktly to the Arduino or Eos
public void setValue(int physicalPage, int value, boolean sendToEos, boolean sendToArduino) {
if (value != this.values[physicalPage][2] || (Math.max(values[physicalPage][1], value) - Math.min(values[physicalPage][1], value)) > 1) {
values[physicalPage][0] = value;
if (!normalController.equals(null))
normalController.getFaderValueLable(faderID).setText(String.valueOf(value));
if (sendToEos) {
oscController.setFaderIntensity(physicalPage, faderID, value / 100.0D, true);
}
if (sendToArduino) {
arduinoController.sendMessage(new ArduinoController.MessageToArduino(faderID, value), true);
}
}
values[physicalPage][2] = values[physicalPage][1];
values[physicalPage][1] = value;
}
Fader(int pages, int faderID) {
values = new int[pages][3];
this.faderID = faderID;
}
public int getFaderID() {
return faderID;
}
public void setFaderID(int faderID) {
this.faderID = faderID;
}
}
}
My Second Class.
import java.io.PrintWriter;
import java.util.Scanner;
import com.fazecast.jSerialComm.SerialPort;
public class ArduinoController extends Thread {
private Main main; //The Instance of the Main class
private Scanner data; //A scanner that is used for recieving Messages from the Arduino
private SerialPort port; //The Port for communication with the Arduino
private PrintWriter printWriter; //For sending Messages to the Arduino
private boolean setupComplete; //True when the Arduino has Calibrated his Fades, and listens for Messages
private boolean arduinoConnected; //False, when the Serial Port couldn't be established, normaly if the Arduino is not Connected, or if a nother Programm is connected to it
public ArduinoController(SerialPort pPort, Main main) {
this.main = main;
port = pPort;
if (port.openPort()) {
System.out.println("Arduino: Successfully opened the port.");
arduinoConnected = true;
port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 0, 0);
port.setBaudRate(1000000);
printWriter = new PrintWriter(port.getOutputStream());
data = new Scanner(port.getInputStream());
start();
} else {
System.out.println("Arduino: Unable to open the port.");
arduinoConnected = false;
return;
}
}
@Override
public void run() {
super.run();
while (data.hasNextLine()) {
String serialMessage = data.nextLine();
if (serialMessage.equals("S;E")) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sendAllValues();
}
MessageFromArduino decodedMessage = new MessageFromArduino(serialMessage);
if (decodedMessage.msgComplete) {
if (1 == decodedMessage.commandType) {
main.faders[decodedMessage.sensorID].setValue(main.getActiveFaderpage(),decodedMessage.sensorValue,true, false);
decodedMessage.used = true;
}
if (!decodedMessage.used)
System.out.println("ERROR: unused Message: \"" + serialMessage + "\"");
}
}
}
public void sendAllValues() {
for (int i = 0; i < main.getPhysicalFaderAmount(); i++) {
sendMessage(new MessageToArduino(i, main.faders[i].values[main.getActiveFaderpage()][0]), true); //faderController.faders[i].valuePages[master.activeFaderPage]
}
}
public void sendMessage(MessageToArduino msg, boolean sendCommandlineFeedback) {
if (this.arduinoConnected) {
printWriter.print(msg.getString());
printWriter.flush();
if (sendCommandlineFeedback) System.out.println("Send Message: " + msg.getString());
} else {
System.out.println("Cant send Message: Not Connected (" + msg.getString() + ")");
}
}
public class MessageFromArduino {
public int addresseeID; //ID of the Device the Msg is send to
public int commandType; //what the command does (0 for msg without real content, used to mark the end of a loop at the Arduino or a successfully received msg, 1 for getValue, 2 for setValue (for Motorfaders))
public int sensorType; //what type of Sensor is effected
public int sensorID; //which sensor was effected
public int sensorValue; //what the new value of the sensor is
public int furtherInformation; //for a potentiometer e.g. the exact value
public boolean msgComplete; //whether the message was complete
public boolean used;
public MessageFromArduino(String msg) {
String[] args = msg.split(";");
this.msgComplete = args.length == 8 && "S".equals(args[0]) && "E".equals(args[7]);
if (this.msgComplete) {
this.addresseeID = Integer.parseInt(args[1]);
this.commandType = Integer.parseInt(args[2]);
this.sensorType = Integer.parseInt(args[3]);
this.sensorID = Integer.parseInt(args[4]);
this.sensorValue = Integer.parseInt(args[5]);
this.furtherInformation = Integer.parseInt(args[6]);
}
used = false;
}
}
public static class MessageToArduino {
public int sensorID; //which sensor was effected
public int sensorValue; //what the new value of the sensor is
public boolean msgComplete; //whether the message was complete
public MessageToArduino(int sensorID, int sensorValue) {
this.sensorID = sensorID;
this.sensorValue = sensorValue;
}
public MessageToArduino(String msg) {
String[] args = msg.split(";");
this.msgComplete = args.length == 8 && "S".equals(args[0]) && "E".equals(args[7]);
if (this.msgComplete) {
this.sensorID = Integer.parseInt(args[0]);
this.sensorValue = Integer.parseInt(args[1]);
}
}
public String getString() {
return "S;" + this.sensorID + ";" + this.sensorValue + ";E";
}
}
}