0

I created a graphic interface in java and 2 buttons.

My aim :

1) When I click on the first button, having a loop in which different tasks are processed (Button "Start"). Between each loop there is a stop of 10 seconds

2) When I click on the second button, the loop is processed immediately one last time but then stopped. (I also would like to make a pop up showing that it has been stopped but that's not the main question, I think I can do it.)

I tried the following code, but first I think they are more simple ways to sort my problem. Plus I can compile but it doesn't work, the loop is not stopped, the window crashes:

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {

    globalStop="Run";

    while (globalStop.equals("Run")) {

        System.out.println("GO");
        // Other stuff

        // For the break ? 
        try {
            Thread.sleep(10000);
          } catch (InterruptedException ex) {
               Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
         }
    }
        System.out.println("done");
    }

}                                        

private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
    globalStop = "Stop";
    System.out.println("Bouton2");
}      

I hope I was clear enough, if that is not the case, please let me know and I will rephrase. Thank you all in advance for your help.

Jan Gerlinger
  • 7,361
  • 1
  • 44
  • 52
user1614914
  • 77
  • 2
  • 7
  • You should use a boolean, not a string. And it should be volatile. – SLaks Aug 21 '12 at 17:36
  • You need to use a separate thread. – SLaks Aug 21 '12 at 17:37
  • @ SLaks Sorry I don't see how to make it, I am just a beginner.What do you mean with "volatile" please ? – user1614914 Aug 21 '12 at 17:37
  • 1
    If you are just a beginner, you shouldn't be writing these kinds of programs. You need to know about multi-threading and/or interrupts to make a program like this. – Matt Westlake Aug 21 '12 at 17:40
  • @ Matt Westlake : thank you for your support.. I am just trying some new stuff by curiosity, and I guess I am able to understand if it's explained clearly enough. I thought they would have been easy ways to manage my aims but if you say that it's too complicated for me ... – user1614914 Aug 21 '12 at 17:49
  • Not saying it's too complicated for you, and learning to multi-thread is something that will open up thousands of possibilities for you to develop different kinds of code, Just saying we cannot explain everything that needs to be explained in 500 characters. Also asking someone to explain multi-threading is like explaining how to drive a car. There are similarities but the switches and buttons may not be in the same place every time. – Matt Westlake Aug 21 '12 at 17:53

4 Answers4

2

You shouldn't be looping within the UI thread, nor telling it to sleep. Fundamentally you should keep the UI thread as free as possible.

If you need something to occur on a regular basis in a Swing UI in the UI thread, use a Swing Timer.

It's unclear what you're doing in the "other stuff" however - it's possible that you should be doing that in a different thread entirely and using (say) an AtomicBoolean to indicate when you want to stop.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Could you show me how you would do that please ? I am sorry I am just a beginner. By "other stuff" I meant other commands, writing in files and such. – user1614914 Aug 21 '12 at 17:41
  • start here http://www.tutorialspoint.com/java/java_multithreading.htm that will at least give you some idea about threading – Matt Westlake Aug 21 '12 at 17:42
  • I see but threading with graphical interface seems to be really complicated right ? Could you show me a concrete example please linked with my aims ? Thank you for your help. – user1614914 Aug 21 '12 at 17:46
  • @user1614914: "writing in files and such" doesn't really give us enough information to know whether that should really happen within another thread. It probably should. And yes, threading is complicated - it sounds like you should probably read basic threading tutorials and the ["Threads and Swing"](http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html) tutorial. – Jon Skeet Aug 21 '12 at 17:47
  • 1
    We cannot just show you how to thread your program. Multi-threading is a somewhat complex funtion that requires multiple classes and function calls to get/start threads. It is nothing we can "teach" you on a forum – Matt Westlake Aug 21 '12 at 17:49
  • So there is no simple solutions for this ? Making graphical interface are barely simple, and making interact different action performed by click on button is that hard ? I am sorry to read that.. – user1614914 Aug 21 '12 at 17:51
  • @user1614914: Yes, threading is hard (doing it properly). You should *expect* that writing reasonably complicated programs will take a lot of effort to learn the various aspects involved. – Jon Skeet Aug 21 '12 at 17:55
  • Essentially because your processor is always busy in the loop, it never has time to check and see if the button has been pressed. That is the issue and the solution is not easy if you don't have multithreading background. – Matt Westlake Aug 21 '12 at 17:55
  • @user1614914 Basically, your application should separate UI from PROCESS/LOGIC, including threads. Your processing thread can be put to wait an UI action if needed, your UI thread can be paused while processing, or your UI can be made to temporarily pause your ongoing processing. --- @Jon Good tip on `Atomic variables` (+1). – CosmicGiant Aug 21 '12 at 17:57
