JavaFX Scene Embedded in a Swing Window

Posted: under JavaFX Technology.
Tags: , , ,

In Stephen Chin’s open source project JFXtras, a JavaFX wrapper for Swing is included in the current release(V0.5). This wrapper allows us to put JavaFX GUI components into a SWING application. It is very useful for developers to integrate JavaFX GUI features with their Java Swing applications.


The JFXtras project comes with a sample test program( refer to SceneToJComponentScene.fx and SceneToJComponentTest.java). I suppose that Swing developers are probably interested in this topic, so I modified the code a bit and created the below example for better illustration purposes.


First we create a subclass of the Scene. Since the Scene class is the top level container in JavaFX , we can virtually place any GUI components in it and then make a reference to it from the java side. Let’s look at the code:

/*
 * MyScene.fx     http://www.javafxgame.com
 * @author Henry Zhang
 */

package swingtest;

import javafx.scene.Scene;
import javafx.scene.text.*;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;

def w = 500;
def h = 400;

public class MyScene extends Scene {
    var xx = w / 3;
    var yy = h / 2;
    var rotate = 0;
    var text = "";

    var tl = Timeline {

      repeatCount: Timeline.INDEFINITE
      keyFrames : [
        KeyFrame {
          time: 70ms
          action: function() {
             text = JavaFXToSwingTest.tf.getText();
             rotate = (rotate+5) mod 360;
          }
        }
      ]
     }

     override var content= [
        Rectangle {
            width: w, height: h
            fill: Color.BLUE
        },
        Text {
            font : Font {
                    size: 24
                   }
            layoutX: bind  xx
            layoutY: bind yy
            rotate: bind rotate
            content: bind text
            fill: Color.YELLOW
        }
    ];

    init { tl.play(); }
}


In the above MyScene.fx, we define a Timeline object to play an animation of a Text instance. Every 70ms, the text will be rotated by an angle of 5 degree. To demostrate the data exchange between JavaFX and java, we update the content of the text for each cycle of the animation. The line shown below is used to get data from a Java static variable:

text = JavaFXToSwingTest.tf.getText();


Though this is not an elegant approach to update data, we use it here for its simplicity. We will discuss more about this issue later. Now that we have a Scene class, we can work on the java class. The code of JavaFXToSwingTest.java is listed below:


package swingtest;

/**
 * JavaFXToSwingTest.java     http://www.javafxgame.com
 * @author Henry Zhang
 */
import java.awt.*;
import javax.swing.*;
import org.jfxtras.scene.SceneToJComponent;

public class JavaFXToSwingTest extends JFrame {

  public static JTextField tf = new JTextField("JavaFX for SWING");

  public JavaFXToSwingTest() {
    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle("JavaFX in SWING Test");

    Container container = getContentPane();
    container.setLayout(new BorderLayout());

    String sceneClass = "swingtest.MyScene";
    JComponent myScene = SceneToJComponent.loadScene(sceneClass);

    JLabel label = new JLabel(" Below is a JavaFX Animation: ");
    container.add(label, BorderLayout.NORTH);
    container.add(myScene, BorderLayout.CENTER);

    JPanel p = new JPanel();
    p.setLayout(new FlowLayout());

    tf.setColumns(28);
    p.add(tf);
    p.add(new JButton("SWING Button"));

    container.add(p, BorderLayout.SOUTH);
    pack();
  }

  public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(
      new Runnable() {
        public void run() {
          new JavaFXToSwingTest().setVisible(true);
        }
      });
  }
}


The code should be quite straightforward for most Java(Swing) programmers. We used BorderLayout and FlowLayout to arrange a few Swing widgets inside a JFrame. There are two lines to create the JavaFX scene to be loaded into a JFrame:

    String sceneClass = "swingtest.MyScene";
    JComponent myScene = SceneToJComponent.loadScene(sceneClass);


The SceneToJComponent class comes from the JFXtras project. Its loadScene() method loads a JavaFX Scene class and returns a JComponent object, which can be used as a normal Swing JComponent. Running the program you will see a text “JavaFX for SWING” rotating in a window(JFrame). If you change the sentence in the input box, you will see the rotating text changes as well. If you have Windows media player installed on your machine, you can click on the below screenshot to watch a video on how the program runs.


DISCUSSION

1) In the above program, we use the JavaFX code actively poll the data from a Java variable. This may waste quite some processing cycles. We can let the Java code notify JavaFX object instead. This requires a technique of invoking JavaFX classes from Java code. Interested readers can refer to this article for details: Java Code to Call JavaFX Classes.


2) Some people may ask how we should compile and run the code. There are basically two approaches for compiling the code. The first is to use javafxc to compile both the Java and JavaFX code. The second is to use javafxc to compile the JavaFX code into a jar file and javac for the java code. To run the program, you can choose either the javafx or java command. You can refer to my article Calling JavaFX Classes from Java Code for more explanations.


3) The last thing I need to point out is that the JFXtras project uses some internal APIs of JavaFX to implement the loadScene() method. This implies that the code may break in future releases of JavaFX. However, as pointed out by Steve, using the JFXtras Swing wrapper could insulate you from the changes of JavaFX APIs (i.e. he will take care of the changes for you). I think Steve is the one we can bank on, because he always acts fast to keep up with the latest JavaFX version. :)


