Processing and JavaFX

Hello,

There are many posts in the forum using JavaFX with Processing.

JavaFX code works with certain configurations but not with others.

These configurations may include:

  • Version of Processing
  • Operating system and version
  • Modification to the source code to add modules
  • Latest build of Processing has modifications (see above) added.
  • Adding JavaFX jar files to sketch
  • Adding import statements
  • JavaFX version if applicable
  • Anything I missed…

All the topics on this subject in this forum can complicate the journey for a new user and we need to stay on point and communicate clearly when discussing these.

I have managed to sort out all of these JavaFX issues for everything on Windows; not tested on a MacOS.
I rarely use JavaFX but have tried some of the examples and overcame any hurdles running these. I always enjoy a challenge!
A new user will certainly have challenges.

I suggest one clear concise communication on this in:

If I had the time I would contribute more but these days I simply do not.

A working configuration must include:

  • Operating system and version
  • Processing version
  • Additional requirements such as import statements, adding files
  • Any other relevant details

A working configuration for my setup:

  • Windows 10 Pro 22H2
  • Processing 4.3.2
  • Adding the 7x JavaFX jar files from the modules folder to the code folder in the sketch
  • Add this to sketch:
    import processing.javafx.*;

This GitHub issue discusses some recent changes to Processing for JavaFX:

Is it a problem or a question?

Please read:

:)

1 Like

Hello,

I posted this in the gallery recently but have moved it to here instead.

@svan The gallery post should include the working configuration to run the code.
Can you please add this?

It does not run on Windows 10 Pro 22H2 with Processing 4.3.2 as is:

It does run on Windows 10 Pro 22H2 with Processing 4.3.2 with some additions.

It works if I import JavaFX and drag and drop the 7 jar files (from the modules folder) onto the sketch window to add them (a folder called code is created and contains these):

I also added this to the sketch:
import processing.javafx.*;

Which will include this additional 1x required javafx.jar file:

You could include all the 8x jar files (from above) in the code folder as well and not need the import processing.javafx.*; statement.

Related:

It now works like it’s supposed to on Mac; so far I have only found one of my old files that won’t run, for reasons I don’t understand. This is without drag 'n dropping jar files. In the beginning I had to modify the editor source code to include all seven modules in order to use JavaFX which is why I made the pull request to add all of them. I also have Linux and Windows 11 systems that I rarely use and have discovered that I’m unable to run the JavaFX multiplication table demo after installing the most recent editor on each. In both cases the demo will run after drag 'n dropping the seven JavaFX module jar files onto the open sketch. That’s a problem that should be addressed. In my opinion we are missing out on using a good renderer and a nice set of JavaFX controls; graphics are a lot sharper with FX2D. For now a bug report seems to be indicated. I would like to see more input from other forum members on their experiences just to make sure they are seeing the same thing before filing the report.

The problem in Windows and Linux seems to be that the JavaFX module jars are in Processing/libraries/javafx/library/yourOS/modules where they are supposed to be, but the runtime does not know that they are there; apparently this issue has been around for some time and never been fixed. Until it gets resolved a work around could be to copy the .jar files from the library folder to a sketch folder named ‘code’ rather than hunting down the jar files and drag 'n dropping them onto an open sketch window. This code was found on StackOverflow and would need to be tested in the above environments to see if it really works. It successfully copied the .jar files on my Mac. You will need to substitute the name of your computer and your operating system. The JavaFX library would need to be installed in your editor to start with.

//https://stackoverflow.com/questions/71148185/how-to-copy-specific-files-from-one-directory-to-another-in-java
import java.util.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.File;
import java.nio.file.Files;
import java.util.stream.Collectors;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

try (Stream<Path> stream = Files.list(Paths.get("/Users/yourName/Documents/Processing/libraries/javafx/library/yourOS/modules"))) {
  List<Path> paths = stream.filter(path -> path.toString().endsWith(".jar")).collect(Collectors.toList());
  for (Path source : paths) {
    Path destination = Paths.get("/Users/yourName/Documents/Processing/yourSketch/code" + File.separator + source.getFileName());
    Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
  }
}
catch (IOException e) {
  println(e);
}

Hello folks!

I can run JavaFX code examples (links below) with:

  • Windows 10 Pro 22H2
  • Processing 4.3.2

