Графический интерфейс обновления Java с запущенной имитацией - PullRequest
0 голосов
/ 03 марта 2019

Я пытаюсь выполнить Java-симуляцию движения поезда от станции к станции.У меня основной код работает, но у меня проблемы с графическим интерфейсом.У меня есть базовый макет с кнопкой запуска и остановки, но как только кнопка запуска выбрана, основной цикл запускается для симуляции, а графический интерфейс не отвечает.У меня были проблемы с поиском, как обойти это.И помогите нам высоко ценить!

Вот основной класс моделирования:

/ ** * Вот основной класс моделирования, который запускает основной цикл.* Используются экземпляры двух классов: поезд и вокзал.* @author Ollie Jones * * / public class SimEngine {

/**
 * Station object array and train object initialised.
 * The line has 16 station so an array of 16 is needed.
 */

Station[] station = new Station[16];
Train train = new Train();
int forwardTimeArray[];
int backwardTimeArray[];


/**
 * Constructor for objects of class SimEngine
 */
public SimEngine()
{

    /**
     * Here that values are initialised.
     */
    train = new Train();
    forwardTimeArray = new int[]{1,4,2,4,6,3,3,5,3,2,5,5,1,4,2};
    backwardTimeArray = new int[]{3,2,4,5,2,2,4,3,4,2,5,2,1,4,5};
    // A for loop is used to initialse the station number
    for(int i=0; i<station.length; i++){
        station[i] = new Station();
        station[i].setStationNumber(i+1);
    }

    /**
     * Each station name is initialised separately as
     * they all have different names.
     */
    station[0].setStationName("Name of 1st station");
    station[1].setStationName("Name of 2nd station");
    station[2].setStationName("Name of 3rd station");
    station[3].setStationName("Name of 4th station");
    station[4].setStationName("Name of 5ht station");
    station[5].setStationName("Name of 6th station");
    station[6].setStationName("Name of 7th station");
    station[7].setStationName("Name of 8th station");
    station[8].setStationName("Name of 9th station");
    station[9].setStationName("Name of 10th station");
    station[10].setStationName("Name of 11th station");
    station[11].setStationName("Name of 12th station");
    station[12].setStationName("Name of 13th station");
    station[13].setStationName("Name of 14th station");
    station[14].setStationName("Name of 15th station");
    station[15].setStationName("Name of 16th station");

}

/**
 * An example of a method - replace this comment with your own
 *
 * @param  y  a sample parameter for a method
 * @return    the sum of x and y
 */

    /**
 * This method stats the train simulation.
 * 
 */
public void start()
{
    int x = 0;
    System.out.println("Station Number:1"); //Print the first staion number.
    while(x == 0){
        int stationNumber = 0;
        int time = 0;
        Boolean forwards;
        stationNumber = train.getStationNumber();
        forwards = train.getDirection();
        if (forwards == true){
            time = forwardTimeArray[stationNumber-1];
            sleep(time);
            stationNumber = stationNumber + 1;
            System.out.println("Station Nubmer:" + stationNumber);
            train.setStationNumber(stationNumber);
        }

        else{
            time = backwardTimeArray[stationNumber-2];
            sleep(time);
            stationNumber = stationNumber - 1;
            System.out.println("Station Number:" + stationNumber);
            train.setStationNumber(stationNumber);
        }
        if (stationNumber == 1){
            forwards = true;
        }
        else if (stationNumber == 16){
            forwards = false;
            //train.setStationNumber(stationNumber-1);

        }
        train.setDirection(forwards);
    }
}
public static void sleep(int time)
{
    try{
        time = time * 100;
        Thread.sleep(time);
    }
    catch(Exception e) {}
}

public void stop()
{
    System.exit(0);
}

}

Вот класс sim, с которого начинается симуляция.

public class Sim 
{
    private GUI gui;
    private SimEngine engine;

    /**
     * Constructor for objects of class sim
     * 
     */
    public Sim()
    {
        engine = new SimEngine();
        gui = new GUI(engine);
    }

    /**
     * Opens window if it has been closed.
     */

    public void show()
    {
        gui.setVisable(true);
    }
}

Вот графический интерфейс, гдеглавная проблема (я думаю).

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

