Swing JFrame crashes JavaFX application on OS X

I am working on a complex JavaFX project that needs to include Swing JFrames. Everything works fine, JFrames however refuse to close under certain conditions, blocking the entire virtual machine. I've thrown everything down to a minimal working example that seems to work on Windows (please confirm if it works on your machine), but with a reliable OS X bump. I'm using Java 8u25 (latest stable) and 8u40 preview (latest build ) - no difference.

How to reproduce: Save the program as "JavaFXWithJFrames.java", compile it and run it. There are now 3 Swing JFrames and 1 JavaFX window with 3 buttons. Pressing the buttons should close 1 of the windows, respectively. This works on Windows (?) But completely blocks the program on OS X (and possibly other OSs?)

Can you reproduce this? Yes / No, which machine / OS / JRE? What am I doing wrong here? Many thanks for your help.

Pastebin code: http://pastebin.com/tUrdNfCw# - save as "JavaFXWithJFrames.java" to compile!

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
//package javafxwithjframes;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javax.swing.JFrame;

/**
 * When starting, a JavaFX window and 3 Swing JFrame-windows appear.
 * The JavaFX window features 3 buttons that each are supposed to close one of the JFrames.
 * The program, however, freezes after clicking a button. What am I doing wrong?
 * It seems to work on Windows, but crash on OS X 10.10.1, using Java 8u25 (latest release) and Java 8u40 Dec 31 preview-build.
 *
 * @author a desperate developer
 */
public class JavaFXWithJFrames extends Application {

JFrame jframe1, jframe2, jframe3;

@Override
public void start(Stage primaryStage) {

    // Create 3 JFrames
    jframe1 = new JFrame("JFrame 1");
    jframe1.setBounds(50, 50, 200, 150);
    jframe1.setVisible(true);

    jframe2 = new JFrame("JFrame 2");
    jframe2.setBounds(275, 50, 200, 150);
    jframe2.setVisible(true);

    jframe3 = new JFrame("JFrame 3");
    jframe3.setBounds(500, 50, 200, 150);
    jframe3.setVisible(true);

    // Create 3 buttons that close each one JFrame
    // Button 1
    Button closeButton1 = new Button();
    closeButton1.setText("Close JFrame 1");
    closeButton1.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            jframe1.setVisible(false);
            System.out.print("Closing JFrame 1...");
            jframe1.dispose();
        }
    });

    // Button 2
    Button closeButton2 = new Button();
    closeButton2.setText("Close JFrame 2");
    closeButton2.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            jframe2.setVisible(false);
            System.out.print("Closing JFrame 2...");
            jframe2.dispose();
        }
    });

    // Button 3
    Button closeButton3 = new Button();
    closeButton3.setText("Close JFrame 3");
    closeButton3.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            jframe3.setVisible(false);
            System.out.print("Closing JFrame 3...");
            jframe3.dispose();
        }
    });

    // Setting up main window
    HBox rootBox = new HBox();
    rootBox.getChildren().addAll(closeButton1, closeButton2, closeButton3);
    Scene scene = new Scene(rootBox, 400, 250);
    primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
        @Override
        public void handle(WindowEvent event) {
            System.exit(0);
        }
    });
    primaryStage.setTitle("JavaFX with JFrames");
    primaryStage.setScene(scene);
    primaryStage.show();
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}

      

+3


source to share


1 answer


You have a threading problem: you are doing everything in the thread of the FX application, whereas Swing's job is to be on the AWT event dispatch thread.

You can schedule code execution in the AWT event thread with SwingUtilities.invokeLater(...);

.

So your code should look like

@Override
public void start(Stage primaryStage) {

    SwingUtilities.invokeLater( () -> {
        // Create 3 JFrames
        jframe1 = new JFrame("JFrame 1");
        jframe1.setBounds(50, 50, 200, 150);
        jframe1.setVisible(true);

        jframe2 = new JFrame("JFrame 2");
        jframe2.setBounds(275, 50, 200, 150);
        jframe2.setVisible(true);

        jframe3 = new JFrame("JFrame 3");
        jframe3.setBounds(500, 50, 200, 150);
        jframe3.setVisible(true);
    });

    // Create 3 buttons that close each one JFrame
    // Button 1
    Button closeButton1 = new Button();
    closeButton1.setText("Close JFrame 1");
    closeButton1.setOnAction(event -> {
        SwingUtilities.invokeLater(() -> {
            jframe1.setVisible(false);
            System.out.print("Closing JFrame 1...");
            jframe1.dispose();
        });
    });

    // similarly for other button handlers...

}

      



(I changed the event handler to lambda expression since nested inner classes are just too ugly and you said you were using JDK 8 ...)

You can also try to run the application as is, but with an experimental system property

-Djavafx.embed.singleThread=true

This launches both sets of UI tools on the same thread. Since (to my knowledge) this is still experimental, I would recommend scheduling the code on the "correct" threads as shown above.

+6


source







All Articles