-1

I have a class that extends Application:

public class IO extends Application {
    private static JFrame frame = new JFrame("Shapes");;
    private final JPanel content = new JPanel();
    private final JPanel drawingArea = new JPanel();

my main/start method:

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

public void start(Stage primaryStage) throws Exception {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            new JFXPanel(); // initializes JavaFX environment
            createGUI();
        }
    });
}

I've done this to hopefully resolve any troubles between the swing/JavaFX components per this question. However, when I click a button that adds either a JPanel or a JFXPanel to another JPanel, if it was the JFXPanel, it will perform once just fine, but it will fail anytime thereafter that I try to generate a JFXPanel again, giving an "Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException" (but it actually takes 3 times total for this error to appear - the second time it will freeze, and if it doesn't, it does nothing). Here's the relevant code for the button:

JButton go = new JButton("Go");
go.addActionListener(new ActionListener() {
    // irrelevant code
        System.out.println("Shape set parameters: " + selectedShape.setParameters(shapeParams));
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new JFXPanel(); // initializes JavaFX environment
                Component shapePanel = (Component) new PaintedShape(selectedShape).getPanel();
                drawingArea.add(shapePanel);
                // drawingArea.add(new JLabel("Test"));
                // drawingArea.repaint();
                shapePanel.repaint();
                shapePanel.validate();
                drawingArea.repaint();
                drawingArea.validate();
                frame.pack();
                frame.setSize(new Dimension((int) (frame.getSize().getWidth() + PaintedShape.size.getWidth()),
                        (int) (frame.getSize().getHeight() + PaintedShape.size.getHeight())));
                frame.setLocationRelativeTo(null);
            }
        });
    }
});
params.add(go);
content.validate();

I call validate() a bunch of times because it was failing to add shapes sometimes, and one of those sets of calls initially fixed that error. Bonus points if you explain which panel is more relevant to call it on for me. If you feel you need the rest of the code, I created a github repo here.

Edit: full stacktrace -

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at com.sun.javafx.tk.quantum.QuantumToolkit.isSupported(QuantumToolkit.java:1153)
    at com.sun.javafx.application.PlatformImpl.isSupportedImpl(PlatformImpl.java:809)
    at com.sun.javafx.application.PlatformImpl.isSupported(PlatformImpl.java:482)
    at javafx.application.Platform.isSupported(Platform.java:168)
    at javafx.scene.shape.Shape3D.<init>(Shape3D.java:74)
    at javafx.scene.shape.Cylinder.<init>(Cylinder.java:89)
    at shapes.PaintedShape.painted3DShapePanel(PaintedShape.java:96)
    at shapes.PaintedShape.<init>(PaintedShape.java:51)
    at shapes.IO$1$1$1.run(IO.java:215)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Edit: Debugging further lead me to discover that it was not possible to instantiate any new three dimensional javafx shape in PaintedShape.java (see line 6 of the stack trace - at javafx.scene.shape.Shape3D.<init>(Shape3D.java:74)), possibly due to thread safety. Some of my fixes are included in my answer below, but I didn't mention that, despite my code being wrapped in Runnable()s "invoked later", I redundantly nested the specific JFXPanel generating code in the same runnable structure as well. This may have helped, though I'm not exactly sure how.

