Home > Enterprise >  Writing to System.in from swing ui and reading the input with Scanner
Writing to System.in from swing ui and reading the input with Scanner

Time:10-12

So, I was wondering if Scanner could read from System.in 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 the 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?

CodePudding user response:

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);

CodePudding user response:

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();
    }
}
  • Related