2

I wondered how long it would take me to create a United States type traffic signal GUI. It took 75 minutes. I was able to create the GUI quickly because a lot of Swing is boilerplate. Once you create one GUI, you can copy some of the classes for your next GUI.

Here's an image of the traffic signal GUI.

Traffic signal GUI

When you press the Start button, the traffic signal will cycle from green to yellow to red. The traffic signal will cycle forever, until you press the Stop button.

When you press the Stop button, the traffic signal will turn red. It will stay red forever, until you press the Start button.

When you press the Start button while the traffic signal is cycling, the green to yellow to red cycle starts over.

Basically, the following steps show you how to create any Swing GUI. I didn't create the code in this order, but it makes sense to explain the code in a logical order. So, let's dig into the code.

This is the model class for the GUI. Every GUI needs to have it's own model, separate from the model of the application. For this GUI, the model is simple.

package com.ggl.traffic.signal.model;

import java.awt.Dimension;

public class TrafficSignalModel {

    public static final int RED_LIGHT_TIME = 15;
    public static final int YELLOW_LIGHT_TIME = 5;
    public static final int GREEN_LIGHT_TIME = 10;

    public static final Dimension LIGHT_SIZE = new Dimension(32, 32);
}

We set the signal light times in the model, as well as the size of the traffic lights.

For a more complicated GUI, we would keep track of the field values in the model.

Next, we have the main class of the traffic signal GUI.

package com.ggl.traffic.signal;

import javax.swing.SwingUtilities;

import com.ggl.traffic.signal.view.TrafficSignalFrame;

public class TrafficSignal implements Runnable {

    @Override
    public void run() {
        new TrafficSignalFrame();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new TrafficSignal());
    }

}

This class ensures that the traffic signal GUI is on the Swing event thread. That's all this class does. You can see how you can copy this class to start any GUI.

Next, we have the Frame class of the GUI.

package com.ggl.traffic.signal.view;

import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

public class TrafficSignalFrame {

    protected ButtonPanel bPanel;

    protected JFrame frame;

    protected TrafficSignalPanel tsPanel;

    public TrafficSignalFrame() {
        createPartControl();
    }

    protected void createPartControl() {
        tsPanel = new TrafficSignalPanel();
        bPanel = new ButtonPanel();

        bPanel.setTrafficSignalPanel(tsPanel);

        frame = new JFrame();
        frame.setTitle("Traffic Signal");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                exitProcedure();
            }
        });


        frame.setLayout(new FlowLayout());
        frame.add(bPanel.getPanel());
        frame.add(tsPanel.getPanel());
        frame.pack();
//      frame.setBounds(100, 100, 400, 200);
        frame.setVisible(true);
    }

    public void exitProcedure() {
        frame.dispose();
        System.exit(0);
    }

    public JFrame getFrame() {
        return frame;
    }

}

This class is boilerplate, except for the particular JPanels that will make up the GUI. If your JFrame has a JMenu, this would be the place to attach your JMenu to your JFrame.

Notice that I did not extend JFrame to make this class. The only time you extend a Swing component is when you're overriding one or more of the component's methods. If I need the actual JFrame, I call the getFrame() method. Using Swing components rather than extending Swing components keeps my methods separate from the Swing methods.

Next, we'll look at the traffic signal light panel. This panel makes up one of the 3 lights in the traffic signal.

package com.ggl.traffic.signal.view;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JPanel;

public class TrafficSignalLightPanel extends JPanel {

    private static final long serialVersionUID = 1L;

    protected boolean lightOn;

    protected Color lightColor;
    protected Color darkColor;

    public TrafficSignalLightPanel(Color lightColor) {
        this.lightColor = lightColor;
        this.darkColor = Color.WHITE;
        this.lightOn = false;
    }

    public void setLightOn(boolean lightOn) {
        this.lightOn = lightOn;
        this.repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        if (lightOn) {
            g.setColor(lightColor);
        } else {
            g.setColor(darkColor);
        }
        g.fillRect(0, 0, getWidth(), getHeight());
    }

}

This class extends JPanel, because we want to override the paintComponent method. This is a simple class. All it does is paint the panel a color, or white.

