Is it possible to launch a JavaFX application through another JavaFX application? - javafx

Can I know why there is an error when I say.
Stage s = new Stage();
new CaeserCipherFX().start(s);
This is my code below. I need to launch another JavaFX Application from this one. Please help. Thank you.
public class Main extends Application
{
String args[];
#Override
public void start(Stage stage) throws Exception
{
// creating types of encryptions (Button)
Button caeserCipher = new Button("1. Caeser Cipher");
Button runningKeyCipher = new Button("2. Running Key Cipher");
Button trithemiusCipher = new Button("3. Trithemius Cipher");
Button vignereCipher = new Button("4. Vignere Cipher");
//setting styles
caeserCipher.setTextFill(Color.BLUE);
runningKeyCipher.setTextFill(Color.BLUE);
trithemiusCipher.setTextFill(Color.BLUE);
vignereCipher.setTextFill(Color.BLUE);
/*need to add more!*/
//setting action listeners
String arr [] = {"CaeserCipher","RunningKeyCipher","TrithemiusCipher","VignereCipher"};
caeserCipher.setOnAction((ActionEvent event)->{
//open caeser cipher
Stage s = new Stage();
new CaeserCipherFX().start(s);
});
runningKeyCipher.setOnAction((ActionEvent event)->{
//open running key cipher
stage.hide();
});
trithemiusCipher.setOnAction((ActionEvent event)->{
//open trithemius cipher
stage.hide();
});
vignereCipher.setOnAction((ActionEvent event)->{
//open vignere cipher
stage.hide();
});
// creating flowpane(FlowPane)
FlowPane menu = new FlowPane();
menu.setHgap(25);
menu.setVgap(25);
menu.setMargin(caeserCipher, new Insets(20, 0, 20, 20));
//list for Flowpane(ObservableList)
ObservableList list = menu.getChildren();
//adding list to flowpane
list.addAll(caeserCipher,runningKeyCipher,trithemiusCipher,vignereCipher);
//scene for stage
Scene scene = new Scene(menu);
stage.setTitle("Main Menu");
stage.setScene(scene);
// stage.initStyle(StageStyle.UTILITY);
stage.setHeight(100);
stage.setWidth(600);
stage.setResizable(false);
// Show the Stage (window)
stage.show();
}
}
And I want to launch the code below:
public class CaeserCipherFX extends Application
{
#Override
public void start(Stage stage) throws Exception
{//some other code
//some other code
}
}

There is a ubiquitous JavaFX main application thread which takes a while to get used to.
Think of it like the front-end thread. Theoretically, you should use that thread to handle UI updates and complex cpu tasks such as looking up something in a BD or figuring out the 100000th decimal of PI should be done in a background thread. If you don't do this, the UI will become unresponsive until the DB data is returned, or that decimal is found.
public class TestClass extends Application {
public static void main(String[] args) {
System.out.println("here");
Application.launch(TestClass.class, args);
System.out.println("this is called once application launch is terminated.");
}
#Override
public void init() throws Exception {
super.init(); //To change body of generated methods, choose Tools | Templates.
System.out.println("message from init");
}
#Override
public void start(Stage primaryStage) throws Exception { // this is abstract.
System.out.println("message from start");
Platform.exit(); // if you remove this line, the application won't exit.
}
}
Since JavaFX comes with some prerequisites, you need to start you rapplication using a front-end. You can work around this, but technically,
public void start(Stage primaryStage)
is what , for all intensive purposes, starts your program.
From here, you can use the primaryStage to control most of your application. It's a good idea to put a .onCloseRequest() on it in which you call Platform.exit();
If you want to have multiple windows in your application, you could use something like
public class TestClass extends Application {
public static void main(String[] args) {
System.out.println("here");
Application.launch(TestClass.class, args);
System.out.println("this is called once application launch is terminated.");
}
#Override
public void init() throws Exception {
super.init(); //To change body of generated methods, choose Tools | Templates.
System.out.println("message from init");
}
#Override
public void start(Stage primaryStage) throws Exception { // this is abstract.
primaryStage.setScene(new Scene(new TextArea("this is the first stage (window)")));
primaryStage.setTitle("stage 1");
primaryStage.show();
primaryStage.setOnCloseRequest((event) -> {
Platform.exit();
});
Stage secondaryStage = new Stage();
secondaryStage.setTitle("stage 2");
TextArea ta2 = new TextArea("this is a different stage.");
Scene scene = new Scene(ta2);
secondaryStage.setScene(scene);
secondaryStage.show();
primaryStage.setX(200);
secondaryStage.setX(200 + primaryStage.getWidth() + 50);
}
}
This is what I assume you want to do. Basically create a new window whenever you press a button. You can create stages like this.
The reason for which you can't do it your way is because you are attempting to start another javafx thread by invoking new CaeserCipherFX which is an application object, not a Stage.
new CaeserCipherFX().start(s); // this can only be called once.
IF you absolutely want to have 2 distinct applications (note: not application windows), then you need to have 2 distinct processes.
Lastly, the primaryStage parameter used in either examples is in the beginning basically a placeholder (as in it's constructed, but there's nothing really in it... like a new String()). You can use different stage objects as your "primary" UI.
Lastly, if depending on the stuff you want to decrypt, you may need to use background threads if you want to keep the UI responsiveness. For this you will need to check out the concurrency part of the javafx tutorial.

