Java Processing 3 PAplet в сцене JavaFX как FXNode

Пытаюсь сделать программу для визуального анализа фрактальных множеств. Я выбираю Processing 3 в качестве библиотеки для рисования и JavaFX для пользовательского интерфейса. Вот несколько скриншотов текущего состояния:

Мой графический интерфейс:

Java Processing 3 PAplet в сцене JavaFX как FXNode

есть код лаунчера:

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:

Java Processing 3 PAplet в сцене JavaFX как FXNode

Вот код этого 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, такую ​​как холст или что-то подобное?

Я надеюсь, что это может работать так, но этот код недействителен: Java Processing 3 PAplet в сцене JavaFX как FXNode

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
0
1 886
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Я разработал два подхода: в первом мы обходим создание этапа JavaFX в Processing и точечную Processing для втягивания в этап JavaFX, загруженный из файла FXML; во втором мы заменяем сцену JavaFX по умолчанию для Processing на сцену, загруженную из файла FXML во время выполнения.

1. Запуск из FXML

При первом подходе мы запускаем приложение, как приложение JavaFX (с использованием Application.launch(Launcher.class);), полностью обходя код создания этапа JavaFX в Processing.

Вам нужно будет загрузить слегка измененный core.jar, чтобы этот подход работал, где я изменил видимость некоторых членов классов PSurfaceFX и PGraphicsFX2D с Protected на Public. Изменения позволяют нам запускать JavaFX из нашего собственного класса ... extends Application, сохраняя при этом доступ к членам, которые Processing должен установить во время запуска для работы.

Обработка 3 вылетает в режиме FX2D, когда используемый JDK выше Java 8, поэтому я также сделал рабочую версию для 8+, поскольку для работы файлов FXML обычно требуется как минимум Java 9.

Это файл FXML, с которым я работаю в этом примере:

С измененным core.jar, добавленным в путь к классам вашего проекта, переопределите initSurface() в вашем классе PApplet с помощью следующего фрагмента. С помощью этого кода мы обходим вызов 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. В этом примере я вручную нашел узел, в который хочу добавить объект холста. Есть лучшие, более программные способы сделать это (например, .Погляди() с использованием fx: id желаемого узла - это можно определить в файле FXML). Я также привязал размеры холста к размерам его родительского элемента, поэтому при перетаскивании делителя, разделяющего панели Мастер и Вид, размер холста обработки изменяется соответствующим образом.

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
    }
}

Вот результат:

Также см. Проект это Github - базовый проект, показывающий, как скетч обработки и этап FXML JavaFX могут быть интегрированы с использованием этого первого подхода, но включает JavaFX Controller для заполнения аннотированных полей @FXML (обеспечивая простой способ сначала получить, а затем ссылаться, Объекты JavaFX в коде).


2. Запуск и загрузка FXML.

Этот подход работает с ванильной обработкой. Здесь мы запускаем Обработку как обычно, а затем заменяем сцену по умолчанию новой сценой, загруженной из файла FXML во время выполнения. Это более простой подход (и не требует использования модифицированного .банка!), Но затруднит взаимодействие 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?

Jan Černý 15.10.2018 12:33

Ваш код прерывается в этой строке `Canvas canvas = (Canvas) surface.getNative (); // шаблонный GraphicsContext graphicsContext = canvas.getGraphicsContext2D (); // шаблон поверхности. fx.context = graphicsContext; // шаблон `

Jan Černý 15.10.2018 12:46

@ JanČerný FXML файл. Как код ломается? Объект поверхности класса Launcher устанавливается в методе переопределения initsurface () PApplet, если он равен нулю.

micycle 15.10.2018 13:23

треугольник не отображается, и я получаю это ПРЕДУПРЕЖДЕНИЕ: загрузка документа FXML с API JavaFX версии 9.0.1 с помощью среды выполнения JavaFX версии 8.0.211 java.lang.IllegalStateException: Эта операция разрешена только в потоке событий; currentThread = main в com.sun.glass.ui.Application.chat processing.core.PApplet.main (PApplet.java:10544) в processingfxnew.SirpenskiTriangle.main (SirpenskiTriangle.jav‌ a: 13), но я использую java 8

gtx 16.08.2019 15:23

@gtx Используйте Java 9 или выше.

micycle 16.08.2019 16:06

Оказывается, это было просто предупреждение, причиной ошибки является java.lang.IllegalStateException: эта операция разрешена только в потоке событий; currentThread = main в com.sun.glass.ui.Application.checkEventThread (Application.ja‌ va: 443) .... в processing.core.PApplet.main (PApplet.java:10544) в processingfxnew.SirpenskiTriangle.main ( SirpenskiTriangle.jav‌ a: 13) эта строка является PApplet.main ("processingfxnew.SirpenskiTriangle"); не показывает причину этого, я не уверен, как это исправить

gtx 16.08.2019 16:31

@gtx Я не могу сказать, почему отсюда ... У меня есть пример на моем Github загрузки этапа JavaFX из файла FXML в обработке. Вы можете использовать этот проект в качестве шаблона.

micycle 17.08.2019 02:38

хорошо, спасибо, я не могу найти github с вашим именем, вы можете дать ссылку?

gtx 17.08.2019 22:41

Я загрузил файлы вашего проекта, и здесь тоже появляется окно, но часть обработки все еще не отображается, и я все еще получаю ту же ошибку java.lang.IllegalStateException: эта операция разрешена только в потоке событий; currentThread = main в com.sun.glass.ui.Application.checkEventThread (Application.ja‌ va: 443) А также ..FXMLLoader..56 mo Вызвано: java.lang.NullPointerException в javafx.Controller.greenPen (Controller. java: 60) ... еще 66

gtx 18.08.2019 20:30

Используя java8 и ваше измененное ядро ​​1.8.jar и изменив ваш fxml на javafx 8 и обработав другие jar-файлы, такие как gluegen-rt

gtx 18.08.2019 20:30

@gtx Я не уверен, что он будет работать с 1.8 - просто попробуйте Java 10!

micycle 18.08.2019 20:45

Чтобы он заработал, вам нужно запустить скетч Processing, а не приложение JavaFX.

Просто делай

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

Также большое спасибо за вашу помощь! Я не имел ни малейшего представления о том, как использовать JavaFX, поставляемый с обработкой!

Пожалуйста, опишите подробнее свое решение, потому что я не знаю, что вы имеете в виду.

Jan Černý 13.11.2018 10:34

Вы все еще не решили это? Я могу отправить вам пример кода сегодня же

StefanH 14.11.2018 13:28

Это было бы очень хорошо :) Я больше занимаюсь математикой, чем практическим программированием, поэтому что-то вроде проблем с библиотекой для меня фатально.

Jan Černý 19.11.2018 12:11

Хорошо, это мой код, он работает! Я все скопировал и поменял названия. !!! Я не тестировал этот модифицированный код, поэтому не копируйте и вставляйте все !!! Однако необработанный принцип определенно должен работать.

Если у вас все еще есть проблемы или вопросы, просто прокомментируйте.

Главный

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

}

Хорошо, с прошлого раза я изменил некоторые элементы. Родителем холста теперь является просто панель, а не 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, и просто добавляю его в Pane.

пожалуйста, не публикуйте несколько ответов на один и тот же вопрос - вместо этого отредактируйте и уточните тот, который решает проблему полностью (или как можно ближе к вам :)

kleopatra 17.12.2018 16:57

Я новичок в Stackoverflow, извините за это. Имейте это в виду!

StefanH 18.12.2018 23:32

Другие вопросы по теме