Next, we'll look at the traffic signal panel. This panel creates 3 light panels and arranges them in a vertical row.

package com.ggl.traffic.signal.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.Border;

import com.ggl.traffic.signal.model.TrafficSignalModel;

public class TrafficSignalPanel {

    protected JPanel panel;

    protected TrafficSignalLightPanel redLight;
    protected TrafficSignalLightPanel yellowLight;
    protected TrafficSignalLightPanel greenLight;

    public TrafficSignalPanel() {
        createPartControl();
    }

    protected void createPartControl() {
        Border border = BorderFactory.createLineBorder(Color.BLACK, 4);

        redLight = new TrafficSignalLightPanel(Color.RED);
        redLight.setBorder(border);
        redLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);

        yellowLight = new TrafficSignalLightPanel(Color.YELLOW);
        yellowLight.setBorder(border);
        yellowLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);

        greenLight = new TrafficSignalLightPanel(Color.GREEN);
        greenLight.setBorder(border);
        greenLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);

        panel = new JPanel();
        panel.setLayout(new FlowLayout());
        panel.setPreferredSize(
                new Dimension(TrafficSignalModel.LIGHT_SIZE.width + 10, 
                        TrafficSignalModel.LIGHT_SIZE.height * 3 + 25));

        panel.add(redLight);
        panel.add(yellowLight);
        panel.add(greenLight);
    }

    public JPanel getPanel() {
        return panel;
    }

    public TrafficSignalLightPanel getRedLight() {
        return redLight;
    }

    public TrafficSignalLightPanel getYellowLight() {
        return yellowLight;
    }

    public TrafficSignalLightPanel getGreenLight() {
        return greenLight;
    }

}

A fairly straightforward creation of a JPanel from 3 JPanels. I set the preferred size of the JPanel so the lights will be in a vertical row.

Next, we'll look at the button panel. You can pretty much copy this code into any GUI that has a button panel.

package com.ggl.traffic.signal.view;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;

import com.ggl.traffic.signal.thread.TrafficSignalCycle;

public class ButtonPanel {

    protected JButton startButton;
    protected JButton stopButton;

    protected JPanel panel;

    protected TrafficSignalCycle thread;

    protected TrafficSignalPanel tsPanel;

    public ButtonPanel() {
        this.thread = null;
        createPartControl();
    }

    protected void createPartControl() {
        panel = new JPanel();
        panel.setLayout(new FlowLayout());

        startButton = new JButton("Start");
        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                if (thread != null) {
                    thread.stopRunning();
                }
                tsPanel.getRedLight().setLightOn(false);
                tsPanel.getYellowLight().setLightOn(false);
                tsPanel.getGreenLight().setLightOn(false);
                thread = new TrafficSignalCycle(tsPanel);
                thread.start();
            }
        });

        panel.add(startButton);

        stopButton = new JButton("Stop");
        stopButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                if (thread != null) {
                    thread.stopRunning();
                    thread = null;
                }
                tsPanel.getRedLight().setLightOn(true);
                tsPanel.getYellowLight().setLightOn(false);
                tsPanel.getGreenLight().setLightOn(false);
            }
        });

        panel.add(stopButton);

        setButtonSizes(startButton, stopButton);
    }

    protected void setButtonSizes(JButton ... buttons) {
        Dimension preferredSize = new Dimension();
        for (JButton button : buttons) {
            Dimension d = button.getPreferredSize();
            preferredSize = setLarger(preferredSize, d);
        }
        for (JButton button : buttons) {
            button.setPreferredSize(preferredSize);
        }
    }

    protected Dimension setLarger(Dimension a, Dimension b) {
        Dimension d = new Dimension();
        d.height = Math.max(a.height, b.height);
        d.width = Math.max(a.width, b.width);
        return d;
    }

    public void setTrafficSignalPanel(TrafficSignalPanel tsPanel) {
        this.tsPanel = tsPanel;
    }

    public JPanel getPanel() {
        return panel;
    }

}

The button actions were simple enough that I could keep them in the button panel. If you want, you can code separate action classes.

Finally, here's the code that runs the traffic light cycle. It's an extension of the Thread class, so it can be run in a separate thread from the GUI. It's always a good idea to do work in threads separate from the GUI thread.

package com.ggl.traffic.signal.thread;

import javax.swing.SwingUtilities;

