0

So, I was wondering if Scanner could read from System.in that is set from JFrame. This is what I mean.

This is my WriteToSystemIn (JFrame class), which is the GUI part of the program.

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;

public class WriteToSystemIn extends JFrame {
    private static class ChangeNumber implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            ByteArrayInputStream s = null;
            try {
                s = new ByteArrayInputStream("1\n".getBytes("UTF-8"));
            } catch (UnsupportedEncodingException ex) {
                throw new RuntimeException(ex);
            }
            System.setIn(s);

        }
    }
    WriteToSystemIn() {
        JButton button = new JButton("try click it m8");
        button.addActionListener(new ChangeNumber());
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(button);
        this.setVisible(true);
        this.pack();
    }
}

And this is the Main function of the program.

import java.util.Scanner;

public class Main {
    private static class MainRunnable implements Runnable {

        @Override
        public void run() {
            new WriteToSystemIn();
        }
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new MainRunnable());

        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        System.out.println(s);

        System.out.println("ended");
    }
}

So, when the button is pressed from WriteToSystemIn, it should write "1\n" to System.in for Scanner to read.

But, it isn't doing that. It won't read anything. It has no problem printing to System.out so I thought it would've been a non-issue, but clearly I'm wrong. So, I'm wondering here, is there something that I'm doing wrong here? Or, am I trying to do something that is impossible?

Abra
  • 19,142
  • 7
  • 29
  • 41
Jun
  • 3
  • 1
  • Here's an example of redirecting `System.in` to a text field (and `System.out` to a text area): https://stackoverflow.com/questions/73977336/how-to-redirect-system-input-to-javafx-textfield/73981973#73981973. Note it's for JavaFX, not Swing, but the concept should essentially be the same. Also, the example sets it up so that `System.in` remains an "infinite stream" (i.e., you can keep sending data to it and it will keep piping that data, unlike the code in this question, which sets the input to a single finite array at a time). Doing this involved a few background threads. – Slaw Oct 13 '22 at 08:44

2 Answers2

2

When you call new Scanner(System.in); you give the scanner a reference to the stream that is currently set as System.in. When you later call System.setIn(s); you only change the value stored in System.in. The reference that was already given to the scanner is not changed and it still points to the original System.in.

You need to make sure that System.setIn(s); is called before you initialize the scanner. You can add debug printing to both statements to verify the order in which they are executed. This can be helpful when you are learning multithreaded programming.

System.out.println("Resetting System.in");
System.setIn(s);

...

System.out.println("Creating scanner");
Scanner scanner = new Scanner(System.in);
Torben
  • 3,805
  • 26
  • 31
2

Continuing on from @Torben's answer, you need to make class Main wait until the JButton (in class WriteToSystemIn) is clicked. Once the JButton is clicked, you can notify Main that it can stop waiting and continue executing.

Class Main

import java.util.Scanner;

public class Main {
    private static class MainRunnable implements Runnable {
        private Main main;

        public MainRunnable(Main main) {
            this.main = main;
        }

        @Override
        public void run() {
            new WriteToSystemIn(main);
        }
    }

    public static void main(String[] args) {
        Main main = new Main();
        javax.swing.SwingUtilities.invokeLater(new MainRunnable(main));
        synchronized (main) {
            try {
                main.wait();
                Scanner scanner = new Scanner(System.in);
                String s = scanner.nextLine();
                System.out.println(s);
            }
            catch (InterruptedException x) {
                x.printStackTrace();
            }
        }
        System.out.println("ended");
    }
}

Class WriteToSystemIn

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;

public class WriteToSystemIn extends JFrame {

    private static class ChangeNumber implements ActionListener {
        private Main main;

        public ChangeNumber(Main main) {
            this.main = main;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            ByteArrayInputStream s = null;
            try {
                s = new ByteArrayInputStream("1\n".getBytes("UTF-8"));
                System.setIn(s);
                synchronized (main) {
                    main.notifyAll();
                }
            } catch (UnsupportedEncodingException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    WriteToSystemIn(Main main) {
        JButton button = new JButton("try click it m8");
        button.addActionListener(new ChangeNumber(main));
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(button);
        this.setVisible(true);
        this.pack();
    }
}
Abra
  • 19,142
  • 7
  • 29
  • 41