0

I'm trying to create a simulator that will print what color a traffic light is currently at into a JTextField when the "start" button is clicked. However, clicking the button doesn't do anything. I have System.out lines that work with the same operation, but the text field won't change. I even tried putting in a custom String just to see if it'll change on the button click, but it doesn't, and I'm not really sure why it's doing this.

This is the action listener for the button

start.addActionListener((ActionEvent e) -> {
            //String x = "hello";
            //firstLight.setText(x);
            TrafficLightSimulator tl =
                new TrafficLightSimulator(TrafficLightColor.GREEN, "Thread 1");

            TrafficLightSimulator t2 =
                new TrafficLightSimulator(TrafficLightColor.RED, "Thread 2"); 

            TrafficLightSimulator t3 =
                new TrafficLightSimulator(TrafficLightColor.YELLOW, "Thread 3"); 

            Thread thrd = new Thread(tl);
            thrd.setName("Thread 1");

            Thread thrd2 = new Thread(t2);
            thrd.setName("Thread 2");

            Thread thrd3 = new Thread(t3);
            thrd.setName("Thread 3");

            thrd.start();
            thrd2.start();
            thrd3.start();

            firstLight.setText("hello");

            for(int i=0; i < 9; i++) { 
              firstLight.setText("hello");
              secondLight.setText(tl.getColor().toString());
              System.out.println(tl.name + " is at " + tl.getColor()); 
              System.out.println(t2.name + " is at " + t2.getColor());
              System.out.println(t3.name + " is at " + t3.getColor());
              tl.waitForChange(); 
              t2.waitForChange(); 
              t3.waitForChange(); 
            } 

            tl.cancel();
            t2.cancel();
            t3.cancel();
        });

