Multiple independent stages in JavaFX -
is there way launch multiple independent stages in javafx? independent mean stages created main thread.
at moment application more or less algorithm plot charts , tables during execution (mainly check whether results correct/ debug).
the problem cannot figure out how create , show multiple stages independently, i.e. this
public static void main(){ double[] x = subfunction.dosomething(); plotutil.plot(x); //creates new window , shows chart/table etc. double[] y = subfunction.dosomethingelse(); plotutil.plot(y); //creates new window , shows chart/table etc. ..... }
which allow use plotutil
1 use plotting functions in other scripting languages (like matlab or r).
so main question how "design" plotutils
? far tried 2 things
- plotutils uses application.launch each plot call (creating new stage single scene every time) --> not work
application.launch
can invoked once. - create kind of "main stage" during first call plotutils, reference created application , start subsequent stages there --> not work using
application.launch(someclass.class)
not able reference created application instance.
what kind structure/design allow me implement such plotutils function?
update 1:
i came following idea , wondering whether there major mistakes in solution.
interface implemented "plots"
public abstract class qpmapplication implements stagecreator { @override public abstract stage createstage(); }
plotting functionality:
public class plotstage { public static boolean toolkitinialized = false; public static void plotstage(string title, qpmapplication stagecreator) { if (!toolkitinialized) { thread appthread = new thread(new runnable() { @override public void run() { application.launch(initapp.class); } }); appthread.start(); } while (!toolkitinialized) { try { thread.sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } } platform.runlater(new runnable() { @override public void run() { stage stage = stagecreator.createstage(); stage.show(); } }); } public static class initapp extends application { @override public void start(final stage primarystage) { toolkitinialized = true; } } }
using it:
public class plotstagetest { public static void main(string[] args) { qpmapplication qpm1 = new qpmapplication() { @override public stage createstage() { stage stage = new stage(); stackpane root = new stackpane(); label label1 = new label("label1"); root.getchildren().add(label1); scene scene = new scene(root, 300, 300); stage.settitle("first stage"); stage.setscene(scene); return stage; } }; plotstage.plotstage(qpm1); qpmapplication qpm2 = new qpmapplication() { @override public stage createstage() { stage stage = new stage(); stackpane root = new stackpane(); label label1 = new label("label2"); root.getchildren().add(label1); scene scene = new scene(root, 300, 200); stage.settitle("second stage"); stage.setscene(scene); return stage; } }; plotstage.plotstage(qpm2); system.out.println("done"); } }
the easiest approach here refactor application driven fx application thread. example, rewrite original code block as
public class main extends application { @override public void start(stage primarystageignored) { double[] x = subfunction.dosomething(); plotutil.plot(x); //creates new window , shows chart/table etc. double[] y = subfunction.dosomethingelse(); plotutil.plot(y); //creates new window , shows chart/table etc. // ..... } public static void main(string[] args) { launch(args); } }
now plotutil.plot(...)
merely creates stage
, puts scene
in it, , show()
s it.
this assumes methods you're calling don't block, if have wrap them in task
, call plotutils.plot(...)
in onsucceeded
handler task.
if want drive non-javafx application, there's well-known hack force javafx application thread start if it's not started, creating new jfxpanel
. jfxpanel
should created on awt event dispatch thread.
here's basic example of second technique. start application , type "show" console. (type "exit" exit.)
import java.util.scanner; import java.util.concurrent.futuretask; import javafx.application.platform; import javafx.embed.swing.jfxpanel; import javafx.scene.scene; import javafx.scene.control.button; import javafx.scene.layout.stackpane; import javafx.stage.stage; import javax.swing.swingutilities; public class main { private jfxpanel jfxpanel ; public void run() throws exception { boolean done = false ; try (scanner scanner = new scanner(system.in)) { while (! done) { system.out.println("waiting command..."); string command = scanner.nextline(); system.out.println("got command: "+command); switch (command.tolowercase()) { case "exit": done = true; break ; case "show": showwindow(); break; default: system.out.println("unknown command: commands \"show\" or \"exit\""); } } platform.exit(); } } private void showwindow() throws exception { ensurefxapplicationthreadrunning(); platform.runlater(this::_showwindow); } private void _showwindow() { stage stage = new stage(); button button = new button("ok"); button.setonaction(e -> stage.hide()); scene scene = new scene(new stackpane(button), 350, 75); stage.setscene(scene); stage.show(); stage.tofront(); } private void ensurefxapplicationthreadrunning() throws exception { if (jfxpanel != null) return ; futuretask<jfxpanel> fxthreadstarter = new futuretask<>(() -> { return new jfxpanel(); }); swingutilities.invokelater(fxthreadstarter); jfxpanel = fxthreadstarter.get(); } public static void main(string[] args) throws exception { platform.setimplicitexit(false); system.out.println("starting main...."); new main().run(); } }
here more along lines follow, if wanted user interact via os terminal (i.e. using system.in
). uses first technique, application driven fx application
subclass. here create 2 background threads, 1 read commands system.in
, , 1 process them, passing them via blockingqueue
. though nothing displayed in main fx application thread, still bad idea block thread waiting commands. while threading adds small level of complexity, avoids "jfxpanel
" hack, , doesn't rely on there being awt implementation present.
import java.util.scanner; import java.util.concurrent.blockingqueue; import java.util.concurrent.callable; import java.util.concurrent.executorservice; import java.util.concurrent.executors; import java.util.concurrent.linkedblockingqueue; import javafx.application.application; import javafx.application.platform; import javafx.scene.scene; import javafx.scene.control.button; import javafx.scene.layout.stackpane; import javafx.stage.stage; public class fxdriver extends application { blockingqueue<string> commands ; executorservice exec ; @override public void start(stage primarystage) throws exception { exec = executors.newcachedthreadpool(runnable -> { thread t = new thread(runnable); t.setdaemon(true); return t ; }); commands = new linkedblockingqueue<>(); callable<void> commandreadthread = () -> { try (scanner scanner = new scanner(system.in)) { while (true) { system.out.print("enter command: "); commands.put(scanner.nextline()); } } }; callable<void> commandprocessingthread = () -> { while (true) { processcommand(commands.take()); } }; platform.setimplicitexit(false); exec.submit(commandreadthread); exec.submit(commandprocessingthread); } private void processcommand(string command) { switch (command.tolowercase()) { case "exit": platform.exit(); break ; case "show": platform.runlater(this::showwindow); break; default: system.out.println("unknown command: commands \"show\" or \"exit\""); } } @override public void stop() { exec.shutdown(); } private void showwindow() { stage stage = new stage(); button button = new button("ok"); button.setonaction(e -> stage.hide()); scene scene = new scene(new stackpane(button), 350, 75); stage.setscene(scene); stage.show(); stage.tofront(); } public static void main(string[] args) { launch(args); } }
Comments
Post a Comment