0

I am following on from a previous question: link

  • I am writing a standalone visualization package for a simulation package
  • The simulation package is written by one of our team in Scala

What I want to do, is create a matplotlib like package for this simulation package. My envisioned end use would look something like matplotlib:

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()

plt.plot([10, 20, 3000, 4121212])
plt.ylabel('some numbers')
plt.show()

For my package, I would do something analogous. Assume here that myFXPackage is a chart package written in ScalaFX. In my DRIVER class:

Import myFXPackage
// run some simulation code here...
myFXPackage.plot(results)

// run some MORE simulation code here...
myFXPackage.plot(results)

Now it seems that for ScalaFX, there can only be one entry point for the whole app; this is the JFXApp class. However, I want to import the package and simply run this code multiple times in my DRIVER class as shown above. So somehow how DRIVER class would call ScalaFX and run the plot, close it, then run another plot.

Is this feasible? If so how would I go about doing this?

finite_diffidence
  • 893
  • 2
  • 11
  • 20
  • Not super familiar with JavaFX but with swing you can just make multiple JFrames, and close them as needed. – PiRocks Jun 03 '20 at 21:26
  • It’s almost the same in JavaFX: you can just create new `Stage`a and `show()` them. Just like in Swing, you *must* do that on the UI thread. The only difference is that in JavaFX, you have to explicitly start the JavaFX toolkit first. I don’t know Scala, so are JavaFX-only answers ok? – James_D Jun 03 '20 at 22:01
  • Yes @James_D, JavaFX is ok. But the issue is I need the package to basically be imported into another, main program. From what I understand JavaFX / ScalaFX hijacks the main right? Is there a way where I can use JavaFX to simply construct the charts, render but not affect my main program that does other stuff? – finite_diffidence Jun 03 '20 at 22:06
  • @finite_diffidence Yes, if I understand the requirements correctly. See if the posted answer helps. – James_D Jun 03 '20 at 22:43

1 Answers1

2

Most JavaFX example code conflates the main method with the Application subclass, and in many cases even does the UI layout, etc., in the same class. There's not necessarily a reason to do this, and in the latter case it's not a particularly good design.

Assuming you separate concerns appropriately in your UI code, you might have a class like

public class MainUI {

    private BorderPane root ;

    public MainUI() {
        root = new BorderPane();
        // do layout, register event handlers, etc etc
    }

    public Pane getView() {
        return root ;
    }
}

Then if you want a standalone JavaFX application, you would do

public class JavaFxApp extends Application {

    @Override
    public void start(Stage stage) {
        MainUI ui = new MainUI();
        Scene scene = new Scene(ui.getView());
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

But this isn't the only place you can use the UI class. In JavaFX (as in most UI toolkits), you must create a new stage, and perform other UI tasks, on the UI thread (i.e. the FX Application Thread).

Additionally, in JavaFX you must explicitly start up the FX toolkit. In the code above, this is done by the Application.launch() method.

JavaFX 9 introduced a Platform.startup(Runnable), which starts the FX toolkit and executes the supplied Runnable on the FX Application Thread.

So, using JavaFX 9 and later, you can have code like

public class SomeApp {

    public static void main(String[] args) {
        // start FX toolkit
        // Since we don't want it to exit when we close a window, set 
        // implicit exit to false:
        Platform.startup(() -> Platform.setImplicitExit(false));

        // do some other stuff...

        // whenever you need to:
        Platform.runLater(SomeApp::showStage);

        // do other stuff...
    }

    private static void showStage() {
        Scene scene = new Scene(new MainUI().getView());
        Stage stage = new Stage();
        stage.show();
    }
}

Prior to JavaFX 9, you can still do this, but you need to launch a "blank" application:

public class FXStartup extends Application {

    @Override
    public void start(Stage ignored) {
        // probably need this:
        Platform.setImplicitExit(false);
    }
}

Note that the launch() method blocks until the toolkit exits, so your application needs to start it in another thread:

public class SomeApp {

    public static void main(String[] args) {
        // start FX toolkit
        new Thread(() -> Application.launch(FXStartup.class, args)).start();

        // do some other stuff...

        // whenever you need to:
        Platform.runLater(SomeApp::showStage);

        // do other stuff...
    }

    private static void showStage() {
        Scene scene = new Scene(new MainUI().getView());
        Stage stage = new Stage();
        stage.show();
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Thanks @James_D that is a great answer. One question though, what is Platform.runLater(SomeApp::showStage); doing? In my IDE it shows up as a type error (I run as Platform.runLater(this.showStage()) as I am using Scala) – finite_diffidence Jun 03 '20 at 23:29
  • @finite_diffidence It’s a method reference. It’s equivalent to `Platform.runLater(() -> SomeApp.showStage());` In Java, any time you have a lambda expression which simply calls a method with the same signature as the method in the target FunctionalInterface, you can replace it with a method reference. (The idea is to mimic languages where you can pass functions as parameters.) – James_D Jun 03 '20 at 23:30