import com.ggl.traffic.signal.model.TrafficSignalModel;
import com.ggl.traffic.signal.view.TrafficSignalLightPanel;
import com.ggl.traffic.signal.view.TrafficSignalPanel;

public class TrafficSignalCycle extends Thread {

    protected boolean isRunning;
    protected boolean isFinished;

    protected TrafficSignalPanel tsPanel;

    public TrafficSignalCycle(TrafficSignalPanel tsPanel) {
        this.tsPanel = tsPanel;
        this.isRunning = true;
        this.isFinished = false;
    }

    @Override
    public void run() {
        while (isRunning) {
            signalLightOn(tsPanel.getGreenLight(), TrafficSignalModel.GREEN_LIGHT_TIME);
            signalLightOn(tsPanel.getYellowLight(), TrafficSignalModel.YELLOW_LIGHT_TIME);
            signalLightOn(tsPanel.getRedLight(), TrafficSignalModel.RED_LIGHT_TIME);
        }
        this.isFinished = true;
    }

    protected void signalLightOn(TrafficSignalLightPanel light, int seconds) {
        if (isRunning) {
            setLightOn(light, true);
        }

        for (int i = 0; i < 1000 && isRunning; i++) {
            try {
                Thread.sleep(1L * seconds);
            } catch (InterruptedException e) {
            }
        }
        setLightOn(light, false);
    }

    protected void setLightOn(final TrafficSignalLightPanel light,
            final boolean isLightOn) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                light.setLightOn(isLightOn);

            }       
        });
    }

    public void stopRunning() {
        this.isRunning = false;
        while (!isFinished) {
            try {
                Thread.sleep(10L);
            } catch (InterruptedException e) {
            }
        }
    }

}

The method that actually changes the color of the signal light must execute in the Swing event thread. That's what the setLightOn method does by calling SwingUtilities.

The timing loop is a bit complicated because we want to be able to stop the thread in a few milliseconds. The isFinished boolean ensures that the thread is stopped completely, so that the lights can be set.

This is a fairly long answer, but I hope it's helpful to anyone creating a Swing GUI.

Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
1

1. You should always keep the UI thread for UI work and Non-UI thread for Non-UI work.

2. In Java GUI, the main() is not Long lived, after assigning the construction of GUI to the Event Dispatcher Thread, the main() quits, and now its EDT's responsibility handle the GUI.

3. So when you click the buttons, and the work you are doing is doing some heavy process or its time consuming....then span a Separate thread.

4. You can use Thread or SwingWorker.

Example:

Button b = new Button("Click me");

b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {

                              Thread t = new Thread(new Runnable(){


                                     public void run(){


                                           // Do the Heavy Processing work.....

                                       }

                                });

                t.start();
            }
        });
Kumar Vivek Mitra
  • 33,294
  • 6
  • 48
  • 75
  • @Gilbert Le...thanks... its that silly mistake i keep making...... Thanks once again for pointing it out – Kumar Vivek Mitra Aug 21 '12 at 17:57
  • Really interesting, so If I understand well, when I will be clicking the button "Click me" this will run the "heavy processing work". Now if I want to stop this run while click on another button (ie "STOP" ), how should I do with your code please ? Thank you for your help all :) – user1614914 Aug 21 '12 at 18:02
  • When you press the button then t.start() will be called and then the new thread will spun... and as soon as the run() is completed..the thread moves in dead state here. Now if you do your heavy code inside a while() loop in the run() method, with a boolean as the controlling mechanism... then you can change the boolean from true to false in your stop button.... simple.... – Kumar Vivek Mitra Aug 22 '12 at 13:31
0

The easy but dirty way:

Multi-thread your program and have one thread do your loop and a second thread monitor your buttons. Have the button change your globalStop variable

The not so easy but cleaner way:

Make the button throw an interrupt to change the value. After the interrupt the for loop will continue to the end.

Matt Westlake
  • 3,499
  • 7
  • 39
  • 80
  • "Make the button throw an interrupt to change the value. After the interrupt the for loop will continue to the end." How can I make it please ? The button trow an interrupt ? – user1614914 Aug 21 '12 at 17:43
  • Scrap the interrupt idea. It is more complicated then multi-threading and since you're a beginner I won't go into OS level commands. – Matt Westlake Aug 21 '12 at 17:50
  • Actually I must say that I am making this program compatible for Windows but I know well more commands in linux so yep it's probably not the best idea. What do you think about the ideas given above then ? – user1614914 Aug 21 '12 at 18:04