Home > Software engineering >  Simulate actionPerformed for a JTextField programatically
Simulate actionPerformed for a JTextField programatically

Time:09-02

I'm trying to simulate adding text to a JTextField in code. I am doing this so I can automatically unit tests what happens when a user enters text.

I've tried the method shown below which worked for other simulations but not the JTextField. How can I make it so the code below enters actionPerformed method like it does in the GUI?

public class Main {

    public static void main(String[] args) {
        RemoteGUI remoteGUI = new RemoteGUI();
        ActionEvent event = new ActionEvent(remoteGUI.getTextField(), ActionEvent.ACTION_PERFORMED, "50");
        remoteGUI.getTextField().dispatchEvent(event);
        System.out.println("Expected to perform action but didn't");
    }
}

class RemoteGUI extends JFrame  {

    JTextField textField;

    public RemoteGUI() {
        JPanel panel = new JPanel();
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setSize(600, 250);
        panel.setLayout(null);

        this.add(panel);


        Font font = new Font("Verdana", Font.BOLD, 28);
        JLabel label = new JLabel("Enter A Number");
        label.setFont(font);
        label.setBounds(10,20,300,50);
        panel.add(label);

        textField = new JTextField("");
        textField.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String velocity = textField.getText();
                System.out.println("Velocity "   velocity   " entered into GUI");
            }
        });

        textField.setFont(font);
        textField.setBounds(320,20,200,50);
        panel.add(textField);
        this.setVisible(true);
    }

    public JTextField getTextField(){
        return textField;
    }
}

CodePudding user response:

Don’t use dispatchEvent(event). Instead, use postActionEvent(). Likewise, for buttons and menu items, you should use doClick(). You should also set the text on the field first, before sending the event.

Generally, use layout managers instead of dealing with absolute coordinates and don’t unnecessarily subclass component classes (like JFrame in your case).

For example

public class Main {
  public static void main(String[] args) {
      RemoteGUI remoteGUI = new RemoteGUI();
      JTextField tF = remoteGUI.getTextField();
      tF.setText("50");
      tF.postActionEvent();
  }
}

class RemoteGUI {
  JTextField textField;

  RemoteGUI() {
      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      Box panel = Box.createHorizontalBox();
      panel.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10));

      Font font = new Font("Verdana", Font.BOLD, 28);
      JLabel label = new JLabel("Enter A Number");
      label.setFont(font);
      panel.add(label);

      panel.add(Box.createHorizontalStrut(20));

      textField = new JTextField(10);
      textField.addActionListener(e -> {
          String velocity = textField.getText();
          System.out.println("Velocity "   velocity   " entered into GUI");
      });

      textField.setFont(font);
      panel.add(textField);

      label.setLabelFor(textField);

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

  JTextField getTextField() {
      return textField;
  }
}

And, since you created a text field for a number, you may consider using a JFormattedTextField instead.

CodePudding user response:

Not a complete answer, but more information.

The following code works:

  1. when the frame is visible
  2. you click on the button
  3. a KeyEvent is used instead of the ActionEvent.

Code:

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

public class Main6 {

    public static void main(String[] args) {
            SwingUtilities.invokeLater(() ->
            {
                RemoteGUI remoteGUI = new RemoteGUI();

                // Doesn't work (maybe add a Timer here to invoke this code after a few ms once the frame is displayed?

                //ActionEvent event = new ActionEvent(getTextField(), ActionEvent.ACTION_PERFORMED, "50");
                KeyEvent event = new KeyEvent(remoteGUI.getTextField(), KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED);
                remoteGUI.getTextField().dispatchEvent(event);
            });
    }
}

class RemoteGUI extends JFrame  {

    JTextField textField;

    public RemoteGUI() {
        JPanel panel = new JPanel();
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        Font font = new Font("Verdana", Font.BOLD, 28);
        JLabel label = new JLabel("Enter A Number");
        label.setFont(font);
        panel.add(label);

        textField = new JTextField(10);
        textField.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String velocity = textField.getText();
                System.out.println("Velocity "   velocity   " entered into GUI");
            }
        });

        textField.setFont(font);
        panel.add(textField);

        JButton button = new JButton("Click Me");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                    //ActionEvent event = new ActionEvent(getTextField(), ActionEvent.ACTION_PERFORMED, "50");
                    KeyEvent event = new KeyEvent(getTextField(), KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED);
                    getTextField().dispatchEvent(event);
            }
        });
        panel.add(button);



        this.add(panel);
        pack();
        this.setVisible(true);
    }

    public JTextField getTextField(){
        return textField;
    }
}

However, the KeyEvent still doesn't work when the code is executed in the main() method. So there must be some kind of timing issue that the GUI components aren't visible. So maybe you can use a Timer to schedule the code to execute after a few milliseconds to give the GUI time to be displayed?

  • Related