Java 在 JavaFX 场景中处理 3 个 PAplet 作为 FXNode
Java Processing 3 PAplet in JavaFX scene as FXNode
我正在尝试制作一个可视化分析分形集的程序。我选择 Processing 3 作为绘图库,并选择 JavaFX 作为用户界面。有一些当前状态的截图:
我的界面:
有启动器代码:
import Graphics.Canvas2D;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import processing.core.PGraphics;
import java.io.IOException;
public class Launcher extends Application {
private static Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Parent root = loadFXML("MainUI.fxml");
Scene scene = new Scene(root, 500, 400);
primaryStage.setTitle("Fractal Analyzer");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setMaximized(true);
Launcher.primaryStage = primaryStage;
}
@Override
public void init() {
}
@Override
public void stop() {
System.exit(0);
}
public static Stage getPrimaryStage() {
return primaryStage;
}
public void setCanvas(Canvas2D canvas){
}
private Parent loadFXML(String path) {
try {
return FXMLLoader.load(getClass().getResource(path));
} catch (IOException e) {
e.printStackTrace();
}
System.exit(1);
return null;
}
}
测试分形 PAplet:
这个PAplet有一个代码:
package Fractal;
import processing.core.PApplet;
public class SirpenskiTriangle extends PApplet {
public static void main(String[] args) {
PApplet.main("Fractal.SirpenskiTriangle");
}
public void settings() {
size(640, 640);
smooth();
if (frame != null) {
frame.setResizable(true);
}
}
public void draw() {
drawTriangle(new Position(300, 20), new Position(620, 620), new Position(20, 620), 0);
noLoop();
scale(10f);
}
public void setup(){}
public void drawTriangle(Position top, Position right, Position left, int depth) {
if (depth > 10) return;
line(top.x, top.y, right.x, right.y);
line(right.x, right.y, left.x, left.y);
line(left.x, left.y, top.x, top.y);
drawTriangle(top, top.middleWith(right), top.middleWith(left), depth + 1);
drawTriangle(top.middleWith(left), left.middleWith(right), left, depth + 1);
drawTriangle(top.middleWith(right), right, left.middleWith(right), depth + 1);
}
class Position {
final float x;
final float y;
Position(float x, float y) {
this.x = x;
this.y = y;
}
Position middleWith(Position other) {
return new Position((x + other.x) / 2, (y + other.y) / 2);
}
}
}
有什么方法可以将处理 PAplet 放入 JavaFX 场景中,例如 canvas 或类似的东西?
我希望它能这样工作,但是这段代码是无效的:
我设计了两种方法:第一种,我们绕过 Processing 的 JavaFX 阶段创建并指向 Processing 以绘制到从 FXML 文件加载的 JavaFX 阶段;在第二步中,我们将 Processing 的默认 JavaFX 场景替换为在运行时 .
从 FXML 文件加载的场景
1。从 FXML
启动
使用第一种方法,我们像启动 JavaFX 应用程序一样启动应用程序(使用 Application.launch(Launcher.class);
),完全绕过 Processing 的 JavaFX 阶段创建代码。
您必须下载稍微修改过的 core.jar 才能使这种方法生效,我在其中更改了 [=] 的一些成员的可见性15=] 和 PGraphicsFX2D
class 从 Protected
到 Public
。这些更改允许我们从我们自己的 ... extends Application
class 启动 JavaFX,同时保持对 Processing 在启动过程中需要设置的成员的访问。
当使用的 JDK 高于 Java 8 时,在 FX2D 模式下处理 3 次崩溃,因此我还制作了一个适用于 8+ 的工作版本,因为 FXML 文件通常至少需要Java 9 点上班。
这是我在此示例中使用的 FXML 文件:
将修改后的 core.jar 添加到项目的 class 路径中,使用 PApplet class 覆盖 initSurface()
以下片段。使用此代码,我们绕过 PApplet 对 initFrame()
的调用 - 这是处理创建自己的 JavaFX 阶段的地方,我们不希望它这样做。
@Override
protected PSurface initSurface() {
g = createPrimaryGraphics();
PSurface genericSurface = g.createSurface();
PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
fxSurface.sketch = this;
Launcher.surface = fxSurface;
new Thread(new Runnable() {
public void run() {
Application.launch(Launcher.class);
}
}).start();
while (fxSurface.stage == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
this.surface = fxSurface;
return fxSurface;
}
像这样将 PApplet 的渲染模式设置为 FX2D:
@Override
public void settings() {
size(0, 0, FX2D);
}
将以下内容或类似内容放入您的启动器 class。在此示例中,我手动找到了要将 canvas 对象添加到的节点。有更好、更程序化的方法来执行此操作(例如 .lookup() 使用所需节点的 fx:id —— 这可以在 FXML 文件中定义) .我还将 canvas 的尺寸绑定到其父级的尺寸,因此当分隔 Master 和 View 窗格的除数是拖动后,Processing canvas 会相应地调整大小。
public class Launcher extends Application {
public static PSurfaceFX surface;
@Override
public void start(Stage primaryStage) throws Exception {
Canvas canvas = (Canvas) surface.getNative(); // boilerplate
GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); // boilerplate
surface.fx.context = graphicsContext; // boilerplate
primaryStage.setTitle("FXML/Processing");
VBox root = FXMLLoader.load(new File("c:/Users/Mike/desktop/test.fxml").toURI().toURL());
SplitPane pane = (SplitPane) root.getChildren().get(1); // Manually get the item I want to add canvas to
AnchorPane pane2 = (AnchorPane) pane.getItems().get(0); // Manually get the item I want to add canvas to
pane2.getChildren().add(canvas); // Manually get the item I want to add canvas to
canvas.widthProperty().bind(pane2.widthProperty());
canvas.heightProperty().bind(pane2.heightProperty());
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
surface.stage = primaryStage; // boilerplate
}
}
这是结果:
另请参阅 this Github 项目——一个基本项目,展示了如何使用第一种方法集成处理草图和 FXML JavaFX 阶段,但包括 JavaFX Controller
填充 @FXML
注释字段(提供一种简单的方法来首先获取,然后在代码中引用 JavaFX 对象)。
2。启动,然后加载 FXML
这种方法适用于原始处理。在这里,我们像往常一样启动 Processing,然后在运行时用从 FXML 文件加载的新场景替换默认场景。这是一种更简单的方法(并且不需要使用修改后的 .jar!)但是会使 JavaFX/Processing 互操作性变得更加困难,因为我们不能使用 JavaFX Controller
通过 FXML 注入获取字段。
示例 PDE 代码:
import java.util.Map;
import java.nio.file.Paths;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import processing.javafx.PSurfaceFX;
public void setup() {
size(800, 800, FX2D);
strokeWeight(3);
}
protected PSurface initSurface() {
surface = (PSurfaceFX) super.initSurface();
final Canvas canvas = (Canvas) surface.getNative();
final Scene oldScene = canvas.getScene();
final Stage stage = (Stage) oldScene.getWindow();
try {
FXMLLoader loader = new FXMLLoader(Paths.get("C:\path--to--fxml\stage.fxml").toUri().toURL()); // abs path to fxml file
final Parent sceneFromFXML = loader.load();
final Map<String, Object> namespace = loader.getNamespace();
final Scene newScene = new Scene(sceneFromFXML, stage.getWidth(), stage.getHeight(), false,
SceneAntialiasing.BALANCED);
final AnchorPane pane = (AnchorPane) namespace.get("anchorPane"); // get element by fx:id
pane.getChildren().add(canvas); // processing to stackPane
canvas.widthProperty().bind(pane.widthProperty()); // bind canvas dimensions to pane
canvas.heightProperty().bind(pane.heightProperty()); // bind canvas dimensions to pane
Platform.runLater(new Runnable() {
@Override
public void run() {
stage.setScene(newScene);
}
}
);
}
catch (IOException e) {
e.printStackTrace();
}
return surface;
}
public void draw() {
background(125, 125, 98);
ellipse(200, 200, 200, 200);
line(0, 0, width, height);
line(width, 0, 0, height);
}
结果:
…使用此 FXML 文件:
要使其正常工作,您必须启动 Processing sketch,而不是 JavaFX 应用程序。
干脆
PApplet.main(Launcher.class.getName());
也非常感谢您的帮助!我不知道我应该如何使用 Processing 附带的 JavaFX 东西!
好的,这是我的代码,行得通!我复制了所有内容并更改了名称。
!!!我没有测试这个修改后的代码,所以不要复制粘贴所有东西!!!
不过,原始原则应该绝对有效。
如果您还有问题或疑问,请发表评论。
主要
public class Main {
public static void main(String[] args) {
// Your code starts here, and runs Processing.
// This is also, how you normally start Processing sketches.
PApplet.main(Sketch.class.getName());
}
}
素描
public class Sketch extends PApplet{
@Override
public void settings() {
size(200, 200, FX2D); // Size doesn't really matter
}
@Override
public void setup() {
}
@Override
public void draw() {
}
// Processing uses this function to determine,
// how to display everything, how to open the canvas...
// We override the code, that would normally open a window with the normal Processing stuff,
// to open start new JavaFX application in a new Thread.
// micycle's code
@Override
protected PSurface initSurface() {
g = createPrimaryGraphics();
PSurface genericSurface = g.createSurface();
PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
fxSurface.sketch = this;
// Because the JavaFX App is being launched by reflection,
// we can't pass variables to it via constructor, so
// we have to access it in static context.
// Here, we give JavaFX the surface.
ExampleApp.surface = fxSurface;
// New thread started, so JavaFX and Processing don't interrupt each other.
new Thread(new Runnable() {
public void run() {
// JavaFX way of launching a new Application
Application.launch(ExampleApp.class);
}
}).start();
while (fxSurface.stage == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
this.surface = fxSurface;
return fxSurface;
}
}
ExampleApp
public class ExampleApp extends Application {
public Canvas canvas; // The Canvas you will be drawing to
public static PSurfaceFX surface; // The Processing surface
// JavaFX started, this method is being run to set everything up.
@Override
public void start(Stage primaryStage) {
// This sets up the canvas, and the drawing region.
canvas = (Canvas) surface.getNative();
surface.fx.context = canvas.getGraphicsContext2D();
surface.stage = primaryStage;
// I'm just loading my FXML file. You can do all JavaFX stuff via code, if you want
try {
// !!My root Container is a BorderPane!!
BorderPane root = FXMLLoader.load(getClass().getClassLoader().getResource("application.fxml"));
} catch(IOException e) {
e.printStackTrace();
}
// Getting the Anchor pane, that is in the center of my BorderPane
AnchorPane pane = (AnchorPane) root.getCenter();
// The Anchor pane is being used, so the canvas can fill the parent (Center)
// Canvases don't have a property to fill it's parent, like most Containers do (Because it isn't a container)
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
// Adding the canvas to your App
root.getChildren().add(canvas);
// Launching the Stage
primaryStage.setTitle("Example App");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
好的,自上次以来,我更改了一些元素。
canvas 的父级现在只是一个 Pane 而不是 AnchorPane。
FXML 对您帮助不大...它只是一个带有另一个窗格的 BorderPane,但是没关系...
<center>
<VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<Pane maxHeight="1.7976931348623157E308" VBox.vgrow="ALWAYS" />
</children>
</VBox>
所以,我正在做的是获取 Canvas 元素,Processing 创建并将其添加到窗格中。
我正在尝试制作一个可视化分析分形集的程序。我选择 Processing 3 作为绘图库,并选择 JavaFX 作为用户界面。有一些当前状态的截图:
我的界面:
有启动器代码:
import Graphics.Canvas2D;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import processing.core.PGraphics;
import java.io.IOException;
public class Launcher extends Application {
private static Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Parent root = loadFXML("MainUI.fxml");
Scene scene = new Scene(root, 500, 400);
primaryStage.setTitle("Fractal Analyzer");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setMaximized(true);
Launcher.primaryStage = primaryStage;
}
@Override
public void init() {
}
@Override
public void stop() {
System.exit(0);
}
public static Stage getPrimaryStage() {
return primaryStage;
}
public void setCanvas(Canvas2D canvas){
}
private Parent loadFXML(String path) {
try {
return FXMLLoader.load(getClass().getResource(path));
} catch (IOException e) {
e.printStackTrace();
}
System.exit(1);
return null;
}
}
测试分形 PAplet:
这个PAplet有一个代码:
package Fractal;
import processing.core.PApplet;
public class SirpenskiTriangle extends PApplet {
public static void main(String[] args) {
PApplet.main("Fractal.SirpenskiTriangle");
}
public void settings() {
size(640, 640);
smooth();
if (frame != null) {
frame.setResizable(true);
}
}
public void draw() {
drawTriangle(new Position(300, 20), new Position(620, 620), new Position(20, 620), 0);
noLoop();
scale(10f);
}
public void setup(){}
public void drawTriangle(Position top, Position right, Position left, int depth) {
if (depth > 10) return;
line(top.x, top.y, right.x, right.y);
line(right.x, right.y, left.x, left.y);
line(left.x, left.y, top.x, top.y);
drawTriangle(top, top.middleWith(right), top.middleWith(left), depth + 1);
drawTriangle(top.middleWith(left), left.middleWith(right), left, depth + 1);
drawTriangle(top.middleWith(right), right, left.middleWith(right), depth + 1);
}
class Position {
final float x;
final float y;
Position(float x, float y) {
this.x = x;
this.y = y;
}
Position middleWith(Position other) {
return new Position((x + other.x) / 2, (y + other.y) / 2);
}
}
}
有什么方法可以将处理 PAplet 放入 JavaFX 场景中,例如 canvas 或类似的东西?
我希望它能这样工作,但是这段代码是无效的:
我设计了两种方法:第一种,我们绕过 Processing 的 JavaFX 阶段创建并指向 Processing 以绘制到从 FXML 文件加载的 JavaFX 阶段;在第二步中,我们将 Processing 的默认 JavaFX 场景替换为在运行时 .
从 FXML 文件加载的场景1。从 FXML
启动使用第一种方法,我们像启动 JavaFX 应用程序一样启动应用程序(使用 Application.launch(Launcher.class);
),完全绕过 Processing 的 JavaFX 阶段创建代码。
您必须下载稍微修改过的 core.jar 才能使这种方法生效,我在其中更改了 [=] 的一些成员的可见性15=] 和 PGraphicsFX2D
class 从 Protected
到 Public
。这些更改允许我们从我们自己的 ... extends Application
class 启动 JavaFX,同时保持对 Processing 在启动过程中需要设置的成员的访问。
当使用的 JDK 高于 Java 8 时,在 FX2D 模式下处理 3 次崩溃,因此我还制作了一个适用于 8+ 的工作版本,因为 FXML 文件通常至少需要Java 9 点上班。
这是我在此示例中使用的 FXML 文件:
将修改后的 core.jar 添加到项目的 class 路径中,使用 PApplet class 覆盖 initSurface()
以下片段。使用此代码,我们绕过 PApplet 对 initFrame()
的调用 - 这是处理创建自己的 JavaFX 阶段的地方,我们不希望它这样做。
@Override
protected PSurface initSurface() {
g = createPrimaryGraphics();
PSurface genericSurface = g.createSurface();
PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
fxSurface.sketch = this;
Launcher.surface = fxSurface;
new Thread(new Runnable() {
public void run() {
Application.launch(Launcher.class);
}
}).start();
while (fxSurface.stage == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
this.surface = fxSurface;
return fxSurface;
}
像这样将 PApplet 的渲染模式设置为 FX2D:
@Override
public void settings() {
size(0, 0, FX2D);
}
将以下内容或类似内容放入您的启动器 class。在此示例中,我手动找到了要将 canvas 对象添加到的节点。有更好、更程序化的方法来执行此操作(例如 .lookup() 使用所需节点的 fx:id —— 这可以在 FXML 文件中定义) .我还将 canvas 的尺寸绑定到其父级的尺寸,因此当分隔 Master 和 View 窗格的除数是拖动后,Processing canvas 会相应地调整大小。
public class Launcher extends Application {
public static PSurfaceFX surface;
@Override
public void start(Stage primaryStage) throws Exception {
Canvas canvas = (Canvas) surface.getNative(); // boilerplate
GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); // boilerplate
surface.fx.context = graphicsContext; // boilerplate
primaryStage.setTitle("FXML/Processing");
VBox root = FXMLLoader.load(new File("c:/Users/Mike/desktop/test.fxml").toURI().toURL());
SplitPane pane = (SplitPane) root.getChildren().get(1); // Manually get the item I want to add canvas to
AnchorPane pane2 = (AnchorPane) pane.getItems().get(0); // Manually get the item I want to add canvas to
pane2.getChildren().add(canvas); // Manually get the item I want to add canvas to
canvas.widthProperty().bind(pane2.widthProperty());
canvas.heightProperty().bind(pane2.heightProperty());
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
surface.stage = primaryStage; // boilerplate
}
}
这是结果:
另请参阅 this Github 项目——一个基本项目,展示了如何使用第一种方法集成处理草图和 FXML JavaFX 阶段,但包括 JavaFX Controller
填充 @FXML
注释字段(提供一种简单的方法来首先获取,然后在代码中引用 JavaFX 对象)。
2。启动,然后加载 FXML
这种方法适用于原始处理。在这里,我们像往常一样启动 Processing,然后在运行时用从 FXML 文件加载的新场景替换默认场景。这是一种更简单的方法(并且不需要使用修改后的 .jar!)但是会使 JavaFX/Processing 互操作性变得更加困难,因为我们不能使用 JavaFX Controller
通过 FXML 注入获取字段。
示例 PDE 代码:
import java.util.Map;
import java.nio.file.Paths;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import processing.javafx.PSurfaceFX;
public void setup() {
size(800, 800, FX2D);
strokeWeight(3);
}
protected PSurface initSurface() {
surface = (PSurfaceFX) super.initSurface();
final Canvas canvas = (Canvas) surface.getNative();
final Scene oldScene = canvas.getScene();
final Stage stage = (Stage) oldScene.getWindow();
try {
FXMLLoader loader = new FXMLLoader(Paths.get("C:\path--to--fxml\stage.fxml").toUri().toURL()); // abs path to fxml file
final Parent sceneFromFXML = loader.load();
final Map<String, Object> namespace = loader.getNamespace();
final Scene newScene = new Scene(sceneFromFXML, stage.getWidth(), stage.getHeight(), false,
SceneAntialiasing.BALANCED);
final AnchorPane pane = (AnchorPane) namespace.get("anchorPane"); // get element by fx:id
pane.getChildren().add(canvas); // processing to stackPane
canvas.widthProperty().bind(pane.widthProperty()); // bind canvas dimensions to pane
canvas.heightProperty().bind(pane.heightProperty()); // bind canvas dimensions to pane
Platform.runLater(new Runnable() {
@Override
public void run() {
stage.setScene(newScene);
}
}
);
}
catch (IOException e) {
e.printStackTrace();
}
return surface;
}
public void draw() {
background(125, 125, 98);
ellipse(200, 200, 200, 200);
line(0, 0, width, height);
line(width, 0, 0, height);
}
结果:
…使用此 FXML 文件:
要使其正常工作,您必须启动 Processing sketch,而不是 JavaFX 应用程序。
干脆
PApplet.main(Launcher.class.getName());
也非常感谢您的帮助!我不知道我应该如何使用 Processing 附带的 JavaFX 东西!
好的,这是我的代码,行得通!我复制了所有内容并更改了名称。 !!!我没有测试这个修改后的代码,所以不要复制粘贴所有东西!!! 不过,原始原则应该绝对有效。
如果您还有问题或疑问,请发表评论。
主要
public class Main {
public static void main(String[] args) {
// Your code starts here, and runs Processing.
// This is also, how you normally start Processing sketches.
PApplet.main(Sketch.class.getName());
}
}
素描
public class Sketch extends PApplet{
@Override
public void settings() {
size(200, 200, FX2D); // Size doesn't really matter
}
@Override
public void setup() {
}
@Override
public void draw() {
}
// Processing uses this function to determine,
// how to display everything, how to open the canvas...
// We override the code, that would normally open a window with the normal Processing stuff,
// to open start new JavaFX application in a new Thread.
// micycle's code
@Override
protected PSurface initSurface() {
g = createPrimaryGraphics();
PSurface genericSurface = g.createSurface();
PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
fxSurface.sketch = this;
// Because the JavaFX App is being launched by reflection,
// we can't pass variables to it via constructor, so
// we have to access it in static context.
// Here, we give JavaFX the surface.
ExampleApp.surface = fxSurface;
// New thread started, so JavaFX and Processing don't interrupt each other.
new Thread(new Runnable() {
public void run() {
// JavaFX way of launching a new Application
Application.launch(ExampleApp.class);
}
}).start();
while (fxSurface.stage == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
this.surface = fxSurface;
return fxSurface;
}
}
ExampleApp
public class ExampleApp extends Application {
public Canvas canvas; // The Canvas you will be drawing to
public static PSurfaceFX surface; // The Processing surface
// JavaFX started, this method is being run to set everything up.
@Override
public void start(Stage primaryStage) {
// This sets up the canvas, and the drawing region.
canvas = (Canvas) surface.getNative();
surface.fx.context = canvas.getGraphicsContext2D();
surface.stage = primaryStage;
// I'm just loading my FXML file. You can do all JavaFX stuff via code, if you want
try {
// !!My root Container is a BorderPane!!
BorderPane root = FXMLLoader.load(getClass().getClassLoader().getResource("application.fxml"));
} catch(IOException e) {
e.printStackTrace();
}
// Getting the Anchor pane, that is in the center of my BorderPane
AnchorPane pane = (AnchorPane) root.getCenter();
// The Anchor pane is being used, so the canvas can fill the parent (Center)
// Canvases don't have a property to fill it's parent, like most Containers do (Because it isn't a container)
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
// Adding the canvas to your App
root.getChildren().add(canvas);
// Launching the Stage
primaryStage.setTitle("Example App");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
好的,自上次以来,我更改了一些元素。 canvas 的父级现在只是一个 Pane 而不是 AnchorPane。
FXML 对您帮助不大...它只是一个带有另一个窗格的 BorderPane,但是没关系...
<center>
<VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<Pane maxHeight="1.7976931348623157E308" VBox.vgrow="ALWAYS" />
</children>
</VBox>
所以,我正在做的是获取 Canvas 元素,Processing 创建并将其添加到窗格中。