Is it possible to launch a JavaFX application through another JavaFX application? Not really.
Alternatively, you can use java.lang.ProcessBuilder
This class essentially sends command lines to your operating system shell.
You can use it to run something like "java -jar XXX\YYY\CaeserCipherFX.jar" whenever you click a button. (you'll have to build a CaeserCypherFX project into a jar file)
This will create a new JVM. This means no memory state sharing. You can handle this through IPC.

Related

JAVAFX: Get User input using dialog before starting main application window

I have a client application, and I want to get the server address, port, and some other info from the user in order to initialize the controller of the main stage.
Currently my code look like this
public class MemoryGameClient extends Application {
#Override
public void start(Stage mainStage) throws Exception {
FXMLLoader fxml = new FXMLLoader(getClass().getResource("MemoryGameClient.fxml"));
MemoryGameClientController controller = fxml.getController()
Parent root = loader.load();
controller.connect(SERVER_ADDRESS, PORT, GAME_BOARD_SIZE);
Scene scene = new Scene(root);
mainStage.setScene(scene);
mainStage.show()
}
public static void main(String[] args) {
launch(args);
}
}
It works fine using the hardcoded values, but I want to be able to open a DialogPane or something like that to get those values from user before initializing the scene and running the main application logic.
Can I set an empty Scene that launch a dialog and after that quitting and starting the main stage? Can I do that from the controller before mainStage.show()?
(I need the user input not only for connecting the server but also to determine the size of the GridPane in root)

Problems copying the content of a HBox from FXML

in my project, I need to copy the content of an specific HBox from some FXML files acording to the menu clicked, and put it in the HBox related to the content of the main page.
Bellow you can see the hierarchy of the project, and this aproach's goal is to have a global page, changing only the content of this HBox. The HBox in the main and in the other pages has the ID "conteudo".
I was able to copy and paste the content, but it can only happen once. After caling clear() or removeAll() once, it does not work anymore. There is no error also, and I checked and "conteudo" is still there after the remove/clear of it's childrens, any idea why it happens only once?
Note 1: Using ((HBox) fxmlAldeia.lookup("#conteudo")).getChildren() I get acess to the content inside "conteudo".
Note 2: getCenter does not help, because I get only a part of the center.
Note 3: I can not build the content of "conteudo" using java. I do that using Scenebuilder to speedUp, thats why I need to load it from the FXML.
Hierarchy:
Código
package simulador;
public class Aldeia extends Application {
private static Scene sceneAldeia;
protected static BorderPane fxmlAldeia;
protected static BorderPane fxmlEdfPrincipal;
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("TW Fake");
fxmlAldeia = FXMLLoader.load(getClass().getResource("Aldeia.fxml"));
fxmlEdfPrincipal = FXMLLoader.load(getClass().getResource("EdfPrincipal.fxml"));
sceneAldeia = new Scene(fxmlAldeia);
primaryStage.setScene(sceneAldeia);
primaryStage.show();
}
public static void changeScreen(String src) throws IOException {
// limpa conteúdo
((HBox) fxmlAldeia.lookup("#conteudo")).getChildren().clear();
switch (src) {
case "aldeia":
((HBox) fxmlAldeia.lookup("#conteudo")).getChildren()
.addAll(((HBox) fxmlAldeia.lookup("#conteudo")).getChildren());
break;
case "edfPrincipal":
((HBox) fxmlAldeia.lookup("#conteudo")).getChildren()
.addAll(((HBox) fxmlEdfPrincipal.lookup("#conteudo")).getChildren());
break;
}
}
public static void main(String[] args) {
launch(args);
}
}

