1
public static void main(String... s) {
        StartUp obj = new StartUp();
          
        while(true) {
            //System.out.println("Option - " + option);
            if(option == 1) {
                option = 0;
                obj.setVisible(false);
                obj.dispose();
                new Test();
                break;
            }else if(option == 2) {
                option = 0;
                obj.setVisible(false);
                obj.dispose();
                new PWorld.Splash().load();
                break;
            }
        }
    }
    

I need to put System.out.println("Option - " + option); inside the while loop for it to work otherwise the programe freezes after running StartUp obj = new StartUp();

option is a static int inside StartUp class and is changed by a Actionlistener the value in option is changed by ActionListener but while loop doesn't seems to work.

But if I put System.out.println("Option - " + option); inside while loop, it works. WHY!

I'm using this while loop because new PWorld.Splash().load(); has Thread.sleep(), and as in this answere new JFrame will not be drawn if called from ActionListener(in UI Thread)which has Thread.

Thank you

  • 2
    Regardless of whether or not delaying the loop with a println helps it to work, the main recommendation that I can give you is this: ***get rid of the loop entirely***. You're using an event-driven GUI library and so there is no need to loop, and instead, you should write code that responds to events with behaviors based on program state (values held by key fields of your objects). – DontKnowMuchBut Getting Better Aug 29 '20 at 19:11
  • @DontKnowMuchBut Getting Better I can't call the methods directly in ActionListener, since for example `new PWorld.Splash().load();` uses Threads which interferes with UI Thread of ActionListenser – Sameer Gupta Aug 29 '20 at 19:33
  • 1
    No, that is not so. New Threads can be created within ActionListeners, and in fact I do it all the time. The question looks to be an [xy problem](https://en.wikipedia.org/wiki/XY_problem) type problem where you are probably asking the wrong question. The right one is how to integrate this code correctly into the GUI without interfering with GUI functionality, and to answer this, you need to tell more detail and perhaps post a valid [mre]. – DontKnowMuchBut Getting Better Aug 29 '20 at 19:39
  • You may wish to [edit] and improve the question, telling these details since I think that your whole approach needs to be changed, including getting rid of the while loop. – DontKnowMuchBut Getting Better Aug 29 '20 at 19:40
  • Example code posted in my answer. Please check it out, run it, comment if you have any questions – DontKnowMuchBut Getting Better Aug 29 '20 at 20:44

2 Answers2

1

Your issues are:

  • You're calling a "tight" loop, one that hogs the CPU and blocks other code from running. The System.out.println(...) statement adds code that slows this loop, releasing the CPU from the jaws of the tight loop, allowing other threads to run, and this is the genesis of your question.
  • Having said that, again, your coding approach is not good, in that you're using a while (true) loop in place of responding to an event, which is how Swing GUI's should be coded.
  • You state that the reason for this is that one bit of code in the while loop calls a Thread.sleep and that this code, if called on the Swing event thread, such as within an ActionListener, will block the event thread, freezing your GUI -- all true.
  • But your solution is wrong. The correct solution is not to call this in a while (true) loop in the main method, but rather to either call the Thread.sleep from a background thread, such as within the doInBackground() method of a SwingWorker (link is to tutorial), or better still, to use a Swing Timer (again, link is to tutorial) in place of the Thread.sleep. This will allow your code to pause some code without blocking the Swing event thread.
  • Another option, if you need to show a dialog (sub) window is to use a modal JDialog to show a window while blocking interaction with the main GUI window, until the dialog window is no longer visible.

For a more detailed and comprehensive solution, again, please consider creating and posting your Minimal, Reproducible Example program with your question.

For example, here is my Minimal, Reproducible Example:

import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Window;
import javax.swing.*;

public class MinReproExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            Startup startup = new Startup();
            startup.showStartUp();

            Option option = startup.getOption();
            if (option == Option.TEST) {
                JOptionPane.showMessageDialog(null, "Test selected", "Selection", JOptionPane.DEFAULT_OPTION);
            } else if (option == Option.PWORLD) {
                PWorld pworld = new PWorld();
                pworld.showSplash();
            }   
        });
    }
}
class Startup {
    private JDialog startupDialog;
    private Option option = null;
    