Steps:

  • Install the JavaFX library.

  • Import the JavaFX library in your code:

  • Add the JavaFX modules (jar files in modules folder) to your sketch folder.
    I have some code below to assist with finding them.
    The simplest way to do this is to drag and drop them into your sketch window.
    This will create a folder called code that contains them.
    Or…
    You cold also manually create a folder called code and copy the JavaFX modules into this.

  • I suggest you create a code template that contains these JavaFX modules for future use!
    You then “Save as” a new sketch to use in future.

I am able to run these examples from the forum with steps provided in this post for W10 and Processing 4.3.2:

Code to assist with finding the JavaFX modules folder:

// Assist with finding JavaFX modules and open folder
// Opens the folder on a Windows 10 system
// Author: glv
// Date:   2025-02-04

// Insight gleaned from various sources; seek and you shall find.

// Note:
// It only works with a new sketch name starting with "sketch" !
// You MUST save the sketch folder first !
// If you don't save it it points the %temp% directory.

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;

void setup()
  {
  String sp = sketchPath("");  
  println(sp);
  
  String path = sp.substring(0, sp.indexOf("\\sketch"));
  println(path);
  path = path + "\\libraries\\javafx\\library\\windows-amd64\\modules";
  
  println();
  println("This is where the JavaFX modules are:");
  println(path);

  // Specify the path of the folder you want to open
  File folder = new File(path);

  if (folder.exists() && folder.isDirectory()) 
    {
    try 
      {
      Desktop.getDesktop().open(folder);
      } 
    catch (IOException e) 
      {
      e.printStackTrace();
      }
    } 
  else 
    {
    System.out.println("The specified folder does not exist or is not a directory.");
    }
  }

Output:

Opens this folder on my system:

I encourage you to explore the references below to help understand and navigate this.
There are some menu items in the Processing PDE to explore that are very useful!

Additional references:

And there is main Processing website!

Have fun!

:)

Hello again!

If anyone can contribute to this request below please respond to the GitHub topic or submit a report:

Add all seven of the javafx modules to JavaBuild.java by vsquared · Pull Request #856 · processing/processing4 · GitHub

And hello @stefterv I see you are here as well!

:)

2 Likes

Something else to try…

You could also copy the javafx.jar into the code folder in the sketch:

D:\Users\GLV\Documents\P4\libraries\javafx\library\javafx.jar 
// Location may be different on you PC

And after copying you may not require the JavaFX library import:

import processing.javafx.*;

I have not tested all the cases so do some testing.

These worked with W10 and Processing 4.4.1:

Contents of code folder in sketch:

:)

Hello folks!

The options below assume that this entire topic was read, there is an understanding of what needs to be done for JavaFX to run under Windows, you have a level of proficiency in creating the necessary file structures and using the command-line.

Both of these options appear to work for JavaFX demos in previous post.

Options for using JavaFX with Windows are to create reusable template sketches:

  • Create a sketch with all the required modules (JAR files) in the code folder.

  • Create a sketch with symbolic links to all the required modules (JAR files) in the library
    This is an advanced topic and will not be discussed in detail here.
    Do a bit of research before asking questions.

This is my code folder with symbolic links:

I may consider adding this in the future:
D:\Users\GLV\Documents\P4\libraries\javafx\library\javafx.jar

Advanced Users Only! < Click here to open!

My batch file for creating mlinks from the command prompt in the code folder:

mklink javafx.base.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.base.jar
mklink javafx.controls.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.controls.jar
mklink javafx.graphics.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.graphics.jar
mklink javafx.media.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.media.jar
mklink javafx.swing.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.swing.jar
mklink javafx.web.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.web.jar
mklink javafx.fxml.jar D:\Users\GLV\Documents\P4\libraries\javafx\library\windows-amd64\modules\javafx.fxml.jar

Directory listing:

You will have to make the necessary changes to reflect your file locations!

:)

This topic needs to be revisited, because there appears to be a simpler way. On my Windows 11 system I have been able to run a javafx demo without using a separate ‘code‘ folder. I did this by copy/pasting all the javafx.xxxx.jar files into the following address: C:\Users\s\Documents\Processing\libraries\javafx\library\javafx.xxxxx.jar. These jar files have been buried in a ‘modules‘ folder inside of the native operating system folder, but need to be at the location above to be picked up by the source code. I tested this technique on Linux and it also works there. You can test this by running the following demo:

import javafx.scene.canvas.Canvas;
import javafx.scene.layout.StackPane;

Canvas canvas;
StackPane root;