public class GUI extends JFrame
{
    // instance variables - replace the example below with your own
    private JFrame frame;
    private JTextField display;
    private final SimEngine sim;
    private JLabel infoLabel;

    /**
     * Constructor for objects of class GUI
     */
    public GUI(SimEngine engine)
    {
        // initialise instance variables
        makeFrame();
        frame.setVisible(true);
        sim = engine;
    }

    /**
     * Creates GUI frame!
     */
    public void makeFrame()
    {
        frame = new JFrame("Train Simulation");

        JPanel contentPane = (JPanel)frame.getContentPane();
        contentPane.setLayout(new BorderLayout(8,8));
        contentPane.setBorder(new EmptyBorder(10,10,10,10));

        display = new JTextField();
        contentPane.add(display, BorderLayout.NORTH);

        JPanel buttonPanel = new JPanel(new GridLayout(1,2));
        addButton(buttonPanel, "Start", () -> sim.start());
        addButton(buttonPanel, "Stop", () -> sim.stop());

        contentPane.add(buttonPanel, BorderLayout.CENTER);
        frame.pack();
    }

    private void addButton(Container panel, String buttonText, ButtonAction
            action)
    {
        JButton button = new JButton(buttonText);
        button.addActionListener(e -> {action.act(); redisplay(); });
        panel.add(button);
    }

    private interface ButtonAction
    {
        /**
         * act on button press.
         */
        void act();
    }

    private void redisplay()
    {
    }

    /**
     * Makes frame visable.
     */
    public void setVisable(boolean visable){
        frame.setVisible(visable);
    }
}

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Как объяснено в комментариях и в этом ответе, выполнение длинных процессов в Потоке диспетчеризации событий блокирует его, поэтому он не реагирует на изменения.Одной из альтернатив является использование SwingWorker , который выполняет фоновую обработку в своем методе doInBackground(), публикует промежуточные значения (метод publish) и способен обновлять графический интерфейс (метод process).Ниже приведена базовая реализация SwingWorker, основанная на вашем коде.Его можно скопировать в один файл (GUI.java) и запустить.Обратите внимание на комментарии в коде:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;

public class GUI extends JFrame
{
    // instance variables - replace the example below with your own
    private JFrame frame;
    private JTextField display;
    private final SimEngine sim;
    private JLabel infoLabel;

    /**
     * Constructor for objects of class GUI
     */
    public GUI(SimEngine engine)
    {
        // initialize instance variables
        makeFrame();
        frame.setVisible(true);
        sim = engine;
    }

    /**
     * Creates GUI frame!
     */
    public void makeFrame()
    {
        frame = new JFrame("Train Simulation");

        JPanel contentPane = (JPanel)frame.getContentPane();
        contentPane.setLayout(new BorderLayout(8,8));
        contentPane.setBorder(new EmptyBorder(10,10,10,10));

        display = new JTextField();
        contentPane.add(display, BorderLayout.NORTH);

        JPanel buttonPanel = new JPanel(new GridLayout(1,2));
        addButton(buttonPanel, "Start", () -> sim.start());
        addButton(buttonPanel, "Stop", () -> sim.stop());

        contentPane.add(buttonPanel, BorderLayout.CENTER);
        frame.pack();
    }

    private void addButton(Container panel, String buttonText, ButtonAction  action)
    {
        JButton button = new JButton(buttonText);
        button.addActionListener(e -> {action.act(); redisplay(); });
        panel.add(button);
    }

    void updateDisplay(String newValue){
        display.setText(newValue);
    }

    private interface ButtonAction
    {
        /**
         * act on button press.
         */
        void act();
    }

    private void redisplay() {  }

    /**
     * Makes frame visible.
     */
    public void setVisable(boolean visable){
        frame.setVisible(visable);
    }

    public static void main(String[] args) {
        Sim sim = new Sim();
        sim.show();
    }
}

//implements Listener so it can listen to SimEngine value changes 
class Sim  implements Listener
{
    private final GUI gui;
    private final SimEngine engine;


    public Sim()
    {
        engine = new SimEngine(this);
        gui = new GUI(engine);
    }