    public Startup() {
        ButtonGroup buttonGroup = new ButtonGroup();
        JPanel optionsPanel = new JPanel(new GridLayout(1, 0, 10, 10));
        optionsPanel.setBorder(BorderFactory.createTitledBorder("Options"));
        for (final Option op : Option.values()) {
            JRadioButton rBtn = new JRadioButton(op.getText());
            rBtn.setActionCommand(op.getText());
            optionsPanel.add(rBtn);
            buttonGroup.add(rBtn);
            rBtn.addActionListener(e -> {
                option = op;
                Window window = SwingUtilities.getWindowAncestor(optionsPanel);
                window.dispose();
            });
        }
        
        startupDialog = new JDialog(null, "Select Option", ModalityType.APPLICATION_MODAL);
        startupDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        startupDialog.add(optionsPanel);
        startupDialog.pack();
        startupDialog.setLocationRelativeTo(null);
    }
    
    public void showStartUp() {
        if (startupDialog != null) {
            startupDialog.setVisible(true);
        }
    }
    
    public Option getOption() {
        return option;
    }
}
class PWorld {
    private static final Color ROBINS_EGG_BLUE = new Color(0, 204, 204);
    private JDialog pworldSplashDialog;
    private JFrame mainPWorldFrame;

    public PWorld() {
        JLabel splashLabel = new JLabel("Splash Window", SwingConstants.CENTER);
        JPanel splashPanel = new JPanel(new GridBagLayout());
        splashPanel.add(splashLabel);
        splashPanel.setBackground(Color.PINK);
        splashPanel.setPreferredSize(new Dimension(300, 250));

        pworldSplashDialog = new JDialog(null, "Splash", ModalityType.MODELESS);
        pworldSplashDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        pworldSplashDialog.add(splashPanel);
        pworldSplashDialog.pack();
        pworldSplashDialog.setLocationRelativeTo(null);

        JLabel mainLabel = new JLabel("Main GUI Window", SwingConstants.CENTER);
        JPanel mainPanel = new JPanel(new GridBagLayout());
        mainPanel.add(mainLabel);
        mainPanel.setBackground(ROBINS_EGG_BLUE);
        mainPanel.setPreferredSize(new Dimension(500, 350));

        mainPWorldFrame = new JFrame("Main PWorld GUI");
        mainPWorldFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainPWorldFrame.add(mainPanel);
        mainPWorldFrame.pack();
        mainPWorldFrame.setLocationRelativeTo(null);

    }

    public void showSplash() {
        int timerDelay = 2000; // two second delay
        Timer timer = new Timer(timerDelay, e -> {
            if (pworldSplashDialog != null && pworldSplashDialog.isVisible()) {
                pworldSplashDialog.dispose();
                showMainPWorldFrame();
            }
        });
        timer.setRepeats(false);
        timer.start();

        pworldSplashDialog.setVisible(true);
    }

    private void showMainPWorldFrame() {
        mainPWorldFrame.setVisible(true);
    }
}
// options to choose from
enum Option {
    TEST("Test"), PWORLD("PWorld");
    private String text;

    private Option(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}
  • Thank you @DontKnowMuchBut Getting Better , [You may want to see this](https://stackoverflow.com/questions/63652166/jframe-not-showing-up-properly-when-called-from-actionlistener) – Sameer Gupta Aug 29 '20 at 22:00
  • 1
    @SameerGupta: nah, I don't need to see that. MadProgrammer has forgotten more Swing than I'll ever know, and he should have things well in hand. – DontKnowMuchBut Getting Better Aug 29 '20 at 22:10
0

What is this loop expected to do if option is not 1 or 2 on initial entry? You're just burning CPU cycles for no reason, waiting for some other thread to do something.

Adding the print statement injects a bit of non-CPU-burning delay, in which case maybe the thread that's going to set 'option' gets to run.

(FWIW, 'option' likely needs to be declared volatile if you're expecting changes to be made visible to other threads).

This is not a good design. I can't tell enough about the context to tell you what you should do, but some sort of decent notification mechanism is needed. But this should answer your question of 'WHY?'.

J.Backus
  • 1,441
  • 1
  • 6
  • 6
  • I can't call the methods directly in ActionListener, since for example `new PWorld.Splash().load();` uses `Thread .sleep` which interferes with UI Thread of ActionListenser and doesn't draw the new frame. So I figured out this way – Sameer Gupta Aug 29 '20 at 19:39
  • ActionListener is an interface. I'd guess you are supposed to implement that interface, and then respond to whatever changes the option *there*. That is, your design is upside-down. You don't call it; it calls you. – J.Backus Aug 29 '20 at 23:24