JavaFX Non-Responsive/Very Slow on Low End Embedded System? - java

I'm trying to implement a simple JavaFX GUI - Meaning a few buttons, a text area, a few circles (as lights) and a few labels.
Note that none of the functionality for these items has been created yet. So all these items are essentially hollow. Note this is my first JavaFX application (I've always worked in Swing).
So running in Eclipse on my i7 laptop has no problems and runs flawlessly. When I try to run it on the target machine (specs below) I get about a 15 second long non-responsive window. Once the gui comes up, any clicks on any elements in the window causes ~10seconds of CPU spiking up to 50ish%.
Once again there is no code behind any of the buttons or lights or text area yet. Is JavaFX just not meant to be on such a low end system?
Specs:
Windows Embedded 32bit (6.1)
Intel Atom E680 # 1.6Ghz
2GB Ram
DX11
Atom E6xx Embedded Media and Graphics
Code is below if anyone needs it.
Main:
public static void main(String[] args)
{
// Initialize GUI
gui = RecorderFXApplication.getGUIInstance();
guiCtrl = gui.getFXMLController();
}
Application:
public class RecorderFXApplication extends Application
{
/**
* Standard error logger for log4j2
*/
static Logger errorLog = LogManager.getLogger(RecorderFXApplication.class.getName());
private static RecorderFXApplication instance = null;
private FXMLLoader fxmlLoader;
public RecorderFXApplication()
{
super();
}
#Override
public void start(Stage primaryStage) throws Exception
{
fxmlLoader = new FXMLLoader(getClass().getResource("/RecorderGUI.fxml"));
try
{
Pane root = (Pane) fxmlLoader.load();
Scene scene = new Scene(new Group(root));
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.titleProperty().set("Faceboss Database Recorder");
instance = this;
} catch (IOException exception)
{
errorLog.error("Error loading fxml for view: " + exception);
}
}
#Override
public void stop() throws Exception
{
super.stop();
System.exit(0);
}
/**
* #return the instance
*/
public static RecorderFXApplication getGUIInstance()
{
if (instance == null)
{
new Thread(new Runnable()
{
#Override
public void run()
{
Application.launch(RecorderFXApplication.class);
}
}).start();
}
while (instance == null)
{
try
{
Thread.sleep(1);
} catch (InterruptedException e)
{
errorLog.error("Error getting instance " + e);
}
}
return instance;
}
public RecorderFXController getFXMLController()
{
while (fxmlLoader.getController() == null)
{
try
{
Thread.sleep(1);
} catch (InterruptedException e)
{
errorLog.error("Error getting controller " + e);
}
}
return fxmlLoader.<RecorderFXController> getController();
}
}
Controller:
public class RecorderFXController implements Initializable
{
/**
* Standard error logger for log4j2
*/
static Logger errorLog = LogManager.getLogger(RecorderFXController.class.getName());
#FXML
private TextArea logWindow;
#FXML
private Button startRecButton;
#FXML
private Button stopRecButton;
#FXML
private Button pauseRecButton;
#FXML
private Button exportButton;
#FXML
private Circle redLight;
#FXML
private Circle yellowLight;
#FXML
private Circle greenLight;
#Override
public void initialize(URL arg0, ResourceBundle arg1)
{
}
}

Related

JavaFX and jnativehook library conflict

I used jnativehook for global keyboard listening. I created GUI in JavaFX 11. I have a TextField to define name of a file created after pressing specified key combination.
The problem is I cannot edit text in the TextField by keyboard. I can delete or paste text by mouse but not by keyboard.
I created an individual thread for global keyboard listening. My idea was to stop this thread when the TextField is focused. Unfortunately, my attempts failed.
Here is a minimimal reproducible example which causes similar problem:
Main.java
public class Main extends Application {
private static final int APP_WIDTH = 400;
private static final int APP_HEIGHT = 400;
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
var fxmlLoader = new FXMLLoader(getClass().getResource("/main.fxml"));
Parent root = fxmlLoader.load();
stage.setTitle("Example");
stage.setScene(new Scene(root, APP_WIDTH, APP_HEIGHT));
stage.show();
Thread background = new Thread(() -> Platform.runLater(() -> {
GlobalKeyboardHook keyboardHook = new GlobalKeyboardHook(true);
keyboardHook.addKeyListener(new GlobalKeyAdapter() {
#Override
public void keyPressed(GlobalKeyEvent keyEvent) {
System.out.println("Key pressed: " + keyEvent.getVirtualKeyCode());
}
#Override
public void keyReleased(GlobalKeyEvent keyEvent) {
System.out.println("Key released: " + keyEvent.toString());
}
});
}));
background.start();
}
Controller.java
public class Controller implements Initializable {
#FXML
private TextField filePath;
private static String filePathString = "filePathString";
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
handleFilePath();
}
private void handleFilePath() {
filePath.textProperty().setValue(filePathString);
filePath.textProperty().addListener(((observable, oldValue, newValue) -> {
filePath.commitValue();
}));
}
}

