【问题标题】:Java update GUI with simulation running运行模拟的 Java 更新 GUI
【发布时间】:2019-03-03 14:10:10
【问题描述】:

我正在尝试对从一站到另一站的火车进行 Java 模拟。我的主要代码可以工作,但 GUI 有问题。我有一个带有开始和停止按钮的基本布局,但是一旦选择了开始按钮,主循环就会运行模拟并且 GUI 没有响应。我一直无法找到解决此问题的方法。非常感谢您的帮助!

这里是主要的模拟类:

/** * 这是运行主循环的主模拟类。 * 它使用两个类的实例:train 和 station。 * @作者奥利琼斯 * */ 公共类 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);
    }
}

这是 GUI,主要问题是(我认为)。

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

【问题讨论】:

标签: java user-interface bluej


【解决方案1】:

您正在事件调度线程上运行模拟。在您进行计算时,它们会独占处理 UI 事件的线程,因此它无法处理任何事情并且 UI 会冻结。

使用worker threads

看起来你有这个用例(来自链接的教程):

后台任务可以通过调用 SwingWorker.publish 来提供中间结果,从而从事件调度线程中调用 SwingWorker.process。

【讨论】:

    【解决方案2】:

    正如 cmets 和 this 答案中所解释的,在 The Event Dispatch Thread 上运行长进程会阻止它,因此它不会响应更改。
    一种替代方法是使用SwingWorker,它在其doInBackground() 方法中进行后台处理,发布中间值(publish 方法)并能够更新 gui(process 方法)。
    以下是基于您的代码的 SwingWorker 的基本实现。
    它可以复制粘贴到一个文件(GUI.java)中并运行。
    注意代码中的 cmets:

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

    【讨论】:

    • 感谢您的帮助!我试图这样做,但在 SimulationWorker 中,像 train 和 forwardTimeArray 这样的变量没有被识别。我是否必须在 SimEngine 类中为这些创建 getter 和 setter?
    • 我正在使用 blueJ 是否有影响?
    • 发布的代码运行没有问题。将整个代码复制粘贴到一个文件(GUI.java)中并运行。
    • 我正在使用 blueJ,它不允许我将所有代码放入一个文件中,您还有其他程序推荐吗?
    • 您不必将所有内容放在一个文件中。为了您的方便,我提供了这个。我对blueJ不熟悉,但我不明白为什么你不能创建一个GUI.java文件,添加包名,然后粘贴所有代码。
    猜你喜欢
    • 2021-08-20
    • 1970-01-01
    • 2023-04-11
    • 2017-08-19
    • 2017-03-09
    • 1970-01-01
    • 1970-01-01
    • 2015-11-17
    • 1970-01-01
    相关资源
    最近更新 更多