I have a simple console application which sometimes need to perform graphics operations, for those I'm using JavaFx framework (there are some functions that I need like the css styling for text )
I simply generate some shapes and text into an hidden scene then save those on file and that's all,
I know that to work with JavaFx I have to pass graphics operations to the JavaFx thread, but when everything is done and I have to close the application (after some hours) this JavaFx thread still remain open... and I really don't want to force exit with System.exit() because if something is blocked I may want to know/wait (ALSO I don't want to execute everything as an JavaFx application (as JavaFx components are less than 1% of my main application)
the code is very simple and googling around I've found only to use
Platform.exit();
which doesn't seems to work, I've also tried playing with Platform parameters like
Platform.setImplicitExit(false);
here is my test application which you can run :
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Pos;
import javafx.scene.layout.VBox;
public class SOTestFX {
public static void main(String[] args) {
SOTestFX t = new SOTestFX();
t.runFxThread();
}
public void runFxThread(){
//Application.launch(args);
final JFXPanel jfxPanel = new JFXPanel();
Platform.runLater(new Runnable() {
#Override
public void run() {
System.err.println("CREATING IMAGE");
simpleFXoperations();
System.err.println("NOW CALL EXIT");
System.err.println("JAVA FX THREAD SHOULD BE EXITED NOW");
Platform.exit();
}
});
try {
Thread.sleep(3000); // just wait a bit if something should happen, let it happen..
} catch (InterruptedException e) {
e.printStackTrace();
}
//jfxPanel.removeNotify(); // return -> java.lang.NullPointerException
//Platform.exit(); // -> does nothing
System.err.println("i will never die!");
}
public void simpleFXoperations(){
VBox vbox1 = new VBox();
vbox1.setAlignment(Pos.BOTTOM_CENTER);
vbox1.setStyle("-fx-border-style: solid;"
+ "-fx-border-width: 1;"
+ "-fx-border-color: white");
System.err.println("simpleFXoperations() _DONE");
}
}
and this is the thread which never close
"Attach Listener" - Thread t#17 java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"JavaFX Application Thread" - Thread t#13 java.lang.Thread.State:
RUNNABLE at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native
Method) at
com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:82)
at java.lang.Thread.run(Thread.java:722)
Locked ownable synchronizers:
- None
Update: I'm using latest Oracle JDK 7u17 64bit on Linux Fedora 16 64bit.
Fix:
I was able to fix this problem by calling com.sun.javafx.application.PlatformImpl.tkExit() immediately before Platform.exit(). I don't really understand the JavaFX source that well, but it seems to be working; YMMV.
Update: Doing this in Java 8 will produce a warning, you can just turn the warning off with #SuppressWarnings("restriction"). It shouldn't be a problem.
Explanation:
I figured this out by digging through the source code; JFXPanel has this little snippet (this is from JavaFX 2.2.25)
finishListener = new PlatformImpl.FinishListener() {
public void idle(boolean paramAnonymousBoolean) {
if (!JFXPanel.firstPanelShown) {
return;
}
PlatformImpl.removeListener(JFXPanel.finishListener);
JFXPanel.access$102(null);
if (paramAnonymousBoolean)
Platform.exit();
}
public void exitCalled()
{
}
The problem is, if you are using only a little bit of JavaFX in your application, then the idle(boolean) method never does anything (because firstPanelShown == false), which prevents the listener from getting removed, which prevents the JavaFX Toolkit from shutting down... which means you have to shut it down manually.
Your main function does not belong to the JavaFx Application object and i think that your program never eneter application thread loop.
It seems you should do:
public class SOTestFX extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// Do stuff here to show your stage or whatever you want;
// This will be called on the JavaFX thread
}
}
That's a slippery situation, as (to my understanding) the purpose of the JavaFX thread is to take advantage of various hardware pipelines transparently. Might I suggest placing your JavaFX requests in a separate, referenced, project; and keep everything else, including your main method, in another? That's always worked for me.
Basically, business logic and model go in one project, and view and control (generally JavaFX-based) go in the other. This allows for independent termination of the JavaFX thread. Hopefully that is applicable to what you are trying to do.
I tried a lot of things on this as none of the above answers worked for me.
Best thing for me was to just shutdown the whole JVM using
System.exit(1);
as my whole Application is just that one JavaFX application and therefor can be shut down, when closing the Stage.
The 1 in my case is just a random int.
Related
I started learning about JavaFX a short time ago and I am trying to switch from Swing to JavaFX. I ran into some logic implementation problem that I cannot think of a solution with JavaFX that I easily solved using Swing.
The application that I developed is huge, containing multiple already developed software modules, that interact with the graphics at some point. For example, in the application I have a smart card reader that, when a card is read on that reader and the operator is authenticating with a smart card, it displays on the graphic that a valid card is read, it display a green card icon and lets the operator enters his password. There are multiple drivers like the smart card reader and all of them generate events also with their status, are they working or not. In the current solution all modules communicate with central main software that can call functions for the Swing graphics.
The application starts with initializing a page, and when all of the devices are working and there is no error, I am showing the first page of the application. If any of them has an error, I am showing the error page. I designed some fxml and connect them with their own controller. In the controller of the initializing page in the method should look something like this:
#Override
public void initialize(URL url, ResourceBundle rb) {
if(no_error){
go to first page
}else{
go to out of order page
}
}
The first thing that I want to implement is to wait, because some of the drivers and devices won't work instantly, for example wait for 10 cycles with timeout of 1 second on each of them.
#Override
public void initialize(URL url, ResourceBundle rb) {
while (true) {
if (no_error) {
go to first page
} else {
if (timeout_expired) {
go to out of order page
} else {
wait
increase timeout
}
}
}
}
I know that purpose of the initialize method is not for this and the above code is not a solution, I am looking more for a function like doInBackground from the AsyncTask.
Also, in this application in the controller, I want to implement events that are not graphic related like the reading of the smart card. How to connect the event from the driver for the smart card, when it reads card data to send that data to a function implemented in the controller like the one below?
public void controller_smart_card_read(SmartCard smart_card){
//Check if valid card from DB
//Display result
}
Also, in some scene I want to implement an inactivity event. If there are no events for a longer period of time (both graphical and from the devices), go back to the first page for example.
To summarize this, is there a way a controller is accessed and triggered from an independent software module, and is there a way to implement a doInBackground() function while scene and controller is up and running?
Create a background thread to do this functionality and use the Platform.runLater to update the UI.
For Example
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
ScheduledExecutorService scheduledExecutorService;
ScheduledFuture<?> scheduledCheck;
public void start(Stage base) {
base.setOnCloseRequest(
scheduledExecutorService.shutdownNow();
);
scheduledExecutorService = Executors.newScheduledThreadPool(1);
Runnable doBackground = () -> {
//Do background tasks i.e. check card reader
if(devices_ready && successful_card_read)
Platform.runLater(() -> {
//Update Javafx UI
});
}
//scheduleAtFixedRate(Runnable function, wait time before starting runnable, cycle time, timeunit)
//the below thread will wait 10 seconds, then execute the doBackground every 1 second
scheduledCheck = scheduledExecutorService.scheduleAtFixedRate(doBackground,10,1, TimeUnit.SECONDS);
}
EDIT:
I noticed that my question was linked to another. While our goals are similar, the other question's set up is different: they are creating all the GUI aspects of the program within the main class of their program, they are also setting the trigger event of the button press within the start method. Therefore the solution of using the "setOnAction(event->" coupled with Task works. It is a single class program, I was able to make the solution work if I created a new, single class program, this application does not work for me for my situation.
In my set up I am not running this event out of the main class, but out of the Controller class that is linked to my FXML and I have the event that triggers the method already defined. I did not post my entire Controller class as that seemed unnecessary. If there is a way to make the linked question's solution work for my different set up, or a link for guidance that would be stellar. I have looked into the "task" set up, taking from the linked question, but so far have not been able to get it to work successfully as pictured below:
#FXML
private void goForIt(ActionEvent event)
{
kickTheBaby();
}
private void kickTheBaby()
{
java.util.Date now = calendar.getTime();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(now.getTime());
statusFld.setOnAction(event -> {statusFld.setText("Running");
Task task = new Task<Void>() {
#Override
public Void call()
{
(new Thread(new EmailCommunication("", currentTimestamp, "START"))).start();
(new Thread(new DataGathering2())).start();
return null;
}
};
task.setOnSucceeded(taskFinishEvent -> statusFld.setText(statusFld.getText()
+ "All done time to sleep..."));
new Thread(task).start();
});
}
I have a program in Java8 using FXML that downloads and parses data. I wish to make the program update the GUI TextField (called "statusFld" here) to say "Running" when I click the start button. Below is the method in the controller that should be responsible for this series of events.
#FXML
private void goForIt(ActionEvent event)
{
statusFld.setText("Running!");
java.util.Date now = calendar.getTime();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(now.getTime());
(new Thread(new EmailCommunication("", currentTimestamp, "START"))).start();
(new Thread(new DataGathering2())).start();
}
However, when I attempt to run the program the GUI does not visually update and goes straight into the other two threads. So I attempted to utilize the "Platform.runLater()" methodology in one of the other threads by passing the status field to it as so:
Platform.runLater(() ->
{
statusFld.setText("Running!");
});
But after 20 minutes it had not given a visual update to the GUI. My guess is that this is probably due to the sheer amount of data processing that I am having it do, so who knows what "later" will actually be in this case.
My question is how can I be sure that the GUI visually updates before moving on to the other, very processing intense, threads?
Thank you!
We have an javafx application developed mainly in java 1.7 and tested in 1.8. It was running fine until java 1.8u35. Now we discovered, JavaFx windows are not going to open in 1.8u40 after upgrade. Even worse, the modal windows are blocking the entire tab/ browser of being used. So the user is just able to close the browser using the task manager.
We use javafx.embed.swing.JFXPanel to embed jfx-code into swing legacy code.
I have completely no clue what might be the problem, as there are no errors displayed in client's java console.
UPDATE:
I reviewed the known issues list for java1.8 here. The only thing I would probably link to our issue is this bug:
BUG-RT-32597: The SwingNode class does not support High DPI displays.
So I tried lowering the screen resolution (1280x1024 to 800x600) but without success.
Does anyone faced a similar issue before and knows what might help?
UPDATE:
I tried to better track down the problem but with not much luck.
To make it more visible, this is basically whats going on on window loading:
public static void initWindow(JDialog dialog){
final JFXPanel jfx = new JFXPanel();
Platform.runLater(new Runnable() {
public void run() {
System.out.println("JFXPanel");
}
});
Runnable r = new Runnable() {
public void run() {
AnchorPane root = new AnchorPane;
//... do some content loading
Scene scene = new Scene(root,width,height);
System.out.println("test");
}
};
dialog.add(jfx);
System.out.println("added jfx panel.");
dialog.pack();
System.out.println("packed jfx panel.");
dialog.setLocationRelativeTo(null);
System.out.println("loaded.");
}
I thought execution is going to stop somewhere, but its running through the entire function as usual. Nevertheless the window is not showing up.
UPDATE:
Not completely correct, my last comment, as I found out:
Around the above function, the following happens:
initWindow(this); //this is extending java.swing.JDialog
System.out.println("this comment is printed to console");
super.setVisible(true); //this is not executed properly. if removed, browser will not be blocked, but window doesnt show up either
System.out.println("this comment is not printed to console";
So, in general, there is the JDialog which gets packed with a JfxPanel. When calling setVisible() method from the JDialog class, the Application gets blocked but the window doesnt show up. Actually, in the thumbnail screen (alt+tab) it is shown as a container inside the application.
When removing the setVisible call, the browser does not get blocked, but also the window does not show up. Unfortunately, I did not find the JDialog class code to look up, what's going on inside setVisible().
Any ideas, what might be wrong with our setup or the setVisible method?
We had a similar problem. While comparing the Java sources of 1.8.0_31 and 1.8.0_45 we found out that there were some changes in the JFXPanel source code introduced with 1.8.0_45 that may cause problems in the following situation:
initialize modal JDialog with JFXPanel (executed on Swing's EDT)
initialize and set FX scene on JFXPanel in FX task (executed on FX Thread)
wait for FX task to be finished (wait on EDT)
pack() and show() JDialog (continued on EDT, blocks program execution)
continue with program execution after user closed JDialog (on EDT)
We use this workflow in order to wait for some user input being shown in a new modal JDialog and continue normal programm execution on EDT afterwards.
In 1.8.0_31, the preferred size of JFXPanel seems to be set in FX thread which allows JDialog.pack() to determine the correct bounds.
In 1.8.0_45, the preferred size of JFXPanel seems to be set not in FX thread anymore but in EDT after all pending AWT events are executed. So, when (4) is executed, JDialog.pack() does not know about the preferred size of the scene. As a consequence, the dialog has no content, or does not show up if undecorated as described in the original question above.
Here is a complete example to reproduce the different behaviour:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// create JDialog on EDT
final JDialog dialog = new JDialog((JDialog)null, "JDialog");
// initialize FX platform and create JFXPanel
final JFXPanel jfxPanel = new JFXPanel();
// add resize listener for JFXPanel
jfxPanel.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// not called in 1.8.0_45
System.out.println("JFXPanel.getSize(): "+jfxPanel.getSize());
}
});
// set FX Scene on JFXPanel and wait until finished
runAndWait(new Runnable() {
public void run() {
Text text = TextBuilder.create().text("JavaFx content").y(20).build();
Group root = new Group(text);
Scene scene = new Scene(root);
jfxPanel.setScene(scene);
}
});
// show undecorated modal JDialog with FX content
System.out.println("JFXPanel.getPreferredSize(): "+jfxPanel.getPreferredSize());
dialog.setUndecorated(true);
dialog.add(jfxPanel);
dialog.pack();
dialog.setModal(true);
System.out.println("JDialog.setVisible()");
dialog.setVisible(true);
}
});
}
private static void runAndWait(Runnable r) {
try {
FutureTask<Object> task = new FutureTask<Object>(r, null);
Platform.runLater(task);
task.get();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
When running this program, componentResized() is called only in 1.8.0_31 but not in 1.8.0_45.
A possible fix while keeping the synchronous program workflow on EDT is to replace JDialog.pack() with JDialog.setSize(...), e. g. by setting a constant size or by using the size of the FX scene that could be determined using root.getBoundsInLocal().
I encountered the same behaviour described by #Peter using 1.8.0_121.
I was able to get dialog.pack() to work using a window listener.
dialog.addWindowListener( new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
((JDialog)e.getSource()).pack();
}
});
I'm trying to remove a Rectangle from my window if it is moved to be inside of a certain part of the screen.
This is the error that I got:
Exception in thread "Thread-1539" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-1539
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:238)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:400)
at javafx.scene.Parent$1.onProposedChange(Parent.java:245)
at com.sun.javafx.collections.VetoableObservableList.remove(VetoableObservableList.java:172)
at com.sun.javafx.collections.ObservableListWrapper.remove(ObservableListWrapper.java:263)
at com.sun.javafx.collections.VetoableObservableList.remove(VetoableObservableList.java:179)
at MovementSample$HandListener.onFrame(MovementSample.java:136)
at com.leapmotion.leap.LeapJNI.SwigDirector_Listener_onFrame(LeapJNI.java:495)
This is the snippet of code that cause the issue:
if(areOverlapping(sauceRectangle, pizzaInside)) {
if(isHolding == null) {
Group g = (Group) scene.getRoot().getChildrenUnmodifiable().get(1);
g.getChildren().remove(sauceRectangle);
}
}
where areOverlapping() is just a method that checks some logic - the issue isn't there.
My question is this: How do I remove a rectangle from my screen if I have the scene. Also, what did I do wrong in my code?
The error says it
IllegalStateException: Not on FX application thread
You are trying to do an operation which should be done on JavaFX Application thread and you are not on it.
In order to execute things on JavaFX Application thread, surround them with Platform.runLater
Platform.runLater(new Runnable() {
#Override
public void run() {
//Code to be executed on JavaFX App Thread
}
});
For more information on Modifying UI components in JavaFX
In JME I try to use threading but when I run the program the function never starts.
I have a server socket who is listening to input from Netbeans.
Listener
while (isRunning) {
//Reads and prints the input
String receivedString = (String) in.readObject();
System.out.println(receivedString);
String[] parts = receivedString.split(";");
if(parts[0].equals("craneCon"))
{
final int containerId = Integer.parseInt(parts[1]);
m.enqueue(new Callable<Spatial>(){
public Spatial call() throws Exception{
m.removeContainersFromMaritime(containerId);
return null;
}
});
}
So in the main there is the function removeContainersFromMaritime
public void removeContainersFromMaritime(final int idContainer)
{
Node container = Maritime.listOfContainers.get(idContainer);
martime.detachChild(Maritime.listOfContainers.get(idContainer));
seagoingcrane.attachChild(Maritime.listOfContainers.get(idContainer));
container.setLocalTranslation(0,5,0);
System.out.println(Maritime.listOfContainers.get(0).getWorldTranslation().z);
}
The connection is alright but the method is never executed. How can I fix this?
jMonkeyEngine uses a swing-style threading model where there is a single render thread that does all the work. Any changes to the scene graph have to be done from that render thread.
To get into the render thread you can implement AppStates, Controls or you can enqueue Callables which are then executed on the render thread in a similar way to Swing's invokeLater.
The code snippet you posted looks about right, so assuming m is your running jME3 SimpleApplication then m.enqueue() will cause the enqueued callable to be executed next time around the render loop (i.e. at the start of the next frame).
If you are not seeing it executed then either:
Your application is not running
You created more than one application and enqueued it to the wrong one
The code is actually running and you just think it isn't.
Stepping through the code in the debugger and/or adding debug statements (for example breakpoint inside removeContainersFromMaritime to see if it is actually called should allow you to narrow this down.
I might be missing something but what is "m" in m.enqueue(...)?
I'm guessing it is an executor service of some sort and it's probably where the problem lies.
You could try instead:
new Thread() {public void run()
{
m.removeContainersFromMaritime(containerId);
}}.start();
It will at least show you if the problem is coming from "m" as an executor.