Please leave comments if you have any questions.


Comments (4) Jul 08 2009

Calling JavaFX Classes from Pure Java Code

Posted: under JavaFX Technology.
Tags: , , , ,

Just after the release of JavaFX 1.0, in one of my posts JavaFX and Java Interoperability, I discussed three possible approaches to invoke JavaFX features from the Java side. These approaches were:

1. The ScriptEngineManager class. It is based on JSR-223, the java scripting API, which allows a java program to call a script(such as JavaFX Script, javascript, perl).
2. The JavaFX reflection API. It can probably call any classes in JavaFX.
3. The JavaFX class implements a Java interface so that a Java program can invoke the JavaFX class via the interface.


The third one seems the most elegant way to call JavaFX from Java. However, there is a drawback: the program should start from the JavaFX side. The reason is that it is simpler to use JavaFX code to instantiate the JavaFX classes which can be passed to Java code. Nevertheless, in some scenario, it would be better to start the program from the java side. For example, if you want to add in some JavaFX features to an existing large java application, it is better to have java code as the entry point. To solve this issue, I am combining the essence of Approach 2 and 3 to create the below example.


Let’s say we want to invoke the latest charting functions of JavaFX 1.2 from the java code. We will first use the JavaFX reflection API to instantiate the JavaFX class. We then use it via its java interface. So we define a Java interface first.

/*
 * JavaInterface.java
 *
 * @author Henry Zhang      http://www.javafxgame.com
 */
package javatest;
public interface JavaInterface {
  public void addData(String name, float data);
  public void showChart();
}

The next step is to create a JavaFX class MyChart to implements this interface:

/*
 * MyChart.fx
 *
 * @author Henry Zhang     http://www.javafxgame.com
 */
package javatest;

import javafx.scene.chart.PieChart;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.scene.chart.PieChart3D;

public class MyChart extends JavaInterface {
  var chartData :  PieChart.Data[] = [];

  public override function addData( l:String, v: Number):Void {
    var labelString = l;

    var data =  PieChart.Data {
      label : l
      value : v
      action: function() {
        println("{labelString} clicked!");
      }
     } ;

    insert data into chartData;
  }

  public override function showChart() : Void {
    var chart =
      PieChart3D {
        data : chartData
        pieThickness: 25
        pieLabelFont: Font{ size: 9 };
        pieToLabelLineOneLength: 10
        pieToLabelLineTwoLength : 20
        pieLabelVisible: true
        pieValueVisible: true
        translateY: -50
     };

    Stage {
      title: "PieChart Window"
      width: 520
      height: 300
      scene: Scene {
        content: [
          Text {
            font : Font {
                    size : 16
                   }
            x: 200
            y: 20
            content: "Pie Chart"
          },
          chart
        ]
      }
    }
  }
}

The last thing is to write the java main class JavaTest.

/*
 * JavaTest.java
 * @author Henry Zhang    http://www.javafxgame.com
 */
package javatest;

import javafx.reflect.FXClassType;
import javafx.reflect.FXLocal;
import javafx.reflect.FXLocal.Context;
import javafx.reflect.FXLocal.ObjectValue;

public class JavaTest {
  public static void main(String args[]) {
    Context context = FXLocal.getContext();
    FXClassType instance = context.findClass("javatest.MyChart");
    ObjectValue obj = (ObjectValue)instance.newInstance();

    JavaInterface ji = (JavaInterface)obj.asObject();

    String [] labels = {"January", "Febuary", "March", "April"};
    int [] values = { 18, 20, 25, 37 };

    for ( int i=0; i < values.length; i++ ) {
      ji.addData(labels[i], values[i]);
    }

    ji.showChart();
  }
}



There are three lines in the above code to instantiate a JavaFX class via reflection:

    Context context = FXLocal.getContext();
    FXClassType instance = context.findClass("javatest.MyChart");
    ObjectValue obj = (ObjectValue)instance.newInstance();



The next line is to convert the JavaFX instance into a java interface so that it can be used by Java side:

    JavaInterface ji = (JavaInterface)obj.asObject();



If you are using NetBeans IDE, you can set javatest.JavaTest as the main class in your project properties. This makes sure that the entry point of your program is a java class. Build this project you will get a javatest.jar. Running this program produces the below screenshot:

Java PieChart via JavaFX


To run it from the command line, use the below command:

   javafx -jar javatest.jar


Actually, you could do it in the purest java style by including all the JavaFX runtime stuffs, the command would look like this:

 java -Djava.library.path="<path to javafx sdk lib>"
     -classpath "<all javafx sdk jars>" -jar javatest.jar



Since there are many jar files used by the JavaFX, this purest java approach turns out to be very troublesome. I would rather use the javafx command, which is a wrapper of the above java command.


Please leave comments if you have any questions.

This article is cross-posted at Pure Java Code to Call JavaFX Class. The Chinese translation can be found at http://www.javafxblogs.com.