JavaFX Multithreading Issue [duplicate]

This question already has an answer here:
JavaFx2 IllegalStateException with Label.setText
(1 answer)
Closed 6 years ago.
I'm currently making an application in which we want to use multithreading to display the flashing label "loading" for a certain period of time after logging in, before continuing on to the next page. Here is my current progress:
public class LoadingController implements Initializable {
#FXML
private Label loadingLabel;
boolean ready = false;
public void setReady() {
System.out.println("now I'm ready");
ready = true;
}
public void showLabel() {
this.loadingLabel.setVisible(true);
}
public void hideLabel() {
this.loadingLabel.setVisible(false);
}
public void goToPage2() {
try {
Parent root = FXMLLoader.load(getClass().getResource("Page2.fxml"));
Scene scene = new Scene(root);
Stage stage = Assignment.getStage();
stage.setScene(scene);
} catch (IOException ex) {
Logger.getLogger(LoadingController.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
System.out.println("In loading page");
// TODO launch thread
Thread2 thread = new Thread2(this);
thread.start();
}
}
public class Thread2 extends Thread {
private LoadingController con;
public Thread2(LoadingController con) {
this.con = con;
}
public void run() {
System.out.println("Hello from a thread!");
try {
for (int i = 0; i < 20; i++) {
con.hideLabel();
Thread.sleep(100);
con.showLabel();
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
con.setReady();
}
}
I currently get this error relating to line
Scene scene = new Scene(root);
Exception in thread "Thread-6" java.lang.IllegalStateException: Not on FX application thread.
Would anyone be able to provide some guidance on this issue?
Thank you
The guidance is very simple - read the relevant documentation about JavaFX concurrency.
https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm
The exception clearly tells you what is wrong. You try to create a new Scene from the wrong thread. SceneGraph manipulations are only allowed from the JavaFX application thread.

How to change ListView only in GUI Thread (JavaFX) [duplicate]

The application reacts on actions which occur on gamepad. When button is pressed something happens on UI. But I ran at the issue with app hangs up or "java.lang.IllegalStateException: Not on FX application thread" exception.
In order to fix it I tried the following approaches: Platform.runLater() and Task usage. But it didn't help.
Here is the problem code:
public class GamepadUI extends Application{
private static final int WIDTH = 300;
private static final int HEIGHT = 213;
private Pane root = new Pane();
private ImageView iv1 = new ImageView();
private boolean isXPressed = false;
#Override
public void start(Stage stage) throws Exception {
initGUI(root);
Scene scene = new Scene(root, WIDTH, HEIGHT);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
public void pressBtn() {
if(!isXPressed) {
iv1.setVisible(true);
isXPressed = true;
}
}
public void releaseBtn() {
if(isXPressed) {
iv1.setVisible(false);
isXPressed = false;
}
}
private void initGUI(final Pane root) {
Image image = new Image(Props.BUTTON);
iv1.setImage(image);
iv1.setLayoutX(198);
iv1.setLayoutY(48);
iv1.setVisible(false);
root.getChildren().add(iv1);
runTask();
}
public void runTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
initStubGamepad();
return null;
}
};
new Thread(task).start();
}
public static void main(String[] args) {
launch(args);
}
public void initStubGamepad() {
Random rnd = new Random();
try {
while (true) {
if (rnd.nextInt(30) == 3) {
pressBtn();
} else if (rnd.nextInt(30) == 7) {
releaseBtn();
}
}
} catch (Exception ex) {
System.out.println("Exception: " + ex);
}
}
}
initStubGamepad() emulates gamepad buttons activity polling. When user presses any button (rnd.nextInt(30) == 3) - an image appears on the UI. When user releases that button (rnd.nextInt(30) == 7) - an image disappears from the UI.
In case above java.lang.IllegalStateException: Not on FX application thread occurs. If you change runTask() to something like this:
Platform.runLater(new Runnable() {
#Override
public void run() {
initStubGamepad();
}
});
Then app will hang or even main UI won't appear at all, but gamepad activity continues.
What I want is just to show/hide different images when some activity is detected on gamepad (btw, there's no way to monitor gamepad activity except for gamepad polling in an infinite loop). What did I wrong
Explanation
In the first scenario, when you are using
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
initStubGamepad();
return null;
}
}
Inside initStubGamepad(), which is running on a Task, you are trying to update the UI components inside pressBtn() and releaseBtn() methods, which is why you are facing a
java.lang.IllegalStateException: Not on FX application thread
because all the UI updates must occur on the JavaFX thread
In the second scenario, when you are using
Platform.runLater(new Runnable() {
#Override
public void run() {
initStubGamepad();
}
});
the UI doesnt appear, because you have an infinite loop inside the initStubGamepad(), which puts the JavaFX application thread run on an infinite loop
Solution
By the time you have reach here, you must have already found the solution. In case you haven't, try try to put the update the Javafx components on the UI thread. So, instead of calling initStubGamepad() inside Platform.runLater, try calling pressBtn() and releaseBtn() inside it.
Try using
while (true) {
if (rnd.nextInt(30) == 3) {
Platform.runLater(() -> pressBtn());
} else if (rnd.nextInt(30) == 7) {
Platform.runLater(() -> releaseBtn());
}
}
or you may also use
public void pressBtn() {
if(!isXPressed) {
Platform.runLater(() -> iv1.setVisible(true));
isXPressed = true;
}
}

JavaFX : Keep listening to keyboard-input and on input perform action

I have JavaFX project in which I have to listen to keyboard-input as our barcode scanner is configured that way.
Are there any libraries in JavaFX where I can keep a listener active and perform suitable action upon reception of a String by barcode-scanner.
I searched on net, but didn't find any good solution unfortunately.
Here is my code :
public class Main extends Application {
private Scene scene;
MyBrowser myBrowser;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Our Application");
java.net.CookieManager manager = new java.net.CookieManager();
java.net.CookieHandler.setDefault(manager);
myBrowser = new MyBrowser();
scene = new Scene(myBrowser, 1080, 1920);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.show();
// # being the escape character
scene.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String text = event.getCharacter();
if (text.equals("#")) {
String tempText = completeText;
completeText = "";
processText(tempText);
}else {
completeText = completeText+text;
}
}
});
}
private void processText(String text){
System.out.println("I will process "+text);
}
public static void main(String[] args) {
launch(args);
}
public class MyBrowser extends Region {
final String hellohtml = "index.html";
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
public MyBrowser() {
webEngine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
}
});
URL urlHello = getClass().getResource(hellohtml);
webEngine.load(urlHello.toExternalForm());
webView.setPrefSize(1080, 1920);
webView.setContextMenuEnabled(false);
getChildren().add(webView);
}
Kindly let me know. Thank you.
So I think I understand what your problem is, you have a barcode scanner that sends in barcodes as keyevents to your application, And then you need to respond to the whole bar code when you're done receiving the code?
If that's the case you can use a KeyListener to intercept the key press events. Then you just need to implement the logic to put the individual key events together.
class MyListener implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
// Logic goes here
}
#Override
public void keyReleased(KeyEvent e) {
// Logic goes here
}
#Override
public void keyTyped(KeyEvent e) {
// Logic goes here
}
}