In JavaFX using JxBrowser invokeAndWaitFinishLoadingMainFrame() method crashes JVM

I'm experimenting with the JXBrowser Chromium browser engine in JavaFX on Mac OS Sierra. I would like to wait until the URL is fully loaded after I call browser.goBack() or browser.goForward() methods so I can check the Navigation History. The simple app below crashes the JVM but the same code works fine in Java (Swing). The same call in a Java swing app works without any issues. Does anyone have any idea why?
public class JavaFXSample extends Application {
#Override
public void init() throws Exception {
// On Mac OS X Chromium engine must be initialized in non-UI thread.
if (Environment.isMac()) {
BrowserCore.initialize();
}
}
#Override
public void start(final Stage primaryStage) {
Browser browser = new Browser();
BrowserView view = new BrowserView(browser);
Scene scene = new Scene(new BorderPane(view), 700, 500);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
Platform.exit();
System.exit(0);
}
});
Browser.invokeAndWaitFinishLoadingMainFrame(browser, new Callback<Browser>
() {
#Override
public void invoke(Browser browser) {
browser.loadURL("http://www.google.com");
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Looks like you've faced a deadlock because you create the Browser instance in heavyweight mode. You can try solving this issue by using the "jxbrowser.ipc.external=true" VM parameter that enables lightweight rendering mode and runs Chromium engine in separate native process to avoid deadlocks in UI thread.

Stuck with event handling Java FX - Controller - Scene builder

I want to use Netbeans Java FX with Scene builder for a measurement application. I have designed a scene with controls. I can handle the events from the UI-controls within the '...Controller.java'.
The 'controller' is the standard piece of code that is referenced in the XML file and gets initialized by the system with:
public void initialize(URL url, ResourceBundle rb) { ..
My problem: how do I access my central, persisting, 'model' objects from within the controller? Or, to be more exact, from the event handlers created within the controller initialize function.
The 'model' object would be created within the application object.
The solution must be trivial, but I have not found a way to
either access the Application from the controller
or access the controller from within the Application.
What am I missing?
(the next question would be how to access the tree of panes within the object hierarchy created by screen builder, e.g. for graphics manipulation on output. Since the objects are not created by own code I can not store references to some of them. Ok, they could perhaps be found and referenced by tree-walking, but there must be a better way!)
Thanks for all insights!
I have used the 2nd approach (access the controller from within the Application) for awhile ago similar to following. In Application class:
//..
private FooController fooController;
private Pane fooPage;
private Model myModel;
#Override
public void start(Stage stage) {
//..
myModel = new Model();
getFooController().updateModel(myModel);
//..
Button button = new Button("Update model with new one");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Model myNewModel = new Model();
getFooController().updateModel(myNewModel);
}
}
// create scene, add fooPage to it and show.
}
private FooController getFooController() {
if (fooController == null) {
FXMLLoader fxmlLoader = new FXMLLoader();
fooPage = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
fooController = (FooController) fxmlLoader.getController();
}
return fooController;
}
Actually the first and second parts of your question is answered JavaFX 2.0 + FXML. Updating scene values from a different Task to the similar question of yours.

JavaFX auto-scroll auto-update text

Newbie question about JavaFX that I haven't been able to answer, despite knowing it must be pretty simple to do and not finding any resources on it anywhere I've looked (tutorials, many of the Oracle online docs, articles, the well-known JavaFX bloggers, etc.)
I'm developing a command line (script) running application and I have successfully gotten output (via ProcessBuilder) from the script that I can display in an ongoing manner, as things happen on the command line. That is, I can do System.out.println(line); all day long, showing the output in the console, which simply returns output from an input stream returned by the 'myProcess' that's running, created like this:
BufferedReader bri = new BufferedReader(new InputStreamReader(myProcess.getInputStream()))
So I am able to see all the output coming back from the script.
I'd like to set-up a JavaFX TextArea or ScrollPane or, not sure what, to display this output text (there's a lot of it, like several thousand lines) as an ongoing 'progress' of what's taking place in the script, as it happens. I have a Scene, I have buttons and get input from this scene to start the script running, but now I'd like to show the result of clicking the button "RUN THIS SCRIPT", so to speak.
I assume I need to create a TextArea as described here or perhaps a TextBuilder would be useful to begin making it. Not sure.
I need a bit of help in how to setup the binding or auto-scroll/auto-update part of this.
Can someone provide me a place to start, to do this with JavaFX? I'd rather not use Swing.
(I'm using JavaFX 2.2, JDK 1.7u7, all the latest stuff, and yes, this is an FXML app--so doing it that way would be preferred.)
UPDATE: Sergey Grinev's answer was very helpful in the binding part. But here is some more detail on what I mean when I ask for "a bit of help in how to setup" -- basically, I need to return control to the main Scene to allow the user to Cancel the script, or to otherwise monitor what's going on. So I'd like to "spawn" the process that runs that script (that is, have some kind of 'free running process'), but still get the output from it. (I wasn't very clear on that in my initial question.)
The technique I'm using here (see below) is to do a waitFor on the process, but of course this means the dialog/Scene is 'hung' while the script executes. I'd like to gain control back, but how do I pass the 'p' (Process) to some other controller piece (or alternatively, simply kick off that other process passing in the parameters to start the script and have it start the script) that will then do the auto-update, via the binding Sergey Grinev mentions--without 'hanging' the Scene/window? Also: Can I then 'stop' this other process if the user requests it?
Here is my current code ('waits' while script--which takes 20-40 min to run!--completes; this is not what I want, I'd like control returned to the user):
public class MyController implements Initializable {
#FXML
private void handleRunScript(ActionEvent event) throws IOException {
ProcessBuilder pb = new ProcessBuilder("myscript.sh", "arg1", "arg2", ...);
Process p = pb.start();
try {
BufferedReader bri = new BufferedReader
(new InputStreamReader(p.getInputStream()));
String line;
while ((line = bri.readLine()) != null) {
System.out.println(line);
textAreaRight.setText(line);
}
bri.close();
p.waitFor();
}
catch (Exception err) {
err.printStackTrace();
}
}
#FXML
private void handleCancel(ActionEvent event) {
doSomethingDifferent();
}
}
To log strings you can use TextArea
To make it asynchronious you need to make a separate thread for output reader.
public class DoTextAreaLog extends Application {
TextArea log = new TextArea();
Process p;
#Override
public void start(Stage stage) {
try {
ProcessBuilder pb = new ProcessBuilder("ping", "stackoverflow.com", "-n", "100");
p = pb.start();
// this thread will read from process without blocking an application
new Thread(new Runnable() {
#Override
public void run() {
try {
//try-with-resources from jdk7, change it back if you use older jdk
try (BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while ((line = bri.readLine()) != null) {
log(line);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
stage.setScene(new Scene(new Group(log), 400, 300));
stage.show();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public void stop() throws Exception {
super.stop();
// this called on fx app close, you may call it in user action handler
if (p!=null ) {
p.destroy();
}
}
private void log(final String st) {
// we can access fx objects only from fx thread
// so we need to wrap log access into Platform#runLater
Platform.runLater(new Runnable() {
#Override
public void run() {
log.setText(st + "\n" + log.getText());
}
});
}
public static void main(String[] args) {
launch();
}
}

Resources