Platform.runLater(() -> {
    try {
        SwingUtilities.invokeLater(() -> {
            drawingPanel.add(shapePanel);
            frame.pack();
        });

    } catch (Exception e) {
        e.printStackTrace();
    }

});
ganondork
  • 41
  • 7
  • 3
    Any good reason to mix Swing and JavaFx ? Please post [mre] and the full stacktrace. – c0der Sep 14 '21 at 08:29
  • @c0der No good reason, I had just begun with simple shapes (circle, rectangle, etc.) and so I was just overriding paintComponent in JPanel. When I moved to the 3D shapes, I realized I needed JavaFX. The assignment didn't explicitly state I couldn't just use awt's Ellipse2D, for instance, but I wasn't sure, so I went ahead and just drew everything with Graphics.drawLine using a collection of Points originally. As far as minimal reproducible, I will try to cut it down to a single file, but it is extremely difficult since I don't really know what's negatively interacting. – ganondork Sep 14 '21 at 08:36
  • @c0der added stack trace. – ganondork Sep 14 '21 at 08:45
  • 1
    See [What is a stack trace, and how can I use it to debug my application errors?](https://stackoverflow.com/q/3988788/418556) Then you might understand why, to make sense of the line numbers in the stack trace, we'd need an MRE as suggested by @c0der. – Andrew Thompson Sep 14 '21 at 09:06
  • 2
    If there is no good reason to mix Swing and JavaFX, then don’t do it. – jewelsea Sep 14 '21 at 09:20
  • 1
    From the stack trace you have JavaFX code running on the AWT event queue. Don’t do that, nothing good will come of it. – jewelsea Sep 14 '21 at 09:23
  • 1
    Swing (actually AWT) `Graphics` supports drawing 3D rectangles. For sphere see https://stackoverflow.com/questions/42531692/drawing-multiple-ovals-in-java/42536820#42536820 – c0der Sep 14 '21 at 10:01
  • @AndrewThompson if I were helping a user who had posted a git repo, I'd just import it and run it, but maybe that's just me (then it'd be pretty obvious how the stack trace relates to the program). I understand that an MRE makes it easiest on you, but in my situation, as I explained, it was very difficult (perhaps more difficult than just brute forcing different methods as I ultimately did successfully) to understand how to make one (coupling too high, probably, I apologize for the poor design in that sense). – ganondork Sep 14 '21 at 11:02
  • @jewelsea I will surely implement your advice in future projects, but it was simply not feasible to do so in this one, so I guess I should amend my response to say that I had a good reason. – ganondork Sep 14 '21 at 11:04
  • @ganondork see [Questions linking to external web sites instead of showing code](https://meta.stackexchange.com/questions/80978/questions-linking-to-external-web-sites-instead-of-showing-code) which helps explain some reasons why having code for the question in the question is the preferred method of asking for most questions. Obviously, for every rule or convention, there are valid exceptions, but, in general, in my experience, it is just a good policy to follow for most questions as it usually helps create both better questions and answers. – jewelsea Sep 14 '21 at 19:14
  • 1
    @ganondork Andrew originally helped devise and popularize the [SSCCE](http://sscce.org), which is, in my opinion (and I think many others would agree), the greatest gift to the StackOverflow community ever made. – jewelsea Sep 14 '21 at 19:16
  • @jewelsea I agree in general that it is poor practice, but I did try to give relevant code, and I did try to isolate the problem, but to no avail. That's why I went so far as to ask the question in the first place. If I had just included the `drawingArea.removeAll()` line, an experienced user may have seen the conflict immediately, as I believe that's what it was (I'm still not certain). I only excluded it in an effort to satisfy the MRE constraint, which ended up being counter to my cause after all. Hindsight is 20/20. – ganondork Sep 14 '21 at 23:59
  • @jewelsea that SSCCE and the notion of MRE are great and all, but it just isn't feasible for every situation. In my example, I had a complex network (again, probably due to my code being too highly coupled, I apologize) that made it really difficult to pin down the culprit. The stack trace wasn't making it very easy for me to figure out what was going wrong either given how long it was. I did debug step through it in an effort to pin it down, but ultimately it was a red herring. – ganondork Sep 15 '21 at 00:07

1 Answers1

-1

Fixed it. I believe the error may have been that, after removing all elements from the JPanel drawingArea, it was not immediately validate()d, and therefore didn't support adding elements to it. I also ensured that the JFXPanel was typed as such rather than being cast to a Component. I also ensured that the JPanel it was added to was created each time the button was pressed via JPanel drawing in case it being in the scope of the method previously mattered for some reason. See:

go.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
    final JPanel drawing = new JPanel();
        // irrelevant
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new JFXPanel(); // initializes JavaFX environment
                if (selectedShape.getClass().getSuperclass().getSimpleName()
                        .equals("TwoDimensionalShape")) {
                    Component shapePanel = (Component) new PaintedShape(selectedShape).getPanel();
                    drawing.add(shapePanel);
                } else {
                    JFXPanel shapePanel = (JFXPanel) new PaintedShape(selectedShape).getPanel();
                    Platform.runLater(() -> {
                        try {
                            SwingUtilities.invokeLater(() -> {
                                drawing.add(shapePanel);
                                frame.pack();
                            });

                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    });
                }
                drawingArea.removeAll();
                drawingArea.validate();
                drawingArea.add(drawing);
                drawingArea.repaint();
                frame.pack();
                frame.setSize(new Dimension((int) (frame.getSize().getWidth()),
                        (int) (frame.getSize().getHeight() + PaintedShape.size.getHeight())));
                frame.setLocationRelativeTo(null);
            }
        });
    }
});

Full code with changes pushed to git repo as well for any more curious.

ganondork
  • 41
  • 7