void setup(){
 size(350,200,FX2D);  
 surface.setTitle("JavaFX in Default Wnd");
 canvas = (Canvas)surface.getNative();
 root = (StackPane)canvas.getParent();
}

void draw(){
  fill(0,255,0);
  circle(width/2,100,100);
}

Output:

Hello @svan,

Please try it with this example on Windows 11 and share your results:
JavaFX Controls Preview

:)

You have to go through and find all those controls that don’t work and import them; this is an acid test, but you can get by without the code folder.

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.CheckBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.TreeView;
import javafx.scene.control.TreeItem;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ToolBar;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tooltip;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Dialog;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.Pane;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Circle;
import java.time.LocalDate;

It may be possible to use something like javafx.scene.control.*; but I’ve always just added what I needed.

Output:

This example did not work with Windows 10 and Processing 4.4.10 using your simple way:
JavaFX Controls Preview

And required these in the sketch folder:

Also required:

import processing.javafx.*; // Required

The other imports I can resolve as required.

Your simpler way works for the example you provided here:
Processing and JavaFX - #9 by svan

Does it work with all your other examples for Windows 11?

:)

Works just fine here with Windows 11; it’s the same demo that I previously posted and this one runs also. There is no ‘code‘ folder in the sketch folder. Full source code follows:


import javafx.scene.control.Button;
import javafx.scene.control.Tooltip;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.TextArea;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ToolBar;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Dialog;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.layout.Pane;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.StackPane;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Circle;
import javafx.scene.canvas.Canvas;
import java.time.LocalDate;

Pane pane;
StackPane root;
Canvas canvas;

