javaswingjavafxjgraphx

Integrating mxgraph inside JavaFX's fxml file


I need to make a desktop application that generates a graph on a JavaFX FXML file. I am using mxGraph libary in eclipse to build the graph and Scene Builder to edit the FXML file, but i can't make the graph to show inside the FXML file, it only works when i use JFrame. When i run like this:

public class mxgraphteste extends JFrame{

public mxgraphteste(){
  mxGraph grafo = new mxGraph();
  Object parent = grafo.getDefaultParent();

  Object v1 = grafo.insertVertex(parent, null, "Brazil", 100, 100, 50, 40);
  Object v2 = grafo.insertVertex(parent, null, "Soccer", 240, 150, 50, 40);
  Object a1 = grafo.insertEdge(parent, null, "loves", v1, v2);

  mxGraphComponent graphComponent = new mxGraphComponent(grafo);
  getContentPane().add(graphComponent);
}

public static void main(String[] args) {
    mxgraphteste frame = new mxgraphteste();
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.setSize(400, 320);
  frame.setVisible(true);
}

}

It works. But i need the graph to go inside this:

<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="view.TelaPrincipalController">
    <children>
        <BorderPane fx:id="borderPane" layoutX="446.0" layoutY="141.0" prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
            <right>
                <Pane fx:id="paineDireita" prefHeight="491.0" prefWidth="126.0" BorderPane.alignment="CENTER">
                    <children>
                        <Button fx:id="buttonNo" layoutX="37.0" layoutY="179.0" mnemonicParsing="false" onAction="#selecionarNo" text="Nó" />
                        <Button fx:id="buttonAresta" layoutX="28.0" layoutY="259.0" mnemonicParsing="false" text="Aresta" />
                    </children>
                </Pane>
            </right>
            <center>
                <Pane fx:id="paineCentro" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" /> **~~~~~~~~~~~~~~~~ I NEED IT GO INSIDE THIS PANE ~~~~~~~~~~~~**
            </center>
        </BorderPane>
    </children>
</AnchorPane>

I am already using jdk8. I tryed to follow Oracle's tutorial for Swing Content in JavaFX Applications but i couldn't turn the graph into SwingNode. I found solutions using JFXPanel for Swing Applications using JavaFX, but it didn't worked on the opposite. Does anyone knows what i did wrong or how can i solve this? Thank you!


Solution

  • I don't know mxGraph, so I am just going to assume your code is correct for that part.

    To embed a swing component into JavaFX, you need to wrap it in a SwingNode. As pointed out in the comments, you need to create the swing content on the AWT event dispatch thread. Since the FXMLLoader runs on the FX Application Thread, you cannot create your mxGraph directly in the FXML file, and will have to do part of the initialization in your controller class.

    So you can do the following in FXML:

    <AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="view.TelaPrincipalController">
        <children>
            <BorderPane fx:id="borderPane" layoutX="446.0" layoutY="141.0" prefHeight="600.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                <right>
                    <Pane fx:id="paineDireita" prefHeight="491.0" prefWidth="126.0" BorderPane.alignment="CENTER">
                        <children>
                            <Button fx:id="buttonNo" layoutX="37.0" layoutY="179.0" mnemonicParsing="false" onAction="#selecionarNo" text="Nó" />
                            <Button fx:id="buttonAresta" layoutX="28.0" layoutY="259.0" mnemonicParsing="false" text="Aresta" />
                        </children>
                    </Pane>
                </right>
                <center>
                    <Pane fx:id="paineCentro" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" >
                        <SwingNode fx:id="swingComponentWrapper"/>
                    </Pane>
                </center>
            </BorderPane>
        </children>
    </AnchorPane>
    

    Now in your controller you can do:

    public class TelaPrincipalController {
    
        @FXML
        private SwingNode swingComponentWrapper ;
    
        public void initialize() {
            SwingUtilities.invokeLater(this::createMxGraph);
        }
    
        private void createMxGraph() {
            mxGraph grafo = new mxGraph();
            Object parent = grafo.getDefaultParent();
    
            Object v1 = grafo.insertVertex(parent, null, "Brazil", 100, 100, 50, 40);
            Object v2 = grafo.insertVertex(parent, null, "Soccer", 240, 150, 50, 40);
            Object a1 = grafo.insertEdge(parent, null, "loves", v1, v2);
    
            mxGraphComponent graphComponent = new mxGraphComponent(grafo);
    
            swingComponentWrapper.setContent(graphComponent);
        }
    
        @FXML
        private void selecionarNo() {
            // ...
        }
    
        // etc etc...
    }
    

    Note that SwingNode only works with lightweight components (i.e. those whose painting is managed by Swing); if mxGraph uses heavyweight components this may not work.