following Scenario:
JavaFxMainApp
JavaFXUpdaterApp
Both are JavaFX applications with a GUI and a void main() method.
The Updater has to be able to start the JavaFXMainApp by accessing
the JavaFxMainApp.jar ONLY knowing about the main class -> it can only! call
main().
The JavaFxMainApp has to also be able to run on its own, by starting
main().
I cannot start multiple VMS and the two apps have no means of communication.
Problem with this is:
Application.launch() can only be executed once per JVM.
The standard javaFx way to start the app:
Updater + MainApp
public static void main(String[] args) {
launch(args);
}
Here it is impossible to fullwill Requirement 1). As both main() methods call launch().
A second approach i found is:
Updater + MainApp
public static void main(String[] args) {
Application app2 = JavaFxMainApp.class.newInstance();
Stage anotherStage = new Stage();
app2.start(anotherStage);
}
First off, i lose the possibility to pass args, but i can live with that as they would not be used anyways.
Major Problem here is, that this code ONLY works, if the JVM already has a JavaFx Thread running, hence it requires that launch() has been called in the JVM at some point before. This is not the case as none of the two calls launch() anymore.
Hybrid approach:
Updater calls launch(), MainApp takes the second approach
Requirement 2) cannot be fulfilled, als starting MainApp without launcher Updater is impossible now.
Another idea i had where dirty "try launch() catch()-> try second approach() in both apps, but that couples both apps and make the setup less flexible.
Is there a way to accomplish this without having to override JavaFxs' LauncherImpl or Application classes to fit these needs?
Can you do something like this:
public class MainApp extends Application {
private Parent uiContent ;
public static final double DEFAULT_WIDTH = 800 ;
public static final double DEFAULT_HEIGHT = 600 ;
public Parent getContent() {
if (uiContent == null) {
uiContent = initializeUI();
}
return uiContent ;
}
public Scene createScene() {
return new Scene(getContent(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
public void initializeAndShowStage(Stage stage) {
stage.setScene(createScene());
stage.show();
}
private Parent initializeUI() {
// probably wise to check we are on the FX Application thread here...
Pane root = ... ;
// build ui....
return root ;
}
#Override
public void start(Stage primaryStage) throws Exception {
initializeAndShowStage(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
And
public class UpdaterApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// whatever you need to launch the updater app here...
}
// invoke from the FX Application Thread to "start" main app:
private void showMainApp(Stage stage) {
MainApp app = new MainApp();
app.initializeAndShowStage(stage);
}
private void showMainApp() {
showMainApp(new Stage());
}
public static void main(String[] args) {
launch(args);
}
}
This would be the far preferred approach. If you have requirements that force you to call main, then you could try something like this:
public class MainApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// .... build UI etc
}
public static void main(String[] args) throws Exception {
if (Platform.isFXApplicationThread()) {
Stage someStage = new Stage();
MainApp app = new MainApp();
app.start(stage);
} else {
launch(args);
}
}
}
Then your updater app can just call MainApp().main(new String[0])); from the FX Application Thread.
This feels like a bit of a hack though.
You want your MainApp to start of its own and you also want an UpdateApp to start the MainApp when required then you can follow my step. I have tried and this model is working.
This is the starting point. You need to call this Class to begin your application
public class StartAnApp {
public static void main(String[] args){
new Thread(new MainApp()).start(); // this will call your MainApp
}
}
This is the First Application which will start. As to start a JavaFX Application you need to have a main() methods. So ensure to provide a main method in this class.
public class MainApp extends Application implements Runnable{
public MainApp(){} // constructor
#Override
public void start(Stage stage){
Text text = new Text("MainApp");
Button startUpdate = new Button("Start Update");
// When this button is pressed. It will launch UpdateApp Application
startUpdate.setOnAction( e -> {
Platform.runLater(new Runnable(){
#Override
public void run(){
new UpdateApp().start(new Stage());
}
});
});
Group root = new Group(text);
Scene scene = new Scene(root,300,400);
stage.setScene(scene);
stage.setX(0);
stage.setY(0);
stage.show();
}
// This method will be used when you first start an Application for
// which main method is required
public static void main(String[] args){
launch(args);
}
// This will be used when you call this Application from another JavaFX application
// if you have closed this application before
#Override
public void run(){
launch();
}
}
This is your UpdateApp. This method does not have main() method.
public class UpdateApp extends Application implements Runnable{
public UpdateApp(){} // constructor
#Override
public void start(Stage stage){
Text text = new Text("UpdateApp");
Button startAnother = new Button("Start Another");
// When this button is pressed. It will launch MainApp Application or you can add any other JavaApplication
startAnother.setOnAction( e -> {
Platform.runLater(new Runnable(){
#Override
public void run(){
new MainApp().start(new Stage());
}
});
});
Group root = new Group(text);
Scene scene = new Scene(root,300,400);
stage.setScene(scene);
stage.setX(350);
stage.setY(0);
stage.show();
}
// This will be used when you call this Application from another JavaFX application
#Override
public void run(){
launch();
}
}
Related
Im trying to bind a label to some property that is modified outside the java application Thread and it throws not an fx application thread. I read the javafx concurrency documentation but Im honestly having a hard time of understanding it or how to implement it in my situation.
public class testApplication extends Application {
private final StringProperty someString = new SimpleStringProperty("inicial value");
#Override
public void start(Stage stage) throws IOException {
Label testLabel = new Label("");
VBox testBox = new VBox(testLabel);
Scene scene = new Scene(testBox);
testLabel.textProperty().bind(someStringProperty());
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
setSomeString("new value");
}
});
}
// getters and setters
}
Consider a non-fx existing application, let's call it Business.
Business exposes a Model object, which in turn exposes some properties. Model also accepts listeners to those properties.
My question is about adding JavaFx gui to such application. The GuiApp obviously extends javafx.application.Application and will need a reference to a Model object.
Searching for a solution for passing a non-String parameter to GuiApp I found several different approaches:
Static approach : for example have Business initialize a static reference to Model in GuiApp. One example of the use of statics can be seen here .
JavaFx 9 approach: as demonstrated here you can launch JavaFx application without extending Application.
Change workflow approach: change the existing workflow to have GuiApp initialize BusinessApp . One example of such workflow can be seen here.
Are there another viable approaches ? Best practice ?
I'll try to demonstrate some different approaches for passing a reference between a java program, and a java-fx program.
I post it in hope it will help some future readers having similar need. I also hope it may encourage other answers with additional solutions.
The posted code should not be considered proper implementation, but rather a short code aiming to clarify the different approaches. For this purpose I'll introduce a simple listening interface :
interface Observe{ void update(int i); }
A java class, that represents an exiting business application :
public class JavaApp {
private Observe observer; private int counter = 0;
JavaApp(Observe observer){ //not null safe
this.observer = observer;
}
void process() {
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
observer.update(counter >=100 ? 0 : ++counter);
}
}, 1000,1000);
}
}
A java-fx application that should be added to the existing business application, listen to it and serve as view:
public class JavaFxApp extends Application implements Observe{
private Label label;
#Override public void start(Stage stage) {
label = new Label("waiting");
BorderPane pane = new BorderPane(label);
Scene scene = new Scene(pane, 100, 100);
stage.setScene(scene);
stage.show();
}
#Override public void update(int i) {
Platform.runLater(()-> label.setText(String.valueOf(i)));
}
}
How do we share a reference, in this case a reference to Observe instance, between the two applications ?
Approach 1: Consider the start() method as the entry point to the application (see James_D answer)
This is simple and straight forward if you want to tie the existing java application with java-fx and use java-fx Application as the entry point:
public class JavaFxApp extends Application implements Observe{
private Label label;
#Override public void start(Stage stage) {
JavaApp main = new JavaApp(this);
label = new Label("waiting");
BorderPane pane = new BorderPane(label);
Scene scene = new Scene(pane, 100, 100);
stage.setScene(scene);
stage.show();
new Thread(()-> { main.process();}).start(); //launch the business process
}
#Override public void update(int i) {
Platform.runLater(()-> label.setText(String.valueOf(i)));
}
public static void main(String[] args) { launch(); }
}
Approach 2: Use JavaFX 9 Platform#startup
This is the best solution I found, when you can not use the Application#start method as the entry point to the application.
As demonstrated in fabians answer, as off java-fx 9 you can launch without extending Application. All you have to do is modify the main of the java application:
public class JavaApp {
private Observe observer; private int counter = 0;
JavaApp(Observe observer){//not null safe
this.observer = observer;
}
void process() {
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override public void run() {
observer.update(counter >=100 ? 0 : ++counter);
}
}, 1000,1000);
}
public static void main(String[] args) {
JavaFxApp view = new JavaFxApp(); //initialize JavaFx application
JavaApp main = new JavaApp(view);
Platform.startup(() -> {//launch JavaFx application
Stage stage = new Stage();
try {
view.start(stage);
} catch (Exception ex) {ex.printStackTrace();}
});
main.process(); //run business process
}
}
Approach 3: Use Static members
For example introduce a static getter in the java-fx application :
public class JavaFxApp extends Application {
private static Label label = new Label("waiting");
#Override public void start(Stage stage) {
BorderPane pane = new BorderPane(label);
Scene scene = new Scene(pane, 100, 100);
stage.setScene(scene);
stage.show();
}
static Observe getObserver() {
return JavaFxApp::update;
}
private static void update(int i) {
Platform.runLater(()-> label.setText(String.valueOf(i)));
}
}
and use it in the java application:
public class JavaApp {
private Observe observer; private int counter = 0;
JavaApp(Observe observer){//not null safe
this.observer = observer;
}
void process() {
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
observer.update(counter >=100 ? 0 : ++counter);
}
}, 1000,1000);
}
public static void main(String[] args){
new Thread(()-> Application.launch(JavaFxApp.class)).start();
Observe observer = JavaFxApp.getObserver(); //get static observer reference
JavaApp main = new JavaApp(observer);
main.process();
}
}
A better approach to get a static reference might be (based on this answer) :
public class JavaFxApp extends Application implements Observe{
private static final CountDownLatch latch = new CountDownLatch(1);
private static Observe observer = null;
private Label label;
#Override public void init() {
observer = this;
latch.countDown();
}
#Override public void start(Stage stage){
label = new Label("waiting");
BorderPane pane = new BorderPane(label);
Scene scene = new Scene(pane, 100, 100);
stage.setScene(scene);
stage.show();
}
#Override public void update(int i) {
Platform.runLater(()-> label.setText(String.valueOf(i)));
}
static Observe getObserver() {
try {
latch.await();
} catch (InterruptedException e) { e.printStackTrace(); }
return observer;
}
}
How to call the launch() more than once in java i am given an exception as "ERROR IN MAIN:java.lang.IllegalStateException: Application launch must not be called more than once"
I have create rest cleint in my java application when request comes it call javafx and opening webview after completing webview operarion am closing javafx windows using Platform.exit() method. when second request comes am getting this error how to reslove this error.
JavaFx Application Code:
public class AppWebview extends Application {
public static Stage stage;
#Override
public void start(Stage _stage) throws Exception {
stage = _stage;
StackPane root = new StackPane();
WebView view = new WebView();
WebEngine engine = view.getEngine();
engine.load(PaymentServerRestAPI.BROWSER_URL);
root.getChildren().add(view);
engine.setJavaScriptEnabled(true);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
engine.setOnResized(new EventHandler<WebEvent<Rectangle2D>>() {
public void handle(WebEvent<Rectangle2D> ev) {
Rectangle2D r = ev.getData();
stage.setWidth(r.getWidth());
stage.setHeight(r.getHeight());
}
});
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("app", new BrowserApp());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
RestClient Method:
Calling to JavaFX application
// method 1 to lanch javafx
javafx.application.Application.launch(AppWebview.class);
// method 2 to lanch javafx
String[] arguments = new String[] {"123"};
AppWebview .main(arguments);
You can't call launch() on a JavaFX application more than once, it's not allowed.
From the javadoc:
It must not be called more than once or an exception will be thrown.
Suggestion for showing a window periodically
Just call Application.launch() once.
Keep the JavaFX runtime running in the background using Platform.setImplicitExit(false), so that JavaFX does not shutdown automatically when you hide the last application window.
The next time you need another window, wrap the window show() call in Platform.runLater(), so that the call gets executed on the JavaFX application thread.
For a short summary implementation of this approach:
See the answer by sergioFC
If you are mixing Swing you can use a JFXPanel instead of an Application, but the usage pattern will be similar to that outlined above.
For an example of the JFXPanel apprach, see Irshad Babar
s answer.
Wumpus Sample
This example is bit more complicated than it needs to be because it also involves timer tasks. However it does provide a complete stand-alone example, which might help sometimes.
import javafx.animation.PauseTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.*;
// hunt the Wumpus....
public class Wumpus extends Application {
private static final Insets SAFETY_ZONE = new Insets(10);
private Label cowerInFear = new Label();
private Stage mainStage;
#Override
public void start(final Stage stage) {
// wumpus rulez
mainStage = stage;
mainStage.setAlwaysOnTop(true);
// the wumpus doesn't leave when the last stage is hidden.
Platform.setImplicitExit(false);
// the savage Wumpus will attack
// in the background when we least expect
// (at regular intervals ;-).
Timer timer = new Timer();
timer.schedule(new WumpusAttack(), 0, 5_000);
// every time we cower in fear
// from the last savage attack
// the wumpus will hide two seconds later.
cowerInFear.setPadding(SAFETY_ZONE);
cowerInFear.textProperty().addListener((observable, oldValue, newValue) -> {
PauseTransition pause = new PauseTransition(
Duration.seconds(2)
);
pause.setOnFinished(event -> stage.hide());
pause.play();
});
// when we just can't take it anymore,
// a simple click will quiet the Wumpus,
// but you have to be quick...
cowerInFear.setOnMouseClicked(event -> {
timer.cancel();
Platform.exit();
});
stage.setScene(new Scene(cowerInFear));
}
// it's so scary...
public class WumpusAttack extends TimerTask {
private String[] attacks = {
"hugs you",
"reads you a bedtime story",
"sings you a lullaby",
"puts you to sleep"
};
// the restaurant at the end of the universe.
private Random random = new Random(42);
#Override
public void run() {
// use runlater when we mess with the scene graph,
// so we don't cross the streams, as that would be bad.
Platform.runLater(() -> {
cowerInFear.setText("The Wumpus " + nextAttack() + "!");
mainStage.sizeToScene();
mainStage.show();
});
}
private String nextAttack() {
return attacks[random.nextInt(attacks.length)];
}
}
public static void main(String[] args) {
launch(args);
}
}
Update, Jan 2020
Java 9 added a new feature called Platform.startup(), which you can use to trigger startup of the JavaFX runtime without defining a class derived from Application and calling launch() on it. Platform.startup() has similar restrictions to the launch() method (you cannot call Platform.startup() more than once), so the elements of how it can be applied is similar to the launch() discussion and Wumpus example in this answer.
For a demonstration on how Platform.startup() can be used, see Fabian's answer to How to achieve JavaFX and non-JavaFX interaction?
I use something like this, similar to other answers.
private static volatile boolean javaFxLaunched = false;
public static void myLaunch(Class<? extends Application> applicationClass) {
if (!javaFxLaunched) { // First time
Platform.setImplicitExit(false);
new Thread(()->Application.launch(applicationClass)).start();
javaFxLaunched = true;
} else { // Next times
Platform.runLater(()->{
try {
Application application = applicationClass.newInstance();
Stage primaryStage = new Stage();
application.start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
try this, I tried this and found successful
#Override
public void start() {
super.start();
try {
// Because we need to init the JavaFX toolkit - which usually Application.launch does
// I'm not sure if this way of launching has any effect on anything
new JFXPanel();
Platform.runLater(new Runnable() {
#Override
public void run() {
// Your class that extends Application
new ArtisanArmourerInterface().start(new Stage());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
I want to create basic JUnit test for JavaFX 8 application. I have this simple code sample:
public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tabs");
Group root = new Group();
Scene scene = new Scene(root, 400, 250, Color.WHITE);
TabPane tabPane = new TabPane();
BorderPane borderPane = new BorderPane();
for (int i = 0; i < 5; i++) {
Tab tab = new Tab();
tab.setText("Tab" + i);
HBox hbox = new HBox();
hbox.getChildren().add(new Label("Tab" + i));
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);
tabPane.getTabs().add(tab);
}
// bind to take available space
borderPane.prefHeightProperty().bind(scene.heightProperty());
borderPane.prefWidthProperty().bind(scene.widthProperty());
borderPane.setCenter(tabPane);
root.getChildren().add(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I only have this code so far:
import javafx.application.Application;
import javafx.stage.Stage;
import org.junit.BeforeClass;
public class BasicStart extends Application {
#BeforeClass
public static void initJFX() {
Thread t = new Thread("JavaFX Init Thread") {
#Override
public void run() {
Application.launch(BasicStart.class, new String[0]);
}
};
t.setDaemon(true);
t.start();
}
#Override
public void start(Stage primaryStage) throws Exception {
// noop
}
}
Can you tell me how I can create JUnit test for the above code?
I use a Junit Rule to run unit tests on the JavaFX thread. The details are in this post. Just copy the class from that post and then add this field to your unit tests.
#Rule public JavaFXThreadingRule javafxRule = new JavaFXThreadingRule();
This code works for both JavaFX 2 and JavaFX 8.
The easiest aproach is the following:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.stage.Stage;
import org.junit.Test;
public class BasicStart {
#Test
public void testA() throws InterruptedException {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
new JFXPanel(); // Initializes the JavaFx Platform
Platform.runLater(new Runnable() {
#Override
public void run() {
new Main().start(new Stage()); // Create and
// initialize
// your app.
}
});
}
});
thread.start();// Initialize the thread
Thread.sleep(10000); // Time to use the app, with out this, the thread
// will be killed before you can tell.
}
}
Hope it helps!
Based on Brian Blonski 's answer I created a JUnit-Testrunner, that does essentially the same thing, but is a bit simpler to use in my opinion.
Using it, your test would look like this:
#RunWith( JfxTestRunner.class )
public class MyUnitTest
{
#Test
public void testMyMethod()
{
//...
}
}
I'm trying to find a way to access the Stage in my main JavaFx class from another class so I can perform some actions on it but I can't since it is passed as a parameter like so:
#Override
public void start(final Stage primaryStage) {
The WakiliProject Class in full:
public class WakiliProject extends Application {
#Override
public void start(final Stage primaryStage) {
Group root = new Group();
StageDraggable.stageDraggable(root, primaryStage);
root.getChildren().addAll(mainContainer);
Scene scene = new Scene(root, 900, 654);
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setTitle("Wakili");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I catch the above Stage primaryStage from another Class and do some actions like I do below after initializing the Stage `public Stage newTryEMail;':
public class TryEMailController implements Initializable {
// Initializes the controller class.
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public Stage newTryEMail;
public void newTryEMailStage() throws IOException {
newTryEMail = new Stage();
newTryEMail.initModality(Modality.WINDOW_MODAL);
newTryEMail.initOwner(AddNewEmailController.newComposeNewEmail);
Parent newTryEMailRoot = FXMLLoader.load(getClass().getResource("/wakiliproject/Forms/AddNew/NewEmail/TryEMailController.fxml"));
StageDraggable.stageDraggable(newTryEMailRoot, newTryEMail);
Scene newComposeNewEmailScene = new Scene(newTryEMailRoot, 590, 670);
newTryEMail.setScene(newComposeNewEmailScene);
newTryEMail.show();
}
}
from another class called TryEMailController?
Thank you all in advance.
Try it like this:
public void newTryEMailStage(Stage primaryStage) throws IOException {
newTryEMail = primaryStage;
And in the start method:
newTryEMailStage(primaryStage);
You pass the primaryStage reference from the start() method to the class that you want to have access to it. Then you store a reference to the primaryStage object in the class that you want to have access to it. If you are trying to "catch" the reference to the primaryStage object prior to the start() method running, there is no way I know of to do that. So organize your code accordingly.