Pane addControls() {
  // **** Pane **** //
  Pane pane = new Pane();

  // **** Label **** //
  Label label = new Label("Label");
  Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
  label.setFont(font);
  label.setTextFill(Color.RED);
  label.setTranslateX(70);
  label.setTranslateY(20);
  pane.getChildren().add(label);

  // **** Toggle Button **** //
  ToggleButton toggleBtn = new ToggleButton("Off");
  toggleBtn.setLayoutX(130);
  toggleBtn.setLayoutY(20);
  toggleBtn.selectedProperty().addListener(((observable, oldValue, newValue) -> {
    if (newValue == true) {
      toggleBtn.setText("On");
    } else {
      toggleBtn.setText("Off");
    }
  }
  ));
  pane.getChildren().add(toggleBtn);

  // **** Dialog with Button **** //
  Dialog<String> dialog = new Dialog<String>();
  dialog.setTitle("Error Dialog");
  ButtonType type = new ButtonType("Ok", ButtonData.OK_DONE);
  dialog.setContentText("Message goes here.");
  dialog.getDialogPane().getButtonTypes().add(type);
  Button btn = new Button("Push Me");
  btn.setLayoutX(350);
  btn.setLayoutY(140);
  btn.setOnAction(e -> {
    dialog.showAndWait();
  }
  );
  pane.getChildren().add(btn);

  // **** Circle **** //
  Circle circle = new Circle(400, 60, 20);
  circle.setFill(Color.GREEN);
  pane.getChildren().add(circle);

  // **** Slider **** //
  Slider slider = new Slider(1, 50, 10);
  slider.setLayoutX(190);
  slider.setLayoutY(20);
  Tooltip tooltip1 = new Tooltip("Sets radius");
  slider.setTooltip(tooltip1);
  slider.valueProperty().addListener((ov, oldValue, newValue) -> {
    circle.setRadius((double)newValue);
  }
  );
  pane.getChildren().add(slider);

  // **** Spinner **** //
  Spinner spinner = new Spinner(0, 255, 0);
  spinner.setPrefSize(70, 25);
  spinner.setLayoutX(480);
  spinner.setLayoutY(60);
  pane.getChildren().add(spinner);

  // **** ComboBox **** //
  ComboBox<String> combo = new ComboBox<String>();
  combo.setPromptText("Select One");
  ObservableList<String> list = combo.getItems();
  list.add("Java");
  list.add("C++");
  list.add("Python");
  list.add("Processing");
  combo.setLayoutX(560);
  combo.setLayoutY(60);
  pane.getChildren().add(combo);

  // **** ListView **** //
  ObservableList<String> items = FXCollections.observableArrayList("Item 1", "Item 2", "Item 3");
  ListView<String> listView = new ListView<String>(items);
  listView.setMaxSize(100, 80);
  listView.setLayoutX(30);
  listView.setLayoutY(80);
  pane.getChildren().add(listView);

  // **** TabPane **** //
  TabPane tabPane = new TabPane();
  tabPane.setLayoutX(30);
  tabPane.setLayoutY(190);
  Tab tab1 = new Tab("Tab 1");
  Tab tab2 = new Tab("Tab 2");
  Tab tab3 = new Tab("Tab 3");
  tab1.setContent(new Rectangle(200, 100, Color.BLUE));
  tab2.setContent(new Rectangle(200, 100, Color.RED));
  tab3.setContent(new Rectangle(200, 100, Color.GREEN));
  tabPane.getTabs().addAll(tab1, tab2, tab3);
  pane.getChildren().add(tabPane);

  // **** TextArea **** //
  TextArea txtArea = new TextArea();
  txtArea.setLayoutX(250);
  txtArea.setLayoutY(190);
  txtArea.setMaxSize(180, 130);
  pane.getChildren().add(txtArea);

  // **** RadioButton Group **** //
  TilePane radioPane = new TilePane(15, 15);
  ToggleGroup group = new ToggleGroup();
  RadioButton radio1 = new RadioButton("A");
  radio1.setToggleGroup(group);
  radio1.setSelected(true);
  RadioButton radio2 = new RadioButton("B");
  radio2.setToggleGroup(group);
  RadioButton radio3 = new RadioButton("C");
  radio3.setToggleGroup(group);
  radioPane.getChildren().addAll(radio1, radio2, radio3);
  radioPane.setLayoutX(150);
  radioPane.setLayoutY(100);
  pane.getChildren().add(radioPane);

  // **** CheckBox **** //
  CheckBox cBox = new CheckBox("Checkbox");
  cBox.setIndeterminate(false);
  cBox.setLayoutX(60);
  cBox.setLayoutY(50);
  pane.getChildren().add(cBox);

  // **** TextField **** //
  TextField txtFld = new TextField();
  txtFld.setLayoutX(150);
  txtFld.setLayoutY(140);
  pane.getChildren().add(txtFld);

  // **** MenuBar **** //
  Menu menu = new Menu("File");
  MenuItem menuItem1 = new MenuItem("Open");
  MenuItem menuItem2 = new MenuItem("Save");
  menu.getItems().addAll(menuItem1, menuItem2);
  MenuBar menuBar = new MenuBar();
  menuBar.getMenus().add(menu);
  pane.getChildren().add(menuBar);

  // **** ToolBar **** //
  ToolBar toolBar = new ToolBar();
  Button openBtn = new Button("Build");
  Button saveBtn = new Button("Run");
  toolBar.getItems().addAll(openBtn, saveBtn);
  toolBar.setLayoutX(450);
  Tooltip tbarTip = new Tooltip("ToolBar");
  toolBar.setTooltip(tbarTip);
  pane.getChildren().add(toolBar);

  // **** ProgressBar **** //
  ProgressBar pBar = new ProgressBar();
  pBar.setLayoutX(560);
  pBar.setLayoutY(20);
  pane.getChildren().add(pBar);

  // **** TreeView **** //
  TreeItem rootItem = new TreeItem("Pets");
  TreeItem dogItem = new TreeItem("Dogs");
  dogItem.getChildren().add(new TreeItem("Poodle"));
  dogItem.getChildren().add(new TreeItem("Collie"));
  dogItem.getChildren().add(new TreeItem("Bulldog"));
  rootItem.getChildren().add(dogItem);

  TreeItem catItem = new TreeItem("Cats");
  catItem.getChildren().add(new TreeItem("Siamese"));
  catItem.getChildren().add(new TreeItem("Ragdoll"));
  catItem.getChildren().add(new TreeItem("Persian"));
  rootItem.getChildren().add(catItem);

  TreeView treeView = new TreeView();
  treeView.setRoot(rootItem);
  treeView.setLayoutX(460);
  treeView.setLayoutY(110);
  treeView.setMaxSize(140, 230);
  //treeView.setShowRoot(false);
  treeView.setShowRoot(true);
  pane.getChildren().add(treeView);

  // **** ColorPicker **** //
  ColorPicker colorPicker = new ColorPicker();
  colorPicker.setLayoutX(200);
  colorPicker.setLayoutY(50);
  colorPicker.setValue(Color.GREEN);
  colorPicker.setOnAction(action -> {
    label.setTextFill(colorPicker.getValue());
    circle.setFill(colorPicker.getValue());
  }
  );
  pane.getChildren().add(colorPicker);

  // **** DatePicker **** //
  DatePicker datePicker = new DatePicker();
  datePicker.setLayoutX(30);
  datePicker.setLayoutY(350);
  datePicker.setOnAction(action -> {
    LocalDate value = datePicker.getValue();
  }
  );
  pane.getChildren().add(datePicker);

  // **** Line Chart **** //
  NumberAxis xAxis = new NumberAxis();
  xAxis.setLabel("No. of months");
  NumberAxis yAxis = new NumberAxis();
  yAxis.setLabel("Dollars Per Dozen");
  LineChart lineChart = new LineChart(xAxis, yAxis);
  XYChart.Series dataSeries1 = new XYChart.Series();
  dataSeries1.setName("2023");
  dataSeries1.getData().add(new XYChart.Data( 1, 2));
  dataSeries1.getData().add(new XYChart.Data( 2, 3));
  dataSeries1.getData().add(new XYChart.Data(3, 4));
  dataSeries1.getData().add(new XYChart.Data(4, 5));
  dataSeries1.getData().add(new XYChart.Data(5, 6));
  dataSeries1.getData().add(new XYChart.Data(6, 6));
  lineChart.getData().add(dataSeries1);
  lineChart.setTitle("Price of Eggs");
  lineChart.setLayoutX(280);
  lineChart.setLayoutY(350);
  lineChart.setMaxSize(320, 240);
  pane.getChildren().add(lineChart);

  return pane;
}