And this is the whole code

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package intersectionsimulator;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class IntersectionSimulator extends JFrame {

    private JFrame frame;
    private JPanel buttonPanel;
    private JPanel outputPanel;
    private JButton pause;
    private JButton start;
    private JButton stop;
    private JLabel light1;
    private JLabel light2;
    private JLabel light3;
    private JLabel car1;
    private JLabel car2;
    private JLabel car3;
    private JTextField firstLight;
    private JTextField secondLight;
    private JTextField thirdLight;
    private JTextField firstCar;
    private JTextField secondCar;
    private JTextField thirdCar;

    IntersectionSimulator() {
        frame = new JFrame("Intersection Simulator");
        frame.setSize(750,500);
        frame.setLayout(new GridLayout(1,2));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        buttonPanel = new JPanel();
        outputPanel = new JPanel();
        pause = new JButton("Pause");
        start = new JButton("Start");
        stop = new JButton("Stop");
        light1 = new JLabel("First Traffic Light");
        light2 = new JLabel("Second Traffic Light");
        light3 = new JLabel("Third Traffic Light");
        car1 = new JLabel("First Car");
        car2 = new JLabel("Second Car");
        car3 = new JLabel("Third Car");
        firstLight = new JTextField(50);
        secondLight = new JTextField(50);
        thirdLight = new JTextField(50);
        firstCar = new JTextField(50);
        secondCar = new JTextField(50);
        thirdCar = new JTextField(50);

        buttonPanel.setLayout(new GridLayout(3,1));
        buttonPanel.add(start);
        buttonPanel.add(pause);
        buttonPanel.add(stop);
        frame.add(buttonPanel);

        outputPanel.setLayout(new GridLayout(6,2));
        outputPanel.add(light1);
        outputPanel.add(firstLight);
        outputPanel.add(light2);
        outputPanel.add(secondLight);
        outputPanel.add(light3);
        outputPanel.add(thirdLight);
        outputPanel.add(car1);
        outputPanel.add(firstCar);
        outputPanel.add(car2);
        outputPanel.add(secondCar);
        outputPanel.add(car3);
        outputPanel.add(thirdCar);
        frame.add(outputPanel);
        frame.setVisible(true);

        start.addActionListener((ActionEvent e) -> {
            //String x = "hello";
            //firstLight.setText(x);
            TrafficLightSimulator tl =
                new TrafficLightSimulator(TrafficLightColor.GREEN, "Thread 1");

            TrafficLightSimulator t2 =
                new TrafficLightSimulator(TrafficLightColor.RED, "Thread 2"); 

            TrafficLightSimulator t3 =
                new TrafficLightSimulator(TrafficLightColor.YELLOW, "Thread 3"); 

            Thread thrd = new Thread(tl);
            thrd.setName("Thread 1");

            Thread thrd2 = new Thread(t2);
            thrd.setName("Thread 2");

            Thread thrd3 = new Thread(t3);
            thrd.setName("Thread 3");

            thrd.start();
            thrd2.start();
            thrd3.start();

            firstLight.setText("hello");

            for(int i=0; i < 9; i++) { 
              firstLight.setText("hello");
              secondLight.setText(tl.getColor().toString());
              System.out.println(tl.name + " is at " + tl.getColor()); 
              System.out.println(t2.name + " is at " + t2.getColor());
              System.out.println(t3.name + " is at " + t3.getColor());
              tl.waitForChange(); 
              t2.waitForChange(); 
              t3.waitForChange(); 
            } 

            tl.cancel();
            t2.cancel();
            t3.cancel();
        });
    }

    enum TrafficLightColor {
        RED, GREEN, YELLOW
    }

    class TrafficLightSimulator implements Runnable {
        private TrafficLightColor tlc; // holds the current traffic light color 
        private boolean stop = false; // set to true to stop the simulation 
        private boolean changed = false; // true when the light has changed
        String name;

        TrafficLightSimulator(TrafficLightColor init, String n) {  
          tlc = init; 
          name = n;
        } 

        TrafficLightSimulator() {  
          tlc = TrafficLightColor.RED; 
        } 

        // Start up the light. 
        @Override
        public void run() { 
          while(!stop) { 
            try { 
              switch(tlc) { 
                case GREEN: 
                  Thread.sleep(10000); // green for 10 seconds 
                  break; 
                case YELLOW: 
                  Thread.sleep(2000);  // yellow for 2 seconds 
                  break; 
                case RED: 
                  Thread.sleep(12000); // red for 12 seconds 
                  break; 
              } 
            } catch(InterruptedException exc) { 
              System.out.println(exc); 
            } 
            changeColor(); 
          }  
        } 

        // Change color. 
        synchronized void changeColor() { 
          switch(tlc) { 
            case RED: 
              tlc = TrafficLightColor.GREEN; 
              break; 
            case YELLOW: 
              tlc = TrafficLightColor.RED; 
              break; 
            case GREEN: 
             tlc = TrafficLightColor.YELLOW; 
          } 

          changed = true;
          notify(); // signal that the light has changed 
        } 

        // Wait until a light change occurs. 
        synchronized void waitForChange() { 
          try { 
            while(!changed) 
              wait(); // wait for light to change 
            changed = false;
          } catch(InterruptedException exc) { 
            System.out.println(exc); 
          } 
        } 

        // Return current color. 
        synchronized TrafficLightColor getColor() { 
          return tlc; 
        } 

        // Stop the traffic light. 
        synchronized void cancel() { 
          stop = true; 
        } 

    }

    public static void main(String[] args) {
        IntersectionSimulator is = new IntersectionSimulator();
    }

}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
nbaq
  • 45
  • 6
  • You cannot sleep in an ActionListener, as ActionListeners are called on the AWT event dispatch thread, which is what processes all Swing redraw events and user input events. A sleep will prevent redraws and user input from being processed. Move your `for` loop to a different thread, and make sure your `setText` calls are wrapped in a call to [EventQueue.invokeLater](https://docs.oracle.com/en/java/javase/13/docs/api/java.desktop/java/awt/EventQueue.html#invokeLater%28java.lang.Runnable%29), since those calls may only be called in the AWT thread. – VGR Mar 09 '20 at 00:09
  • @VGR That makes sense! Thank you. Is there any other way I can make the threads run using the start button then? The threads will have to sleep either way, so I'm not sure how to run the simulation. Or is there anyway to simply run the simulation without using the button at all (such as running as soon as the JFrame object is created in the main method?) – nbaq Mar 09 '20 at 00:15
  • 3
    Just take every single line code in your ActionListener, move them into the `run` method of a Runnable, then in your ActionListener, pass that Runnable to a `new Thread` constructor and start that new Thread . But remember, EventQueue.invokeLater is required when making Swing calls like `setText`—without it, you will get unpredictable results. – VGR Mar 09 '20 at 00:20
  • All you're doing is pausing, and in this situation using a Thread directly is gross over-kill and potentially dangerous. Much better to use a [Swing Timer](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) instead, and let the Swing library do the sleeping for you in a thread-safe manner. – Hovercraft Full Of Eels Mar 09 '20 at 00:24
  • @VGR I'll try doing that. One question - when moving them into the run method of a Runnable, should I create a new class for that, or simply create it in the same class? I'm not sure how to pass the Runnable unless I have a class and pass it as a parameter. Sorry if these are primitive questions. I'm new to multithreading. – nbaq Mar 09 '20 at 00:32
  • @HovercraftFullOfEels Thank you for the advice. I didn't know that. I'm new to multithreading so I haven't figured out that much yet, but I'm gonna look into this and try to apply it as I work on this project. – nbaq Mar 09 '20 at 00:34
  • 1
    @VGR I created a new class that implements runnable and passed to a thread as you recommended, and it worked! Thank you very much. – nbaq Mar 09 '20 at 00:50

0 Answers0