0

I've been struggling with a weird issue that I don't really understand for a while now (I have very little knowledge about threading):

I have a JFrame class that is supposed to use JButtons to represent customers in a queue. There's a method called simulate() which, essentially, adds a new JButton to the JFrame every time it's called. I want to call the method repeatedly with brief pauses after each call. To this end, I've tried the following:

   private void goButtonActionPerformed(java.awt.event.ActionEvent evt) {                                         
    try {
        for(int i = 0; i < sim.length ; i++){
        simulate();
        Thread.sleep(500);
        }
    } catch (Exception e) {}
}

but the program just waits until the loop is finished to update the JFrame (i.e. five customers suddenly appear after a few seconds, instead of one arriving every half-second). What's the problem? Am I causing the wrong thread to sleep? Could I be using wait()?

The full program is really long, but I'll include it here if that helps:

package homeworkfour;

import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JTextField;

public class GUI extends javax.swing.JFrame {

final private int w = 60, h = 20;
ArrayList<Integer> busy = new ArrayList<>();
ArrayList<Integer> linePositions;
ArrayList<Integer> kioskPositions;
public Simulator sim = new Simulator();
private boolean fastMode = false;
public GUI() {
    initComponents();
    busy.add(-1);//BEFORE -1 IS BROKEN KIOSKS, AFTER IS FIXERS
    this.getContentPane().setBackground(new Color(255,240,240));//reddish background? maybe
    this.setVisible(false);
}

private void simulate(boolean fastMode) throws InterruptedException{
    ArrayList<Integer> feedback;
        int spaces = (int)((double)(375 - (sim.num_cashiers * w))/(double)sim.num_cashiers);
        int kspaces = (int)((double)(375 - (sim.num_kiosks * w))/(double)sim.num_kiosks);
        feedback = sim.simulate();
        if(feedback.get(0)!=0){//a customer spawned, add a button!!
            JButton cButton = buildButton();
            System.out.println(feedback.get(0));
            boolean kiosk = feedback.get(0) < 0;//these 
            feedback.set(0, Math.abs(feedback.get(0)));//lines
            int lineNumber = feedback.get(0)%100;//parse
            int waitTime = feedback.get(0)/100;//feedback[0]
            cButton.setText(String.valueOf(waitTime));
            cButton.setVisible(true);
            //System.out.println("cSpawn~");
            if(!kiosk){
                cButton.setBounds((spaces/2)+(lineNumber*(spaces+w)), linePositions.get(lineNumber), w, h);
                linePositions.set(lineNumber, linePositions.get(lineNumber)+35);
            }
            else{//they joined a kiosk
                cButton.setBounds(375 + (kspaces/2)+(lineNumber*(kspaces+w)),kioskPositions.get(lineNumber),w,h);
                kioskPositions.set(lineNumber,kioskPositions.get(lineNumber)+35);
            }
            this.add(cButton);
            //System.out.println(cButton.getBounds().getLocation().toString());
        }
        if(feedback.get(1)!=0){//hey someone finished this turn!
            sim.totalTimeWaited+=feedback.get(1);
            //we will remove their button in another block(:
        }
        for (int i = 2,value = feedback.get(i); value != -1; i++, value = feedback.get(i)){ //goes through all cashiers who lose a customer
            remove(getComponentAt((spaces/2)+(value*(spaces+w)), 65));
            reButton(i);
        }
        for (int i = feedback.indexOf(-1)+1, value = feedback.get(i); value!= -2; i++,value = feedback.get(i)){//kiosks...
            remove(getComponentAt(375 + (kspaces/2)+(value*(kspaces+w)),65));
            reButton(-i);
        }
        for (int i = feedback.indexOf(-2)+1, value = feedback.get(i); value!=-3;i++,value = feedback.get(i)){//goes through broken kiosks
            busy.add(0,value);
            getComponentAt(375+(kspaces/2)+(value*kspaces+w), 30).setBackground(Color.red);
        }
        for (int i = feedback.indexOf(-3)+1, value = feedback.get(i); value!=-4;i++,value = feedback.get(i)){//goes through fixer dudes
            busy.add(busy.indexOf(-1)+1,value);
            getComponentAt((spaces/2)+(value*spaces+w), 30).setBackground(Color.red);
        }
        for (int i = 0; i < sim.num_cashiers; i++) 
            if(sim.cashiers[i].getRepairTime()==0)
               getComponentAt((spaces/2)+(i*spaces+w), 30).setBackground(Color.black); 
        for (int i = 0; i < sim.num_kiosks; i++) 
            if(!sim.kiosks[i].broken)
               getComponentAt((kspaces/2)+(i*kspaces+w), 30).setBackground(Color.black); 
        jLabel1.setText("Time = "+sim.time);
        jLabel3.setText("Orders = "+sim.orders);
        revalidate();
        repaint();
        Thread.sleep(300);
}

private void reButton(int line){
    int y = 65;
    int x;
    Component c;
    int spaces = (int)((double)(375 - (sim.num_cashiers * w))/(double)sim.num_cashiers);
    int kspaces = (int)((double)(375 - (sim.num_kiosks * w))/(double)sim.num_kiosks);
    if(line>0){//ITS A CASHIER LINE
        x = ((spaces/2)+(line*(spaces+w)));
        for(;getComponentAt(x,y)!=null;y+=35)
            getComponentAt(x,y).setLocation(x, y-35);
    }
    else{//ITS A KIOSK LINE
        x = (375 + (kspaces/2)+(-line*(kspaces+w)));
        for(;getComponentAt(x,y)!=null;y+=35)
            getComponentAt(x,y).setLocation(x, y-35);
    }
    revalidate();
    repaint();
}

private JButton buildButton(){
    JButton button = new JButton();
    button.setContentAreaFilled(false);
    button.setOpaque(true);
    button.setBorderPainted(false);
    button.setForeground(Color.black);
    button.setBackground(Color.white);
    button.setVisible(true);
    return button;
}
public void buildLines(){
    JButton temp;
    linePositions = new ArrayList<>(sim.num_cashiers);
    kioskPositions = new ArrayList<>(sim.num_kiosks);
    for (int i = 0; i < sim.num_cashiers; i++) 
        linePositions.add(65);
    for (int i = 0; i < sim.num_kiosks; i++) 
        kioskPositions.add(65);
    //System.out.println(linePositions.size());
    for (int i = 0; i < sim.num_cashiers; i++) {//sets up cashier buttons
        temp = buildButton();
        int spaces = (int)((double)(375 - (sim.num_cashiers * w))/(double)sim.num_cashiers);
        temp.setBounds((spaces/2)+(i*(spaces+w)),30,w,h);
        temp.setText("~C~");
        temp.setBackground(Color.darkGray);
        temp.setForeground(Color.white);
        this.add(temp);
    }
    for (int i = 0; i < sim.num_kiosks; i++) {//sets up cashier buttons
        temp = buildButton();
        int spaces = (int)((double)(375 - (sim.num_kiosks * w))/(double)sim.num_kiosks);
        temp.setBounds((375+(spaces/2))+(i*(spaces+w)),30,w,h);
        temp.setText("~K~");
        temp.setBackground(Color.darkGray);
        temp.setForeground(Color.white);
        this.add(temp);
    }


}

/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    jPanel1 = new javax.swing.JPanel();
    fastModeButton = new javax.swing.JToggleButton();
    jButton1 = new javax.swing.JButton();
    jLabel1 = new javax.swing.JLabel();
    jLabel3 = new javax.swing.JLabel();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle("CSE214HW4");
    setBackground(new java.awt.Color(255, 230, 230));
    setPreferredSize(new java.awt.Dimension(750, 435));

