Home > front end >  Returning value to Java program from Swing GUI
Returning value to Java program from Swing GUI

Time:03-31

Background

I am writing this GUI as a plugin for another program. The GUI prompts the user to enter values which should be returned to the main program when the window closes (in this case the println statement in tester).

My current method is using an ActionListener on the okButton, which when clicked, sets returnValue to the text in the text field, and closes the window.

I have read through the docs and attempted to search terms such as "return value from GUI", "return field value after button press", but I have not yet found anything relating to my use case. I have minimal experience with Java GUI's, so there is a solid chance that I am missing a built-in way of doing this.

Driver class:

public class Tester {

    public static void main(String[] args) {

        TestWindow tw = new TestWindow();
        String ret = tw.run();
        System.out.println(ret);
    }
}

Window Class

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class TestWindow {
    JFrame jFrame = new JFrame();
    JTextField textField = new JTextField("", 10);
    JButton okButton = new JButton("OK");
    String returnValue = "defaultValue";

    public TestWindow() {
        jFrame.setSize(400, 400);
        textField.setBounds(50, 100, 200, 30);
        okButton.setBounds(50, 150, 200, 30);
        okButton.addActionListener(e -> {
            // Set returnValue field to textField value
            returnValue = textField.getText();
            // Dispose of JFrame
            jFrame.dispose();
        });
        jFrame.add(textField);
        jFrame.add(okButton);
        jFrame.setLayout(null);
    }

    public String run() {
        jFrame.setVisible(true);
        return textField.getText();
    }

}

When I step through the driver class with a debugger, the value returned is always "defaultValue", or the initial default value of retValue. Furthermore, the print statement after the run method runs immediately as the window appears, not after the GUI exits.

Question

How can I give this value back to the main program when the OK button is clicked? (My actual program returns a hashmap of the various values set in the UI, but I have simplified it to a text field for purposes of a MCVE).

CodePudding user response:

Use a modal dialog, see How to Make Dialogs for more details.

As a simple example, you could just use JOptionPane...

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        JTextField textField = new JTextField(10);
        textField.setText("Default value");
        JPanel contentPane = new JPanel();
        contentPane.add(new JLabel("Type something: "));
        contentPane.add(textField);

        JOptionPane.showMessageDialog(null, contentPane, "Type someting", JOptionPane.PLAIN_MESSAGE);

        System.out.println(textField.getText());
    }
});

The alternative would be to implement some kind of observer pattern which could be called when the ActionListener is triggered, but to be honest, a JOptionPane is just simpler

CodePudding user response:

I propose writing a minimal interface for a callback, such as:

interface StuffToDoWithDialog {
    // will be called once values for foo and bar are ready
    void doStuff(String foo, int bar);
}

and building the interface so that, once values are ready, the passed-in instance of this interface is called:

public static void showDialog(StuffToDoWithDialog callback) {
    SwingUtilities.invokeLater(() -> {
        // ... draw interface ...
        // ... and somewhere, once values are ready, call callback
        callback.doStuff(foo, bar);
    });
}

this makes it very simple to test, and you can pass in much more complex behavior when needed:

public static void main(String ... args) {
    showDialog((foo, bar) -> {
        System.out.println("Foo is "   foo);
        System.out.println("Bar is "   bar);
    });
}

Working program (save as Example.java):

import javax.swing.*;
import java.awt.*;

public class Example {

    interface StuffToDoWithDialog {
        void doStuff(String foo, int bar);
    }

    public static void main(String ... args) {
        showDialog((foo, bar) -> {
            System.out.println("Foo is "   foo);
            System.out.println("Bar is "   bar);
        });
    }

    private static JTextField addFormRowToPanel(JPanel panel, String label, int rowNum) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.ipadx = 2;
        gbc.gridx = 0;
        gbc.gridy = rowNum;
        panel.add(new JLabel(label), gbc);
        JTextField field = new JTextField();
        field.setPreferredSize(new Dimension(50, (int)field.getPreferredSize().getHeight()));
        gbc.gridx = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        panel.add(field, gbc);
        return field;
    }

    public static void showDialog(StuffToDoWithDialog callback) {
        SwingUtilities.invokeLater(() -> {
            // builds contents for dialog: two inputs with labels
            JPanel jp = new JPanel(new GridBagLayout());
            JTextField foo = addFormRowToPanel(jp, "String foo", 0);
            JTextField bar = addFormRowToPanel(jp, "Integer bar", 1);
            
            boolean valid = false;
            while ( ! valid) {
                // blocks while showing dialog
                JOptionPane.showMessageDialog(null, 
                    jp, "Input options", JOptionPane.PLAIN_MESSAGE);
                // checks results
               if (foo.getText() != null 
                    && bar.getText() != null 
                    &&  bar.getText().matches("[0-9] ")) {
                        valid = true;
                } else {
                    // complain: bad inputs
                    int rc = JOptionPane.showConfirmDialog(null, 
                        "Invalid inputs, try again or cancel?", "Error", 
                        JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE);
                    if (rc == JOptionPane.CANCEL_OPTION) return;
                }
            }

            // returns results
            callback.doStuff(foo.getText(), Integer.parseInt(bar.getText()));
        });
    }    
}
  • Related