    /**
     * make gui visible 
     */
    public void show()
    {
        gui.setVisable(true);
    }

    @Override
    public void valueChanged(String newValue){
        gui.updateDisplay(newValue);
    }
}

class SimEngine {

    /**
     * Station object array and train object initialized.
     * The line has 16 station so an array of 16 is needed.
     */
    private final Station[] station = new Station[16];
    private Train train = new Train();
    private final int forwardTimeArray[],  backwardTimeArray[];
    private final Listener listener;

    //accept a listener 
    public SimEngine(Listener listener)
    {
        this.listener = listener;
        train = new Train();
        forwardTimeArray = new int[] {1,4,2,4,6,3,3,5,3,2,5,5,1,4,2,3}; //needs 16 values, had only 16
        backwardTimeArray = new int[]{3,2,4,5,2,2,4,3,4,2,5,2,1,4,5,4};
        // A for loop is used to initialize the station number and name
        for(int i=0; i<station.length; i++){
            station[i] = new Station();
            station[i].setStationNumber(i+1);
            station[0].setStationName("Station #"+(i+1));
        }
    }

    /**
     * This method starts the train simulation.
     *
     */
    public void start()
    {
        //have all background processing done by a SwingWorker so GUI does not freeze
        new SimulationWorker().execute();
    }
    public static void sleep(int time)
    {
        try{
            Thread.sleep(time * 300);
        }
        catch(Exception e) {}
    }

    public void stop()
    {
        System.exit(0);
    }

    class SimulationWorker extends SwingWorker<Void,Integer>{

        boolean stop = false; //use if you wish to stop 

        //do background processing 
        @Override
        protected Void doInBackground() throws Exception {
            while(! stop){
                int stationNumber = 0;
                int time = 0;
                boolean forwards;
                stationNumber = train.getStationNumber();

                forwards = train.getDirection();
                if (stationNumber == 1){
                    forwards = true;
                    train.setDirection(forwards);
                }
                else if (stationNumber == 15){
                    forwards = false;
                    train.setDirection(forwards);
                }

                if (forwards == true){
                    time = forwardTimeArray[stationNumber+1];//time = forwardTimeArray[stationNumber-1];
                    sleep(time);
                    stationNumber = stationNumber + 1;
                    //System.out.println("Station Number:" + stationNumber);
                    train.setStationNumber(stationNumber);
                }
                else{
                    time = backwardTimeArray[stationNumber-2];
                    sleep(time);
                    stationNumber = stationNumber - 1;
                    //System.out.println("Station Number:" + stationNumber);
                    train.setStationNumber(stationNumber);
                }
                publish(train.getStationNumber()); //publish result (station number)
            }
            return null;
        }

        //process published information 
        @Override
        protected void process(List<Integer> stationsList) {
            for(int stationNumber : stationsList){
                listener.valueChanged("Train is at "+ stationNumber); //notify listener 
            }
        }
    }
}

class Train {

    private  int stationNumber = 0;
    private  boolean forwards = true ;

    public int getStationNumber() {
        return stationNumber;
    }

    public void setDirection(boolean forwards) {
        this.forwards = forwards;
    }

    public void setStationNumber(int stationNumber) {
        this.stationNumber = stationNumber;
    }

    public boolean getDirection() {
        return forwards;
    }
}

class Station {

    private int stationNumber;
    private String stationName;

    public void setStationNumber(int stationNumber) {
        this.stationNumber = stationNumber;
    }

    public void setStationName(String stationName) {
        this.stationName = stationName;
    }
}

//an interface use by Sim to listen to SimEngine changes 
interface Listener {
    void valueChanged(String newValue);
}
0 голосов
/ 03 марта 2019

Вы запускаете симуляцию в потоке отправки событий.Пока ваши вычисления происходят, они монополизируют поток, который обрабатывает события пользовательского интерфейса, поэтому он ничего не может обработать, и пользовательский интерфейс зависает.

Использование рабочих потоков .

Похоже, у вас есть этот сценарий использования (из связанного учебника):

Фоновая задача может дать промежуточные результаты, вызвав SwingWorker.publish, в результате чего SwingWorker.process будет вызван из потока диспетчеризации событий.

...