Using JFrame with a Continuous Input Stream

2019-03-02 03:48发布

问题:

I'm trying to implement code which reads from my redboard's serial port and based on what it reads have it repaint a circle. The end goal of this is to use the Robot class to achieve actual cursor control, but I first want to learn more about Java along the way and so I'm trying to achieve it with some basic graphics first.

To summarize my issue, I do not know how to use JFrame with a continuously changing input from a static method.

The serial port accessing JAR can be found at http://fazecast.github.io/jSerialComm/

The Arduino continuously writes to the serial based on an FPGA-accelerometer system in the form "UpLeft", "Up", "UpRight", "Left", "Center", "Right", "DownLeft", "Down", "DownRight". The Java program should then grab this and repaint a circle accordingly.

I am able to open COMM3 and print the correct direction received from my hardware, but whenever I try to apply JFrame I get lost. I've found many ActionListener tutorials, but this implementation should be continuous and not dependent on mouse or keyboard events. Thus, I don't know how to use the paintComponent() and painter() methods since the main method is static.

Thank you very much for your time!

import java.awt.Color;
import java.awt.Graphics;
import java.util.Scanner;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import com.fazecast.jSerialComm.*;

public class Main extends JPanel{

    public void paintComponent(Graphics g, int x, int y) {
        super.paintComponent(g);
        g.setColor(Color.MAGENTA);
        g.fillOval(x, y, 20, 20);
    }
    public void painter(int x, int y, int velx, int vely){
        x = x + velx;
        y = y + vely;
        repaint();
    }

    public static void main(String[] args) {
        int x = 0, y = 0, velx = 0, vely = 0;
        SerialPort ports[] = SerialPort.getCommPorts();
        System.out.println("Select a Port:");
        SerialPort port = ports[1];
        Graphics g;

        if(port.openPort()){
            System.out.println("Successfully opened the port.");
        } else {
            System.out.println("Unable to open the port.");
        }
        port.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
        JFrame jf = new JFrame();
        Main main = new Main();

        jf.setTitle("Window");
        jf.setSize(600, 400);
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.add(main);

        Scanner data = new Scanner(port.getInputStream());
        while(data.hasNextLine()) {
            System.out.println(data.nextLine());
            try{String dir = data.nextLine();
                if(dir.equals("UpLeft")) {
                    velx = -1;
                    vely = -1;
                }
                if(dir.equals("Up")) {
                    velx = 0;
                    vely = -1;
                }
                if(dir.equals("UpRight")) {
                    velx = 1;
                    vely = -1;
                }
                if(dir.equals("Left")) {
                    velx = -1;
                    vely = 0;
                }
                if(dir.equals("Center")) {
                    velx = 0;
                    vely = 0;
                }
                if(dir.equals("Right")) {
                    velx = 1;
                    vely = 0;
                }
                if(dir.equals("DownLeft")) {
                    velx = -1;
                    vely = 1;
                }
                if(dir.equals("Down")) {
                    velx = 0;
                    vely = 1;
                }
                if(dir.equals("DownRight")) {
                    velx = 1;
                    vely = 1;
                }
                System.out.println(velx);
                System.out.println(vely);
            }           
            catch(Exception e) {};
        }
    }
}

回答1:

Suggestions:

  1. Get the reading of the InputStream out of the static main method and into its own class.
  2. This class should extend SwingWorker<Void, String>. You MUST read the Concurrency in Swing tutorial to understand fully how to use this class. It will help you do the long-running stream reading within a background thread and then safely pass the data obtained into the Swing GUI on the Swing event thread.
  3. All the reading from the stream should be done within this class's doInBackground() method. Get the String from the Scanner/Stream, and call the worker's publish(...) method passing the String into this method.
  4. Give your Swing GUI a public method, say public void getPortText(String text)
  5. Within the Worker use a protected void process(List<String> chunks) method override. Iterate through the chunks List<String> with a for loop, passing the data into your Swing GUI by calling the GUI's getPortText(...) method.
  6. In the GUI's getPortText method, change the state of a field of the GUI class, the velX and velY fields would work, and then call repaint()
  7. In the GUI's (JPanel's) paintComponent method override, use the velX and velY fields to change what is drawn.
  8. Of course the Worker will need a reference to the visualized GUI for this to work right (it all needs to be "wired" correctly).
  9. Your main method has a Graphics variable within it. Get rid of it as it's dangerous in that location. Avoid using a Graphics object outside of a paintComponent method or method called from within paintComponent. Avoid giving your class Graphics fields. The main exception to this is if you're using a Graphics object extracted from a BufferedImage.

Success