Phrixus
Phrixus

Reputation: 1219

Run new GUI window from an event of another class

I have 2 classes. Both implements runnable to create the GUI. The first one is the main, and the second one is the secondary class.

I want within the actionlistener of the main class to startup the secondary class.

Here is the code (the two classes are separated files):

public class Main implements Runnable
{
    private JTextField txt1, txt2;
    private JLabel lbl1, lbl2;

    public void run() 
    {
        JFrame frame = new JFrame("Secondary");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container pane = frame.getContentPane();

        JPanel background = new JPanel();
        background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));

        ......... 

        // Horizontally adding the textbox and button in a Box
        Box box = new Box(BoxLayout.Y_AXIS);
        ......

        background.add(box);
        pane.add(background);

        frame.pack();
        frame.setVisible(true);
    }

    private class SListener implements ActionListener 
    {
        public void actionPerformed(ActionEvent a)
        {
            Secondary s = new Secondary();
        }
    } 

    public static void main (String[] args) 
    {
        Main gui = new Main();
        SwingUtilities.invokeLater(gui);
    }

}


public class Secondary implements Runnable
{
    private JTextField txt1, txt2;
    private JLabel lbl1, lbl2;

    public Secondary()
    {
      Secondary gui = new Secondary();
      SwingUtilities.invokeLater(gui);
    }

    public void run() 
    {
        JFrame frame = new JFrame("Secondary");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container pane = frame.getContentPane();

        JPanel background = new JPanel();
        background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));

        ......... 

        // Horizontally adding the textbox and button in a Box
        Box box = new Box(BoxLayout.Y_AXIS);
        ......

        background.add(box);
        pane.add(background);

        frame.pack();
        frame.setVisible(true);
    }   
}

I want to keep the code in two files, I don't want to mixed the two classes in one file. As you can see from the code, in the Secondary class, in it's constructor I create an Instance of the Secondary class and I run the gui so that when the Instance of this class is created in the Main class, to run the gui.

Unfortunately this technique is not working.

Any ideas? Thanks

Upvotes: 1

Views: 261

Answers (2)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

I would recommend that you completely re-design your program. I find that it is most helpful to gear my GUI's towards creation of JPanels, not top level windows such as JFrame, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. I find that this greatly increase the flexibility of my GUI coding, and is exactly what I suggest that you do. So...

  • Your first class creates a JPanel that is then placed into a JFrame.
  • In the first class's ActionListener, create an instance of the 2nd class, place it into a JDialog (not a JFrame), and then display it.

For example,

import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class TwoWindowEg {
   public TwoWindowEg() {
      // TODO Auto-generated constructor stub
   }

   private static void createAndShowGui() {
      GuiPanel1 mainPanel = new GuiPanel1();

      JFrame frame = new JFrame("Main GUI");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class GuiPanel1 extends JPanel {
   private static final int PREF_W = 800;
   private static final int PREF_H = 650;
   private GuiPanel2 guiPanel2 = new GuiPanel2(); // our second class!
   private JDialog dialog = null;  // our JDialog

   public GuiPanel1() {
      setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
      add(new JButton(new LaunchNewWindowAction("Launch New Window")));
      add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   private class LaunchNewWindowAction extends AbstractAction {
      public LaunchNewWindowAction(String name) {
         super(name);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         if (dialog == null) {
            // get the Window that holds this JPanel
            Window win = SwingUtilities.getWindowAncestor(GuiPanel1.this);
            dialog = new JDialog(win, "Second Window", ModalityType.APPLICATION_MODAL);
            dialog.add(guiPanel2);
            dialog.pack();
         }
         dialog.setVisible(true);
      }
   }
}

class GuiPanel2 extends JPanel {
   public GuiPanel2() {
      setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
      add(new JLabel("The second JPanel/Class"));
      add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
   }
}

class DisposeAction extends AbstractAction {
   public DisposeAction(String name, int mnemonic) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      Component comp = (Component) e.getSource();
      Window win = SwingUtilities.getWindowAncestor(comp);
      win.dispose();
   }
}

Alternatively, you could swap JPanel "views" using a CardLayout, but either way, you will want to avoid showing two JFrames. Please have a look at The Use of Multiple JFrames, Good/Bad Practice?.

Upvotes: 2

Robin
Robin

Reputation: 36611

The following line are complety wrong:

public Secondary(){
  Secondary gui = new Secondary();
  SwingUtilities.invokeLater(gui);
}

Each time you call new Secondary() somewhere in your code, the above code will be triggered, which in turn calls new Secondary() again, and again, and again, ... and your program is blocked.

You probably want to replace it either by

public Secondary(){
      SwingUtilities.invokeLater(this);
}

which will avoid the loop, but this is weird behaviour for a constructor.

It makes much more sense to switch to an empty constructor (or delete it all together)

public Secondary(){
}

and rewrite your listener to

public void actionPerformed(ActionEvent a){
  Secondary s = new Secondary();
  SwingUtilities.invokeLater( s );
}

Upvotes: 2

Related Questions