JavaFX中的多个独立阶段
有没有办法在JavaFX中启动多个独立的阶段?所谓独立,我的意思是所有阶段都是从主线程创建的 目前,我的应用程序或多或少是一种算法,我希望在执行过程中绘制一些图表,主要是检查结果是否正确/是否需要调试 问题是,我不知道如何独立地创建和显示多个阶段,也就是说,我想这样做JavaFX中的多个独立阶段,javafx,Javafx,有没有办法在JavaFX中启动多个独立的阶段?所谓独立,我的意思是所有阶段都是从主线程创建的 目前,我的应用程序或多或少是一种算法,我希望在执行过程中绘制一些图表,主要是检查结果是否正确/是否需要调试 问题是,我不知道如何独立地创建和显示多个阶段,也就是说,我想这样做 public static void main(){ double[] x = subfunction.dosomething(); PlotUtil.plot(x); //creates a new window
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调用一次。
在第一次调用PlotUtils期间创建某种主阶段,获取对已创建应用程序的引用并从中开始后续阶段->不作为使用Application.launchSomeClass.class工作我无法获取对已创建应用程序实例的引用。
什么样的结构/设计允许我实现这样的PlotUtils功能
更新1:
我提出了以下想法,并想知道在这个解决方案中是否有任何重大错误
所有绘图都要实现的接口
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");
}
}
这里最简单的方法就是重构应用程序,使其从FX应用程序线程驱动。例如,您可以将原始代码块重写为
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。。。仅仅是创造一个舞台,在其中放置一个场景,并展示它
这假设您正在调用的方法不阻塞,但如果阻塞,您只需将它们包装在任务中并调用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();
}
}
这里最简单的方法就是重构应用程序,使其从FX应用程序线程驱动。例如,您可以将原始代码块重写为
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。。。仅仅是创造一个舞台,在其中放置一个场景,并展示它
这假设您正在调用的方法不阻塞,但如果阻塞,您只需将它们包装在任务中并调用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();
}
}
只需创建多个阶段实例,并对其进行呼叫显示。感谢您的反馈。但我仍然必须确保1。该工具包已初始化,并在2。在fx线程而不是主线程上创建阶段。我相应地更新了帖子,非常感谢您的进一步建议。我想我不了解您的整个应用程序生命周期。如果我从头开始编写R接口,我的应用程序将启动。。。方法将显示一个控制台并在主阶段中显示它。当用户输入命令时,它将在事件处理程序中处理该命令,并输出到同一控制台,或者如果需要显示图形,例如,创建一个新阶段来显示该图形。你能这样组织它吗?如果是这样的话,那就很简单了。你当然是对的,但我的用例略有不同。首先,也许是最重要的一点,我不希望controlflow由JavaFX应用程序启动/驱动。我一直在寻找的是一种快速且有希望的、不太脏的方法,可以随时绘制一些图形,而不是像您在R/Matlab脚本中那样,在调试/开发过程中绘制一些中间结果,而不必担心工具包被初始化,确保在JavaFX线程上绘制等等。。。无论如何,我真的很感激你的评论。我很难理解为什么你不使用JavaFX应用程序来驱动它,即使该应用程序没有做任何其他事情:毕竟,你必须用一些东西来驱动它。但是如果你真的不能做到这一点,强迫JavaFX应用程序启动(如果它还没有启动)的快速方法是在AWT事件调度线程上创建一个JFXPanel。只需创建多个阶段实例并调用show就可以了。谢谢你的反馈。但我仍然必须确保1。工具箱
已初始化,为2。在fx线程而不是主线程上创建阶段。我相应地更新了帖子,非常感谢您的进一步建议。我想我不了解您的整个应用程序生命周期。如果我从头开始编写R接口,我的应用程序将启动。。。方法将显示一个控制台并在主阶段中显示它。当用户输入命令时,它将在事件处理程序中处理该命令,并输出到同一控制台,或者如果需要显示图形,例如,创建一个新阶段来显示该图形。你能这样组织它吗?如果是这样的话,那就很简单了。你当然是对的,但我的用例略有不同。首先,也许是最重要的一点,我不希望controlflow由JavaFX应用程序启动/驱动。我一直在寻找的是一种快速且有希望的、不太脏的方法,可以随时绘制一些图形,而不是像您在R/Matlab脚本中那样,在调试/开发过程中绘制一些中间结果,而不必担心工具包被初始化,确保在JavaFX线程上绘制等等。。。无论如何,我真的很感激你的评论。我很难理解为什么你不使用JavaFX应用程序来驱动它,即使该应用程序没有做任何其他事情:毕竟,你必须用一些东西来驱动它。但是如果您真的不能这样做,那么强制JavaFX应用程序启动(如果尚未启动)的快速方法是在AWT事件调度线程上创建一个JFXPanel。