    jPanel1.setBackground(new java.awt.Color(230, 230, 255));
    jPanel1.setMinimumSize(new java.awt.Dimension(100, 30));

    fastModeButton.setText("Toggle Fast Mode");
    fastModeButton.setMaximumSize(new java.awt.Dimension(140, 20));
    fastModeButton.setMinimumSize(new java.awt.Dimension(140, 20));
    fastModeButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            fastModeButtonActionPerformed(evt);
        }
    });

    jButton1.setText("Simulate");
    jButton1.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            jButton1ActionPerformed(evt);
        }
    });

    jLabel1.setFont(new java.awt.Font("Lucida Console", 0, 20)); // NOI18N
    jLabel1.setText("Time = 0");

    jLabel3.setFont(new java.awt.Font("Lucida Console", 0, 20)); // NOI18N
    jLabel3.setText("Orders = 0");

    javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
    jPanel1.setLayout(jPanel1Layout);
    jPanel1Layout.setHorizontalGroup(
        jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE)
            .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(206, 206, 206)
            .addComponent(fastModeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jButton1)
            .addContainerGap())
    );
    jPanel1Layout.setVerticalGroup(
        jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(jPanel1Layout.createSequentialGroup()
            .addContainerGap()
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(fastModeButton, javax.swing.GroupLayout.DEFAULT_SIZE, 22, Short.MAX_VALUE)
                    .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
            .addContainerGap())
    );

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addContainerGap(401, Short.MAX_VALUE)
            .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
    );

    pack();
}// </editor-fold>                        

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    try {
        //buildLines();
        sim.build();
        for(int i = 0; i<sim.length;i++){
        simulate(fastMode);
        Thread.sleep(200);
        }
    } catch (InterruptedException ex) {
        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
    }
}                                        

