Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX + AWT Canvas

Tags:

javafx

awt

I read that running AWT With JavaFX is a bad idea. But we have an old application that runs on Swing and uses the AWT canvas(Cannot change due to an external library that uses the canvas)

Is it really such a horrible idea? Is there a workaround for this?

like image 966
David Limkys Avatar asked May 17 '14 15:05

David Limkys


1 Answers

Update

Although the code in this answer used to work on Windows with an earlier version of JavaFX, I retested the same same code on OS X 10.9.5 + JavaFX 8u72 and the code no longer works.

The line swingNode.setContent(awtInitializerTask.get()); which instructs the JavaFX thread to wait on the awt thread to initialize the awt canvas never returns, blocking execution and startup of the app.


Just put your AWT canvas in a SwingNode and watch your thread management and you'll be fine.

import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

import javax.swing.*;
import java.awt.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class AwtCanvasWrapper extends Application {
    private static final int W = 200;
    private static final int H = 100;

    @Override public void start(final Stage stage) throws Exception {
        final AwtInitializerTask awtInitializerTask = new AwtInitializerTask(() -> {
            JPanel jPanel = new JPanel();
            jPanel.add(new CustomAwtCanvas(W, H));

            return jPanel;
        });

        SwingUtilities.invokeLater(awtInitializerTask);

        SwingNode swingNode = new SwingNode();
        swingNode.setContent(awtInitializerTask.get());

        stage.setScene(new Scene(new Group(swingNode), W, H));
        stage.setResizable(false);
        stage.show();
    }

    private class AwtInitializerTask extends FutureTask<JPanel> {
        public AwtInitializerTask(Callable<JPanel> callable) {
            super(callable);
        }
    }

    private class CustomAwtCanvas extends Canvas {
        public CustomAwtCanvas(int width, int height) {
            setSize(width, height);
        }

        public void paint(Graphics g) {
            Graphics2D g2;
            g2 = (Graphics2D) g;
            g2.setColor(Color.GRAY);
            g2.fillRect(
                0, 0, 
                (int) getSize().getWidth(), (int) getSize().getHeight()
            );
            g2.setColor(Color.BLACK);
            g2.drawString("It is a custom canvas area", 25, 50);
        }
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Here is the output:

uglycanvas

Related Question

  • Interoperability between Graphics2D and GraphicsContext

Answering some additional questions

But that one is for swing components.

Yes, but awt components can be wrapped in Swing components.

furthermore It says in the docs that it should not be used of heavyweight components

Regardless, it seems to work for me, your mileage may vary.

performance is crucial for my app

Then try the approach with your app and check:

  1. The painting is reliable.
  2. The performance is acceptable.

If either of the above checks fail then you may need to use a different approach (though I do not know what that approach would be, maybe just spawn Frame as a new window in which to include the the AWT canvas content rather than embedding the canvas inside the JavaFX scene).

like image 145
jewelsea Avatar answered Sep 29 '22 16:09

jewelsea