JavaFX Switch Scene in a SplitPane?

At the Base I have an AnchorPane then a SplitPane. On the left pane I have a listView and depending on the list element selected, the right pane displays the appropriate content. The way I have done this is by overlapping AnchorPanes and setting them to .setVisible(false) initially and as they are selected I set them to .setVisible(true) like so :
public void listSelection() {
String selection = listView.getSelectionModel().getSelectedItem();
switch(selection) {
case "Speed of sound":
disableOld(); // disables old AnchorePane
response.setText("Speed of sound conversion");
AnchorPane1.setVisible(true);
break;
case "Temperature conversion":
disableOld();
response.setText("Temperature conversion");
AnchorPane2.setVisible(true);
break;
}
}
I would like to know how to produce the same effect visually but with different scenes as I would like for each new AnchorPane to have it's own FXML and ControllerClass.
You can implement something like this :
Your main class :
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Title");
primaryStage.setScene(createScene(loadMainPane("path_of_your_fxml")));
primaryStage.show();
}
private Pane loadMainPane(String path) throws IOException {
FXMLLoader loader = new FXMLLoader();
Pane mainPane = (Pane) loader.load(
getClass().getResourceAsStream(path));
return mainPane;
}
private Scene createScene(Pane mainPane) {
Scene scene = new Scene(mainPane);
return scene;
}
public static void main(String[] args) {launch(args); }
Then you can create a separate class call Navigator to store all your fxml paths:
public class Navigator {
private final String P1;
private final String P2;
//then you can implement getters...
public String getP1() {
return P1;
}
public String getP2() {
return p2;
}
private static FxmlController Controller;
public static void loadPane(String fxml) {
try {
FxmlController.setPane(
(Node) FXMLLoader.load(Navigator.class.getResource(fxml)));
} catch (IOException e) {
e.printStackTrace();
}
}
public Navigator() throws IOException {
this.P1 = "p1.fxml";
this.P2 = "p2.fxml";}
In your main FxmlController(which is the controller of the permanent layer of your application , rest of the stack-panes-{p1 and p2} will load on your permanent layer )
This is how you load layers on the main FxmlController :
#FXML
private StackPane stackPaneHolder;
...
public void setPane(Node node) {
if (stackPaneHolder.getChildren().isEmpty()) {
//if stackPaneHolder is empty
stackPaneHolder.getChildren().add(node);
} else {
if (stackPaneHolder.getClip() != node) {
//if stackPaneHolder is not empty then remove existing layer and add new layer
stackPaneHolder.getChildren().remove(0);
stackPaneHolder.getChildren().add(0, node);
}
}
}
Then you can load panes by pressing a button like below :
#FXML
private void btnAction(ActionEvent event) throws IOException {
Navigator.load(new Navigator().getP1());
..
This is how it works :

Categories