如何在javafx中嵌入PApplet?

时间:2015-02-01 18:34:09

标签: java user-interface netbeans javafx processing

所以我的处理代码在java中运行。但现在我想将它嵌入JavaFX中以用于我的GUI。我怎么能这样做? 我尝试使用以下代码,但它似乎不起作用。

 package testprocessing;
import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import java.awt.Dimension;

import java.util.concurrent.*;
import processing.core.*;

public class JavaFxApplet extends Application {
    private PApplet applet = new MyProcessingSketch();
    private Dimension appletSize;

    @Override public void init() throws ExecutionException, InterruptedException {
        applet.init();

        FutureTask<Dimension> sizingTask = new FutureTask<>(() ->
            applet.getRootPane().getPreferredSize()
        );
        SwingUtilities.invokeLater(sizingTask);
        appletSize = sizingTask.get();
    }

    @Override public void start(Stage stage) {
        final SwingNode swingNode = new SwingNode();
        SwingUtilities.invokeLater(() ->
            swingNode.setContent(applet.getRootPane())
        );

        stage.setScene(
            new Scene(
                new Group(swingNode),
                appletSize.getWidth(), appletSize.getHeight(),
                Color.BLACK
            )
        );
        stage.show();
    }

    @Override public void stop() {
        applet.stop();
        applet.destroy();
    }

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

我在getRootPane()时遇到错误。你能为它建议一个替代方案吗?

2 个答案:

答案 0 :(得分:1)

背景

处理3 中引入的是JavaFX渲染模式,该模式可以将JavaFX包括在草图中。不必从头创建我们自己的JavaFX窗口,然后将其嵌入其中,我们可以在JavaFX模式下初始化时,修改由PApplet类构造的窗口,在其中添加新的JavaFX元素。

在JavaFX模式下初始化期间,PApplet类创建一个javafx.scene.canvas.Canvas对象,并将其作为子对象添加到javafx.scene.layout.StackPane对象中。然后,使用 stackPane 对象作为参数构造一个javafx.scene.Scene对象。最后,PApplet类创建一个javafx.stage.Stage对象,并将其场景设置为 scene 对象,以提供我们的PApplet实例-草图。

因此,就JavaFX元素而言,PApplet窗口由以下层次结构中的四个元素初始化:Stage > Scene > StackPane > Canvas,其中 canvas 是草图的图形画布(即处理绘制到的对象)。

要创建自己的GUI ,我们可以将任何javafx.scene.Node对象(这是JavaFX图形元素的超类)添加到 stackPane 对象。或者,您可以构造一个新的 Scene ,向其中添加Processing的画布,并替换 Stage 中现有的 Scene


什么似乎不起作用

在未指定渲染模式的情况下,“处理”默认为JAVA2D模式。在这种模式下,PApplet类使用画布和窗口的java.awt版本(分别为java.awt.Canvasjava.awt.Frame)创建一个PApplet实例。从理论上讲,可以将java.awt.Frame转换为javax.swing.JFrame,将其嵌入到javafx.embed.swing.SwingNode对象中,最后将其添加到JavaFX阶段。但是,我无法使它正常工作。

还有P2DP3D模式。在这些模式下,画布是com.jogamp.newt.opengl.GLWindow对象。再次,我尝试在com.jogamp.opengl.awt.GLJPanel的帮助下将其嵌入到Swing节点中,但是事实证明它没有成功。


实施

在对FX2D的调用中,以Processing的size()渲染模式初始化草图:

size([width], [height], FX2D);

然后我们可以通过重复转换来揭示在初始化期间创建的四个JavaFX元素:

final PSurfaceFX FXSurface = (PSurfaceFX) surface;

final Canvas canvas = (Canvas) FXSurface.getNative();
final StackPane stackPane = (StackPane) canvas.getParent();
final Scene scene = canvas.getScene();
final Stage stage = (Stage) canvas.getScene().getWindow();

我们现在可以选择如何添加JavaFX元素:

1)添加到现有的stackPane

我们可以使用以下方法将JavaFX元素(javafx.scene.Node对象)添加到在初始化期间创建的 stackPane 中:

stackPane.getChildren().add(Node node);

2)创建一个新场景(推荐)

(推荐,除非您希望将stackPane用作顶层对齐器),我们可以创建一个新的场景对象(而不是使用 scene stackPane 对象)在初始化过程中创建的),并向其中添加JavaFX元素。

Scene newscene = new Scene(new Group(canvas)); // simple group containing only the Processing canvas
stage.setScene(Scene scene);

在初始化期间, canvas 的尺寸绑定到 stackPane 的尺寸。如果我们希望在运行时更改窗口内“处理”画布的大小,则必须包括以下内容:

canvas.widthProperty().unbind();
canvas.heightProperty().unbind();

现在,我们可以在JavaFX窗口(阶段)中自由调用canvas.setHeight()canvas.setWidth()来调整“正在处理”画布的大小。

示例

让我们在窗口中添加一个javafx.scene.control.MenuBar。请注意,我这样做是在initSurface()方法中初始化JavaFX元素,而不是在setup()方法中初始化JavaFX元素,因为这样做更安全。

在此示例中,首先用javafx.scene.layout.VBox替换 stackPane ,使菜单栏位于 canvas 的顶部,其次是确保菜单栏位于 stage 是启动时的正确高度(menuBar高度和画布高度的总和)。

@Override
public void settings() {
    size(500, 500, FX2D);
}

@Override
protected PSurface initSurface() {

    PSurface surface = super.initSurface();

    final PSurfaceFX FXSurface = (PSurfaceFX) surface;
    final Canvas canvas = (Canvas) FXSurface.getNative(); // canvas is the processing drawing
    final Stage stage = (Stage) canvas.getScene().getWindow(); // stage is the window

    stage.setTitle("Processing/JavaFX Example");
    canvas.widthProperty().unbind();
    canvas.heightProperty().unbind();

    final MenuItem menuItem1 = new MenuItem("Fill green");
    menuItem1.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            noLoop();
            background(0, 255, 0); // Fills the canvas green on click
        }
    });

    final MenuItem menuItem2 = new MenuItem("Exit");
    menuItem2.setOnAction(actionEvent -> exit()); // Exit PApplet on click

    final Menu menu = new Menu("Menu");
    menu.getItems().add(menuItem1);
    menu.getItems().add(menuItem2);

    final MenuBar menuBar = new MenuBar();
    menuBar.getMenus().add(menu);

    final VBox vBox = new VBox(menuBar, canvas); // Menubar will sit on top of canvas
    final Scene newscene = new Scene(vBox); // Create a scene from the elements
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            stage.setScene(newscene); // Replace the stage's scene with our new one.
        }
    });
    return surface;
}

@Override
public void draw() {
    background(50);
    fill(0, 255, 0);
    strokeWeight(5);
    stroke(255, 5, 5);
    line(0, 0, width, 0); // shows us that window is the correct dimensions
    line(0, height, width, height); // shows us that window is the correct dimensions
    noStroke();
    ellipse(100, 100, 200, 200);
    fill(255, 0, 0);
    ellipse(100, 200, 200, 200);
    fill(0, 0, 255);
    ellipse(100, 300, 200, 200);
}

结果

Result

答案 1 :(得分:0)

为什么要尝试获取applet的根窗格?只需将applet添加到JPanel,然后将JPanel添加到SwingNode:

JPanel panel = new JPanel();
panel.add(applet);
swingNode.setContent(panel)