JavaFX场景中的Java处理3 PAplet作为FXNode

Posted

技术标签:

【中文标题】JavaFX场景中的Java处理3 PAplet作为FXNode【英文标题】:Java Processing 3 PAplet in JavaFX scene as FXNode 【发布时间】:2019-03-13 13:38:01 【问题描述】:

我正在尝试制作一个用于可视化分析分形集的程序。我选择 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 或类似的东西?

我希望它可以这样工作,但是这段代码无效:

【问题讨论】:

【参考方案1】:

我设计了两种方法:第一种,我们绕过 Processing 的 JavaFX 阶段创建并指向 Processing 以绘制到从 FXML 文件加载的 JavaFX 阶段;第二,我们将 Processing 的默认 JavaFX 场景替换为 在运行时从 FXML 文件加载的场景。

1。从 FXML 启动

使用第一种方法,我们像启动 JavaFX 应用程序一样启动应用程序(使用 Application.launch(Launcher.class);),完全绕过 Processing 的 JavaFX 阶段创建代码。

您必须下载稍作修改的 core.jar 才能使这种方法工作,我已经更改了 PSurfaceFXPGraphicsFX2D 类的一些成员的可见性ProtectedPublic。这些更改允许我们从我们自己的 ... extends Application 类启动 JavaFX,同时保持对 Processing 在启动期间需要设置的成员的访问权限。

当使用的 JDK 高于 Java 8 时,处理 3 在 FX2D 模式下崩溃,因此我还为 8+ 制作了一个工作版本,因为 FXML 文件通常至少需要 Java 9 才能工作。

Download core.jar (Java 8 & below) Download core.jar (Above Java 8)

这是我在此示例中使用的 FXML 文件:

将修改后的 core.jar 添加到项目的类路径中,使用以下 sn-p 覆盖 PApplet 类中的 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);

将以下内容或类似内容放入您的 Launcher 类中。在此示例中,我手动找到了要添加画布对象的节点。有更好、更程序化的方法(例如 .lookup() 使用所需节点的 fx:id -- 这可以在 FXML 文件中定义)。我还将画布的尺寸绑定到其父级的尺寸,因此当拖动分隔 MasterView 窗格的除数时,处理画布会相应地调整大小。

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 以填充 @FXMLannotated 字段(提供首先获取,然后在代码中引用 JavaFX 对象的简单方法。


2。启动,然后加载 FXML

这种方法适用于普通处理。在这里,我们像往常一样启动处理,然后在运行时用从 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 文件:

【讨论】:

拜托,你能添加你的 FXML 文件吗? 您的代码在这一行中断 ` Canvas canvas = (Canvas) surface.getNative(); // 样板 GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); // 样板surface.fx.context = graphicsContext; // 样板文件 ` @JanČerný FXML file。代码如何破解? Launcher 类的表面对象在 initsurface() 覆盖方法 PApplet 中设置,如果它为 null。 三角形未显示并且我收到此警告:使用版本 9.0.1 的 JavaFX API 加载 FXML 文档,版本为 8.0.211 的 JavaFX 运行时 java.lang.IllegalStateException:允许在事件上执行此操作仅线程; currentThread = main at com.sun.glass.ui.Application.chat processing.core.PApplet.main(PApplet.java:10544) at processingfxnew.SirpenskiTriangle.main(SirpenskiTriangle.java:13) 但我使用 java 8 @gtx 使用 Java 9 或更高版本。【参考方案2】:

要使其正常工作,您必须启动 Processing 草图,而不是 JavaFX 应用程序。

简单的做

PApplet.main(Launcher.class.getName());

也非常感谢您的帮助!我不知道应该如何使用 Processing 附带的 JavaFX 东西!

【讨论】:

请详细描述您的解决方案,因为我不知道您的意思。 你还没有解决这个问题吗?今天晚些时候我可以给你发送示例代码 那就太好了 :) 我更喜欢数学而不是实际编程,所以像图书馆这样的问题对我来说是致命的。【参考方案3】:

好的,这是我的代码,有效!我复制了所有内容并更改了名称。 !!!我没有测试过这个修改过的代码,所以不要复制粘贴所有内容!!! 不过,原始原则应该绝对有效。

如果您仍有问题或疑问,请发表评论。

主要

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;
    

示例应用

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();
    


【讨论】:

【参考方案4】:

好的,自从上次以来,我改变了一些元素。 画布的父级现在只是一个窗格而不是 AnchorPane。

FXML 对你帮助不大……它只是一个 BorderPane,里面有另一个 Pane,但是没关系……

 <center>
  <VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
     <children>

        <Pane maxHeight="1.7976931348623157E308" VBox.vgrow="ALWAYS" />

     </children>
  </VBox>

所以,我正在做的是获取 Canvas 元素,Processing 创建并将其添加到窗格中。

【讨论】:

请不要对同一个问题发布多个答案 - 而是编辑和完善能够完全解决问题的答案(或尽可能接近 :) 我是 *** 的新手,对此我深表歉意。会记住这一点!

以上是关于JavaFX场景中的Java处理3 PAplet作为FXNode的主要内容,如果未能解决你的问题,请参考以下文章

尝试替换场景内容时 JavaFx 中的指针异常

如何强制 Java FX 场景刷新?

属性选择器如何在JavaFX中工作?

JAVA也可以用于图像的设计吗?

Ubuntu 中的 JavaFX 场景生成器路径

javafx 场景生成器 2.0 中的文件选择器在哪里?我找不到它