private void fastModeButtonActionPerformed(java.awt.event.ActionEvent evt) {                                               
    fastMode = !fastMode;
}                                              

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new GUI().setVisible(true);
        }
    });
}

// Variables declaration - do not modify                     
private javax.swing.JToggleButton fastModeButton;
private javax.swing.JButton jButton1;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel3;
private javax.swing.JPanel jPanel1;
// End of variables declaration                   
}
Eric M.
  • 642
  • 5
  • 16
  • I looked at that, but his circumstances are just different enough that I don't really understand how to implement the solutions offered to him--he only wants 1 pause, and it's attached to an ActionListener. – Eric M. Mar 21 '14 at 23:23
  • The core issue is the same, you are putting the thread responsible for repainting the GUI to sleep, so it never has time for actually doing the work. You have to make the calls to `simulate` using a method that does not involve sleeping the main thread. – DSquare Mar 21 '14 at 23:25

2 Answers2

0

You havent created an Object of Thread in your code. You need to create a thread, override its run() method with your code and start the run() method of your thread by invoking start() on your Thread.

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

    new Thread() {
        public void run() {
            try {
                for (int i = 0; i < sim.length; i++) {
                    simulate();
                    Thread.sleep(500);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }.start();
}

Since you are updating a gui-element from a thread you should use invokelater().

private void simulate() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // Here, we can safely update the GUI
                // because we'll be called from the
                // event dispatch thread
                /* HERE YOUR your simulate() code */
            }
        });

}

I think that should work.

Gee858eeG
  • 185
  • 8
  • This is basically what a SwingWorker does. It creates a separate Thread and allows you to publish results so that the code is executed on the EDT. – camickr Mar 22 '14 at 00:13
  • oh ok. didnt know that yet. I <3 this site so much! Learned again something :) – Gee858eeG Mar 22 '14 at 00:41
0

Code in an ActionListener executes on the Event Dispatch THread. The Thread.sleep(...) causes the EDT to sleep so the GUI can't repaint itself until the entire loop is finished executing.

Read the section from the Swing tutorial on Concurrency for more information.

Ones solution, as described in the tutorial, is to use a SwingWorker and then "publish" the results as required.

Another approach is to use a Swing Timer to schedule the updates. The tutorial also has a section on How to Use Swing Timers.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Hm.. odd choice of code formatting on `How to Use Swing Timers`. It seemed deliberate, so I didn't change it, but it seems inappropriate for that Java Doc page title. How about **bold** or *italic* instead? – Andrew Thompson Mar 22 '14 at 02:52