JavaFX Controls in py5

It would be neat if you could use py5 drawing commands to draw something that is inside a JavaFX window that also has those controls on it.

To the best of my knowledge it is not possible to have JavaFX controls in a window which supports standard py5 drawing commands. A py5 window with a FX2D renderer returns PSurfaceFX$ResizableCanvas@703580bf for ‘get_surface().get_native()’, not a JavaFX object. You can draw into the FX2D renderer window using standard Processing primitives (circle, rect, triangle, etc) in the draw() loop as we are accustomed to, but it will not accept JavaFX controls without using a stage, scene, and pane (which creates another window). If you used a P2D renderer you could mix cp5 controls with standard drawing but the graphics is not quite as sharp as those with a FX2D renderer. The only way to have higher resolution graphics with snazzier controls is to use FX2D with JavaFX all the way. JavaFX has its own set of graphic shapes, but of course it’s more code. Examples are shown below.

Py5 window with FX2D renderer:

# Uses Imported mode for py5

_wndW = 450
_wndH = 350

def setup():
  size(_wndW, _wndH, FX2D)
  window_title("Sketch Drawing")
  print(get_surface().get_native())

def draw():
  fill(0)
  text_size(32)
  text("FX2D Renderer Text",80,60)
  fill(255,0,0)
  circle(100,150,100)
  fill(0,0,255)
  triangle(220,100,170,200,270,200)
  fill(0,255,0)
  rect(300,100,100,100)

Output:

Py5 window with P2D renderer and cp5 controls:

# Uses Imported mode for py5
from controlP5 import *

_wndW = 450
_wndH = 350

def setup():
  global btn1
  
  size(_wndW, _wndH, P2D)
  window_title("Sketch Drawing")
  print(get_surface().get_native())
  cp5 = ControlP5(get_current_sketch())
  btn1 = Button(cp5,"Hello") 

def draw():
  fill(0)
  text_size(32)
  text("P2D Renderer Text",80,60)
  fill(255,0,0)
  circle(100,150,100)
  fill(0,0,255)
  triangle(220,100,170,200,270,200)
  fill(0,255,0)
  rect(300,100,100,100)
  
def mouse_pressed(e):
  if(btn1.isPressed()):
    print("btn1 was pressed.")

Output:

Py5 window with JavaFX and FX2D renderer:

# Uses Imported mode for py5
import javafx.stage.Stage;

_wndW = 600
_wndH = 300

def myBtnAction(event):
    print("btn hit.")
    
def settings():
  size(1,1,FX2D)
  print(get_surface())
  
def setup():
  stage = javafx.stage.Stage()
  print("stage =",stage)
  stage.setTitle("JavaFX Graphics Demo")
#   stage.setResizable:true  
  pane = javafx.scene.layout.Pane()

# Button
  btn = javafx.scene.control.Button("Button")
  btn.setLayoutX(90)
  btn.setLayoutY(10)
  btn.setOnAction(myBtnAction)
  pane.getChildren().add(btn)
  
# Text
  txt = javafx.scene.text.Text(130, 80, "Welcome to JavaFX!")
  txt.setFont(javafx.scene.text.Font(35))
  pane.getChildren().add(txt)
  
# Rectangle
  r = javafx.scene.shape.Rectangle(50, 130, 100, 50)
  r.setStroke(javafx.scene.paint.Color.color(0.0, 1.0, 0.0, 0.95))
  r.setStrokeWidth(5)
  r.setArcWidth(15)
  r.setArcHeight(15)
  pane.getChildren().add(r);
  
# Circle
  myCircle = javafx.scene.shape.Circle(250, 160, 40)
  myCircle.setFill(javafx.scene.paint.Color.DARKRED)
  myCircle.setStrokeWidth(6)
  myCircle.setStroke(javafx.scene.paint.Color.DARKSLATEBLUE)
  pane.getChildren().add(myCircle)

# Triangle
  polygon = javafx.scene.shape.Polygon();
  polygon.getPoints().addAll([ 380.0, 100.0, 330.0, 200.0, 430.0, 200.0 ])
  polygon.setFill(javafx.scene.paint.Color.GREEN)
  pane.getChildren().add(polygon)
  
# Line
  ln = javafx.scene.shape.Line(450,150,550,150)
  ln.setStroke(javafx.scene.paint.Color.color(1.0, 0.0, 0.0, 1.0))
  ln.setStrokeWidth(10)
  pane.getChildren().add(ln)
  
  scene = javafx.scene.Scene(pane, _wndW, _wndH, javafx.scene.paint.Color.ALICEBLUE)
  stage.setScene(scene)
  stage.show()

Output: