1

I'm trying to separate my Swing GUI from my actual code. In short, I want the user to kick off a process (based on the user's selections); in this case, the JFrame will no longer be needed.

What I couldn't figure out is how to share the user's selection from the GUI.class with the Main.class.

Do you have any advice for me?

Here's my code:

public class Main {
  public static void main(String[] args) {
    // Show GUI
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        GUI gui = new GUI(templates);
        gui.setVisible(true);
      }
    });

    // Kick off a process based on the user's selection
  }
}

public class GUI extends JFrame {
  private static final long serialVersionUID = 1L;

  public GUI(Object[] objects) {
    setTitle("GUI");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 350, 100);
    setLocationRelativeTo(null);

    JPanel cp = new JPanel();
    cp.setBorder(new EmptyBorder(10, 10, 10, 10));
    setContentPane(cp);

    JLabel lbl = new JLabel("Selection:");
    cp.add(lbl);

    final JComboBox<String> comboBox = new JComboBox<String>(new String[] { "One", "Two", "Three" });
    comboBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        setVisible(false);
        dispose();
        // Share the selected item with Main.class
      }
    });
    cp.add(comboBox);
  }
}
Nate W.
  • 9,141
  • 6
  • 43
  • 65
not_a_number
  • 305
  • 1
  • 6
  • 18
  • 1
    Keep the frame; change the content with [`CardLayout`](http://docs.oracle.com/javase/tutorial/uiswing/layout/card.html). – trashgod Mar 21 '14 at 18:43

2 Answers2

1

You could create an object to store the selection result and pass it in to the constructor of the GUI class. Set the selection result in that object before closing the UI and then your Main class could access the value:

public class SelectionResult {
    private String selectionResult;

    public void setSelectionResult(final String selectionResult) {
        this.selectionResult = selectionResult;
    }

    public String getSelectionResult() {
        return this.selectionResult;
    }
}

Then, you could modify the GUI constructor like this:

private final SelectionResult selectionResult;

public GUI(Object[] objects, SelectionResult selectionResult) {
    this.selectionResult = selectionResult;
    ...

Create a SelectionResult object in your Main class, and pass it to the constructor of the GUI class. In you GUI class ActionListener, you can then call the setSelectionResult() method with the selected value and that value will be available from the Main class.

You would need to add code to make your main method wait while you are waiting for the value to be set in the UI and then proceed with your logic based on the selection.

djmorton
  • 616
  • 4
  • 6
  • Djomrton, thanks a lot! In fact, I had not considered passing an object! But what would be a good way to make the main method wait? Is there anything more suitable than while (true) { if (gui == null) { break; } }? – not_a_number Mar 21 '14 at 19:08
  • You could use a Semaphore, and also pass it as a constructor param: Semaphore semaphore = new Semaphore(0); call the .release() method on it in the GUI class after setting the selection result, and call the .aquire() method on it in the main method where you wish to wait. – djmorton Mar 21 '14 at 19:17
0

A Good way of doing this is use Callback mechanism.

Steps to follow:

  • create a callback interface

    interface Callback {
        void execute(Object result);
    }
    
  • GUI class will implement Callback interface but without providing any implementation

  • Make GUI class abstract

    abstract class GUI extends JFrame implements Callback
    
  • Now create an object of GUI class providing actual implementation of Callback interface

  • Here you can use Anonymous class

    GUI gui = new GUI() {
    
        @Override
        public void execute(Object result) {
            System.out.println("You have selected " + result);
        }
    
    };
    
  • You can pass any thing in execute() method of Callback.

    comboBox.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            setVisible(false);
            dispose();
            // Share the selected item with Main.class
            // Callback
            execute(comboBox.getSelectedItem());
        }
    });
    

Here Main class is responsible for capturing the response of Callback that is directed by GUI class.


Here is the code:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Main {
    public static void main(String[] args) {
        // Show GUI
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                GUI gui = new GUI() {

                    @Override
                    public void execute(Object result) {
                        System.out.println("You have selected " + result);
                    }

                };
                gui.setVisible(true);
            }
        });

        // Kick off a process based on the user's selection
    }
}

interface Callback {
    void execute(Object result);
}

abstract class GUI extends JFrame implements Callback {
    private static final long serialVersionUID = 1L;

    public GUI() {
        setTitle("GUI");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 350, 100);
        setLocationRelativeTo(null);

        JPanel cp = new JPanel();
        cp.setBorder(new EmptyBorder(10, 10, 10, 10));
        setContentPane(cp);

        JLabel lbl = new JLabel("Selection:");
        cp.add(lbl);

        final JComboBox comboBox = new JComboBox(new String[] { "One", "Two", "Three" });
        comboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
                dispose();
                // Share the selected item with Main.class
                execute(comboBox.getSelectedItem());
            }
        });
        cp.add(comboBox);
    }

}
Braj
  • 46,415
  • 5
  • 60
  • 76
  • Thank you very much for the great explanation! This truly offered me a new perspective. – not_a_number Mar 21 '14 at 21:43
  • May I expand my question? Again from the Design Pattern Perspektive: If I want to make some calculations based on the user's selection and dynamically update the GUI - my first idea was to simply nest another Calculations object and make the GUI object static (so that the Calculations object can access the GUI object). Do you have any advice for me? – not_a_number Mar 23 '14 at 18:23
  • I can't say until and unless I have full understanding of your code. Try to code with minimal changes to avoid regression. – Braj Mar 23 '14 at 18:30
  • Obviously, you were referring to an earlier version of my comment. What I meant to avoid (although regression was probably the wrong term) was this: public void run() { gui = new GUI() { (a)Override public void execute(Object result) { Calculations calc = new Calculations(result) { (a)Override public void execute(Object result) { gui.update(result) } } } – not_a_number Mar 23 '14 at 19:06
  • Yes there are lots of ways to do to the same. It there is a single instance of the `GUI` in whole application then you can make it static. I think `Calculations` can also be treated as utility class, so you can define static methods in it. Why are you implementing `Callback` interface for `Calculations`? Use `Callback` when source will be notified about the changes from target. – Braj Mar 24 '14 at 02:46
  • Now I can show you why I'm implementing the Callback interface for the Calculations.class, see [here](http://stackoverflow.com/questions/22629697/dynamically-adding-rows-to-jtable-why-do-they-appear-at-once). – not_a_number Mar 27 '14 at 18:51