void setup() {
  size(700, 600, FX2D);
  surface.setTitle("JavaFX Controls");
  canvas = (Canvas)surface.getNative();
  root = (StackPane)canvas.getParent();
  pane = addControls();
  root.getChildren().add(pane);
}

void draw() {
}

Output:

Works on Linux too:

Your Processing\libraries\javafx\library folder should look like this:

Environment:

The example I quoted did not work as is.

I had to make this change:

  //slider.valueProperty().addListener(new ChangeListener<Number>() 
  //  {
  //  public void changed(ObservableValue <?extends Number>observable, Number oldValue, Number newValue) 
  //    {
  //    circle.setRadius((double)newValue);
  //    }
  //  }
  //);
  
  slider.valueProperty().addListener((ov, oldValue, newValue) -> {
    circle.setRadius(newValue.doubleValue());
  }
  );

To correct this error to work with your “simple way”:

The method addListener(ChangeListener<? super Number>) in the type ObservableValue<Number> is not applicable for the arguments (new ChangeListener<Number>(){})

I also added all the additional imports.

Google Gemini explanation:

Summary

So… why did the original wrong code work when I simply added the javafx.base.jar to the sketch directly?

The original wrong code produced the ChangeListener error because of an issue with the compiler’s classpath during the Processing setup, not an issue with the logic itself. Adding the javafx-base.jar fixed this by resolving the dependency in a brute-force way.

:brick: The Compiler Path Issue

  1. Missing JavaFX Definition: When you run a Processing sketch using the FX2D renderer, Processing should automatically configure its internal compiler to include all the necessary JavaFX modules. When this automatic configuration fails (which can happen in certain IDEs or environments), the compiler cannot properly find the full definition for certain JavaFX types, like ChangeListener and ObservableValue.
  2. Generic Type Ambiguity: The original error, addListener(ChangeListener<? super Number>) is not applicable for the arguments (new ChangeListener<Number>(){}), is often triggered when the compiler sees an incomplete or ambiguous definition of the target method. By manually launching a new Stage in your setup(), you were relying on the system’s runtime environment, but the compile-time check failed because it couldn’t fully reconcile the generic type bounds (<? super Number>) required by the method signature with the type you provided (<Number>).
  3. Manual Resolution: By directly copying javafx-base.jar into the sketch folder, you provided the compiler with a local, explicit copy of the JavaFX definitions. This allowed the compiler to fully resolve the signatures of all the core JavaFX methods, including addListener, making the specific generic type error disappear.

:warning: Why It’s Still “Wrong”

While adding the JAR resolved the compilation error, it didn’t fix the application architecture error:

  • The code still creates a separate, detached JavaFX window via new Stage().
  • It doesn’t integrate the UI controls into the main Processing canvas, which is the purpose of using size(..., FX2D) in the first place.

In short, the JAR fixed the compiler’s vision problem, allowing it to compile the code, but it didn’t fix the code’s flawed runtime execution strategy.

This simple replacement worked in original code: //slider.valueProperty().addListener(new ChangeListener()
// {
// public void changed(ObservableValue <?extends Number>observable, Number oldValue, Number newValue)
// {
// circle.setRadius((double)newValue);
// }
// }
//);

slider.valueProperty().addListener((ov, oldValue, newValue) → {
circle.setRadius(newValue.doubleValue());
}
);

That’s right, replacing the verbose Anonymous Inner Class with the Lambda Expression works because the lambda simplifies the code and allows the compiler to better infer the required generic type for the ChangeListener.

The original error was: addListener(ChangeListener<? super Number>) is not applicable for the arguments (new ChangeListener<Number>(){})


:light_bulb: Why the Lambda Fixes the Error

The fix demonstrates a subtle point about how Java generics and the compiler interact:

  • Original Code (Anonymous Class): You explicitly declared new ChangeListener<Number>(){...}. Even though Number is a supertype of itself, the compiler in the context of your original setup (which was already shaky) struggled to reconcile your explicit declaration of ChangeListener<Number> with the required contravariant type ChangeListener<? super Number>.
  • Lambda Code (Simplified): When you use the lambda expression, (ov, oldValue, newValue) -> {...}, you are relying on the compiler to infer the functional interface (ChangeListener) and its generic types. Because the method signature requires ChangeListener<? super Number>, the compiler correctly and seamlessly infers the lambda to conform to this specific requirement.

The lambda expression is generally the cleanest way to handle simple functional interfaces in Java 8 and later, and in this case, it side-stepped the compilation ambiguity caused by the anonymous inner class syntax in your specific Processing environment.

You may be on to something with your “simple way”.

Does it work with all your other examples?

I included some references for visitors to the topic.
Adding the JAR files is an alternative and can be useful when needed.

References:

:)

Yes, and across all three platforms; it’s a keeper. I have requested that the distribution be changed to install the javafx.xxxx.jar files in the Processing/libraries/javafx/library folder initially so that users won’t have to do it manually.

I never had a problem with this issue on MacOS because I had unwittingly installed all the javafx.xxxx.jar files in the libraries/javafx/library folder to start with. The clincher occurred when I removed all of them from this folder and broke my own system so that it would no longer run JavaFX demos. I then re-installed the javafx.xxxx.jar files in the libraries/javafx/library folder and ‘fixed‘ it.

Hello @svan,

I was able to make modifications to the JavaBuild.java and run the JavaFX sketches in this topic with the JavaFX library as is out of the box (no copying or adding jars):

The modified Processing PDE ran the code successfully but the editor gave false flags about the classes not existing.

I also inserted print statements in that file to provide debug information:

Debug output of modified Processing
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.base.jar
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.controls.jar
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.fxml.jar
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.graphics.jar
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.media.jar
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.swing.jar
classpath_0: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.web.jar
DEBUG: Successfully added 7 JavaFX module paths.
classpath_1: ;D:\Users\GLV\Documents\Processing\libraries\javafx\library\javafx.jar
javaLibraryPath: ;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64

classPath: D:\temp\processing\JavaFX_Controls_Preview_New7169551845653962132temp;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\annotations-13.0.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\antlr-2.7.7.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\core-sources.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\core.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0-natives-android-aarch64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0-natives-linux-aarch64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0-natives-linux-amd64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0-natives-linux-armv6hf.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0-natives-macosx-universal.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0-natives-windows-amd64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-2.5.0.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\gluegen-rt-main-2.5.0.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0-natives-android-aarch64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0-natives-linux-aarch64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0-natives-linux-amd64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0-natives-linux-armv6hf.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0-natives-macosx-universal.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0-natives-windows-amd64.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-2.5.0.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\jogl-all-main-2.5.0.jar;F:\test2\processing4\app\build\compose\binaries\main\app\Processing\app\resources\core\library\kotlin-stdlib-2.0.20.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.base.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.controls.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.fxml.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.graphics.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.media.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.swing.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\windows-amd64\modules\javafx.web.jar;D:\Users\GLV\Documents\Processing\libraries\javafx\library\javafx.jar
WARNING: Custom port 0 is invalid. Falling back to random selection.
Debug Port Selected: 8257

Google Gemini was my assistant in the process.
I did all the work it seems!

Conclusions (Goggle Gemini assisted) from examining the Class Paths from original and modified Processing:

I would focus on the root cause of this and address that.
This “simple way” is a workaround to add to the list of others presented.

I had fun exploring this and encourage others to do the same!

Code shared on a related GitHub issue.

Have fun!

:)