P.S. Stephen Chin has released the JFXtras 0.5 version. In this project, a class SceneToJComponent is provided to load a JavaFX scence as a SWING component. Though it uses the internal method of the Scence class, it can insulate your code from future JavaFX change(i.e. Steve will take care of the changes for you. :) )


Comments (4) Jun 21 2009

JavaFX SyntaxHighlighter

Posted: under JavaFX Technology.
Tags: , , , , ,

In a modern IDE(Integrated Development Environment), such as NetBeans or Eclipse, syntax elements of a programming language are usually shown in different colors and styles. On a web page, there are tools for highlighting the keywords. They are either client side or server side tools. The SyntaxHighlighter is a client side(javascript) tool to display keywords of a programming language in a particular style(bold, different color etc). It supports quite a few languages such as Java, C, C#, VB, Ruby, javascript. While I was recently writing articles on JavaFX, I found it necessary to come up with a syntax highlighter for JavaFX.


I did some search and found the reserved keywords, then modified the shBrushJava.js to be shBrushJavaFX.js. Now my articles can have highlighted JavaFX syntax. For example:

package pacman;
import java.lang.Math;
import pacman.MazeData;
import pacman.PacMan;

/**
 * @author Henry Zhang  http://www.javafxgame.com
 */

public class MoveDecision {

  // x and y of an intended move
  public var x: Number;
  public var y: Number;

  // evaluate if the move is valid and its score if it's valid
  public function evaluate(pacMan:PacMan, isHollow:Boolean): Void {
    if ( x < 1 or y < 1 or y >= MazeData.GRID_SIZE
         or x >= MazeData.GRID_SIZE){
      score = -1;
      return ;
    }
  }
 . . .
}


If you are interested in using this JavaFX highlighter, you can download the brush file: shBrushJavaFX.js and the CSS file: SyntaxHighlighter.css. Information about the original SyntaxHighlighter 1.5.1 can be found here: SyntaxHighlighter .


For those who are intersted in the reserved words (keywords) of JavaFX, you can find it here. For your convience, I list them below:

abstract after and as assert at attribute before bind bound break
catch class continue def delete else exclusive extends false finally
first for from function if import indexof in init insert instanceof
into inverse last lazy mixin mod new not null on or override
package postinit private protected public-init public public-read
replace return reverse sizeof static step super then this throw
trigger true try tween typeof var where while with



Comments (0) May 02 2009

JavaFX and Java Interoperability

Posted: under JavaFX Technology.
Tags: , , , ,

[ 2009/06/22 Update: My latest article discussed how to use
Pure Java Code to Call JavaFX Class]



From the JavaFX blog, an article discussed the possiblity of invoking methods of JavaFX classes from Java. JavaFX can call Java code without any problem, however, the reverse is not supported by JavaFX. Doing some googling shows that programmers are trying all kinds of hacks to invoke a JavaFX class method from Java. You can check out an interesting article on reverse engineering of JavaFX classes. Even the example on JavaFX blog provided a hack to work around this.

 

So do we have the need of such kind of interaction between Java and JavaFX? I say it is a “YES”. If Java and JavaFX can be used interchangeably(when possible), this could give more life to JavaFX in the long run. Just consider the MVC design pattern, we can write an application by using Java and JavaFX together. The “M” and “C” part can be implemented in Java while the “V” can be done by JavaFX. It would be very interesting to see this.

 

Right now, there are a few “standard” ways to call JavaFX from Java:

1) Using the ScriptEngineManager class. From Geertjan Wielenga’s article, we can do it in this way:

package calc; 

import java.io.InputStreamReader;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException; 

public class CalculatorLauncher { 

public static void main(String[] args) {
 try {
 ScriptEngineManager manager=new ScriptEngineManager();
 ScriptEngine engine = manager.getEngineByExtension("fx");
 InputStreamReader reader = new InputStreamReader
 (CalculatorLauncher.class.getResourceAsStream
 ("Calculator.fx"));
 engine.eval(reader);
   } catch (ScriptException ex) {
  }
 }
}

However, this is just like System.exec(”calc”) as pointed out by cronseaux. I agree with him. A even simpler way is to use System.exec(”javafx Calculator.fx”) to complete the above code. So this is not a good solution.

 

2) Manage to use java reflection to call JavaFX class methods. This one should work because JavaFX classes are compiled into Java classes and byte code. However, the complexity makes it almost unusable and code written in this way has no readability.

 

3) A third approach is to define an interface in Java and implement it in JavaFX. For example,

public interface JavaInterface
{ ... }

In MyJavaFXClass.fx, do something like:

public class MyJavaFXClass extends JavaInterface
{ ... }

In you java code, just invoke the JavaFX object directly using the interface. This approach can solve most of the interoperation issues. Just that an interface is needed for every JavaFX class which is going to be called from Java. Though it is very cumbersome, at least it is the best workaround I can find so far.

 

Since this is the first release of JavaFX, I don’t criticize much on this given the strong powerful features of JavaFX. I do hope the future releases of JavaFX can improve on this.

[ 2009/06/22 Update: Please refer to my latest article for discussion on: Pure Java Code to Call JavaFX Class ]

Comments (2) Dec 26 2008