javaswingvariablesawtjprogressbar

Java second variable dismisses first one


What I have to do is make a little GUI that when a button is clicked it starts to progress bars with threads; however, we have to do it with an inner class that implements the Runnable interface, and within the constructor for that class, it takes a String and a JProgressBar object. Then within the main constructor, we are supposed to instantiate 2 JProgressBar objects and then create 2 of the Inner class objects using those JProgressBars.

Only my second JProgressBar is updating with the thread. I know that my second JProgressBar is basically overriding my first one, but I don't know how I would go to fix that, because if I try to change the inner class's constructor to setting 2 JProgressBar attributes equal to the parameter of the constructor, then the first JProgessBar just completely disappears from the GUI. Here is my code, thank you to anyone that can help.

    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;

    public class Lab4Part2 {
    //Attributes
    private JFrame frame;
    private String progress;
    private JProgressBar jpb;

    //Constructor
    public Lab4Part2() {
      //Create frame with specified grid layout
      frame = new JFrame();
      GridLayout grid = new GridLayout(0,1);
      frame.setLayout(grid);
      JButton jbClick = new JButton("Let's start this show");
      frame.add(jbClick);

      //Add in JProgressBars and create Inner Class objects with them
      JProgressBar jpb1 = new JProgressBar();
      JProgressBar jpb2 = new JProgressBar();
      InnerProgress bar1 = new InnerProgress("Progress 1: ", jpb1);
      InnerProgress bar2 = new InnerProgress("Progress 2: ", jpb2);

      //Anonymous class for the button's action listener
      jbClick.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            Thread t1 = new Thread(bar1);
            Thread t2 = new Thread(bar2);
            t1.start();
            t2.start();
         }
      });

      //Adds inner object to frame
      frame.add(bar1);
      frame.add(bar2);

      //Packing and stuff
      frame.pack();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
    }

      //Inner class
      class InnerProgress extends JPanel implements Runnable {

      //Constructor
      public InnerProgress(String pbar, JProgressBar jpBar) {
         jpBar.setMinimum(0);
         jpBar.setMaximum(80);
         jpBar.setStringPainted(true);
         progress = pbar;
         jpb = jpBar;
         JLabel label = new JLabel(pbar);
         add(label);
         add(jpBar);
      }

      //Thread action
      public void run() {
         System.out.println("We are running " + Thread.currentThread().getName());
         for(int i = 1; i<= 80; i++) {
            try {
               Thread.currentThread().sleep((long)Math.random()*100);
               jpb.setValue(i);
            }
            catch(InterruptedException ie) {
               System.out.println(ie);
            }
         }
         System.out.println("Thread name: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
      }
     }

      //Main
      public static void main(String[] args) {
      new Lab4Part2();
     }

    }

Solution

  • Your inner class should maintain it's own reference to the JProgressBar that it is passed.

    Simply take...

    private JProgressBar jpb;
    

    and move it inside your inner class

    class InnerProgress extends JPanel implements Runnable {
    
        private JProgressBar jpb;
        //...
    

    So, two other issues

    One, (long)Math.random()*100 is causing the result of Math.random() to be case to an int, which will result in it becoming 0.

    You need to cast the result of the operation, for example...

    Thread.currentThread().sleep((int)(Math.random() * 500));
    

    (nb: I made it 500 for my testing)

    Two, Swing is not thread safe. This means you should not be updating the UI from outside of the context of the Event Dispatching Thread.

    So, instead of...

    jpb.setValue(i);
    

    I wrote a new method which will update the UI accordingly

    protected void updateProgress(int value) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                InnerProgress.this.jpb.setValue(value);
            }
        });
    }
    

    (Long and short, this over comes issues with referencing variables from inside an anonymous context)

    Then simply called it using updateProgress(i);

    Runnable example...

    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JProgressBar;
    import javax.swing.SwingUtilities;
    
    public class Lab4Part2 {
    //Attributes
    
        private JFrame frame;
        private String progress;
    
    //Constructor
        public Lab4Part2() {
            //Create frame with specified grid layout
            frame = new JFrame();
            GridLayout grid = new GridLayout(0, 1);
            frame.setLayout(grid);
            JButton jbClick = new JButton("Let's start this show");
            frame.add(jbClick);
    
            //Add in JProgressBars and create Inner Class objects with them
            JProgressBar jpb1 = new JProgressBar();
            JProgressBar jpb2 = new JProgressBar();
            InnerProgress bar1 = new InnerProgress("Progress 1: ", jpb1);
            InnerProgress bar2 = new InnerProgress("Progress 2: ", jpb2);
    
            //Anonymous class for the button's action listener
            jbClick.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    Thread t1 = new Thread(bar1);
                    Thread t2 = new Thread(bar2);
                    t1.start();
                    t2.start();
                }
            });
    
            //Adds inner object to frame
            frame.add(bar1);
            frame.add(bar2);
    
            //Packing and stuff
            frame.pack();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        //Inner class
        class InnerProgress extends JPanel implements Runnable {
    
            private JProgressBar jpb;
    
            //Constructor
            public InnerProgress(String pbar, JProgressBar jpBar) {
                jpBar.setMinimum(0);
                jpBar.setMaximum(80);
                jpBar.setStringPainted(true);
                progress = pbar;
                jpb = jpBar;
                JLabel label = new JLabel(pbar);
                add(label);
                add(jpBar);
            }
    
            //Thread action
            public void run() {
                System.out.println("We are running " + Thread.currentThread().getName());
                for (int i = 1; i <= 80; i++) {
                    try {
                        Thread.currentThread().sleep((int)(Math.random() * 500));
                        updateProgress(i);
                    } catch (InterruptedException ie) {
                        System.out.println(ie);
                    }
                }
                System.out.println("Thread name: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
    
        protected void updateProgress(int value) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    InnerProgress.this.jpb.setValue(value);
                }
            });
        }
        }
    
        //Main
        public static void main(String[] args) {
            new Lab4Part2();
        }
    
    }