有没有办法在JavaFX中启动多个独立阶段?独立的意思是这些阶段都是从主线程创建的。
目前我的应用程序或多或少是一种算法,我想在执行期间绘制一些图表和表格(主要是检查结果是否正确/调试)。
问题是我无法弄清楚如何独立创建和展示多个阶段,即我想做这样的事情
public static void main(){
double[] x = subfunction.dosomething();
PlotUtil.plot(x); //creates a new window and shows some chart/table etc.
double[] y = subfunction.dosomethingelse();
PlotUtil.plot(y); //creates a new window and shows some chart/table etc.
.....
}
允许使用PlotUtil
,因为可以使用其他脚本语言(如Matlab或R)中的绘图函数。
所以主要问题是如何“设计”PlotUtils
?到目前为止,我尝试了两件事
Application.launch
只能调用一次。Application.launch(SomeClass.class)
我无法获得对创建的Application实例的引用。什么样的结构/设计可以让我实现这样的PlotUtils功能?
更新1:
我提出了以下想法,并想知道此解决方案中是否存在重大错误。
所有“Plots”实现的接口
public abstract class QPMApplication implements StageCreator {
@Override
public abstract Stage createStage();
}
绘图功能:
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;
}
}
}
使用它:
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");
}
}
答案 0 :(得分:4)
这里最简单的方法就是重构你的应用程序,以便它从FX Application线程驱动。例如,您可以将原始代码块重写为
public class Main extends Application {
@Override
public void start(Stage primaryStageIgnored) {
double[] x = subfunction.dosomething();
PlotUtil.plot(x); //creates a new window and shows some chart/table etc.
double[] y = subfunction.dosomethingelse();
PlotUtil.plot(y); //creates a new window and shows some chart/table etc.
// .....
}
public static void main(String[] args) {
launch(args);
}
}
现在PlotUtil.plot(...)
只创建Stage
,在其中加Scene
,然后show()
。
这假设你正在调用的方法没有阻塞,但是如果你这样做,你只需要将它们包装在Task
中并在PlotUtils.plot(...)
处理程序中调用onSucceeded
任务。
如果你真的想从非JavaFX应用程序中驱动它,那么通过创建一个新的JFXPanel
,有一个相当着名的黑客可以强制JavaFX应用程序线程启动(如果尚未启动)。应在AWT事件派发线程上创建JFXPanel
。
这是第二种技术的一个非常基本的例子。启动应用程序并在控制台中键入“show”。 (输入“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 for 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 are \"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();
}
}
如果我希望用户通过OS终端进行交互(即使用System.in
),那么这就是我将遵循的更多内容。这使用第一种技术,其中应用程序由FX Application
子类驱动。在这里,我创建了两个后台线程,一个用于从System.in
读取命令,另一个用于处理它们,通过BlockingQueue
传递它们。即使主FX应用程序线程中没有显示任何内容,阻止该线程等待命令仍然是一个非常糟糕的主意。虽然线程增加了一小部分复杂性,但这避免了“JFXPanel
”hack,并且不依赖于存在AWT实现。
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 are \"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);
}
}