Search code examples
javamultithreadingswingswingworkerjprogressbar

Java: read values continuously to multiple jprogressbars that never completes


I have 3 "JProgressBars" in a Java GUI application, which I need to keep updating the values at constant time intervals.

This is my application and highlighted are the progress meters which I want to read the values into.

enter image description here to read the values for each progress meter I have 3 methods in my object (here a robot class) example:

robot.readBattery();
robot.readSonic();
robot.readLight();

All methods will return a value between 0 and 100.

I have seen for a single progress bar this can be done using a swingworker. so does that mean I need 3 swing worker classes in my program to serve all three progress bars?

even if I did that how does the property change listener distinguish among progress bars?

I'll be thankful if somebody could guide me through this.

I have a connect method in robot which returns true if the program is connected to the robot.

ie: boolean connected = robot.connect();

so inside a swingworker the code should be something like ,

while (true) {
    if (connected){
    setProgress(robot.readBattery());
    }

    sleep(1000);
}

I may be wrong. please guide me.

SSCCE as requested:

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class Tester extends JFrame implements PropertyChangeListener,
        ActionListener {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private JProgressBar batteryMeter, lightMeter, sonicMeter;

    private Robot robot = new Robot();
    private BatteryTask bt;

    private JButton start = new JButton("Start");

    public Tester() {

        JPanel statusPanel = new JPanel();
        statusPanel.setLayout(new GridBagLayout());

        GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(2, 4, 2, 4);
        c.fill = GridBagConstraints.HORIZONTAL;

        c.gridx = 0;
        c.gridy = 0;
        statusPanel.add(new JLabel("Battery Level:"), c);

        batteryMeter = new JProgressBar(0, 100);
        batteryMeter.setStringPainted(false);
        batteryMeter.setPreferredSize(new Dimension(215, 15));
        batteryMeter.setValue(0);
        c.gridx = 1;
        c.gridy = 0;
        statusPanel.add(batteryMeter, c);

        c.gridx = 0;
        c.gridy = 1;
        statusPanel.add(new JLabel("Light Sensor:"), c);

        lightMeter = new JProgressBar(0, 100);
        lightMeter.setStringPainted(false);
        lightMeter.setPreferredSize(new Dimension(215, 15));
        lightMeter.setValue(0);
        c.gridx = 1;
        c.gridy = 1;
        statusPanel.add(lightMeter, c);
        c.gridx = 0;
        c.gridy = 2;
        statusPanel.add(new JLabel("Ultrasonic Sensor:"), c);

        sonicMeter = new JProgressBar(0, 100);
        sonicMeter.setStringPainted(false);
        sonicMeter.setPreferredSize(new Dimension(215, 15));
        sonicMeter.setValue(0);
        c.gridx = 1;
        c.gridy = 2;
        statusPanel.add(sonicMeter, c);
        c.gridx = 0;
        c.gridy = 3;

            start.addActionListener(this);
        statusPanel.add(start, c);
        this.getContentPane().add(statusPanel);

        this.setSize(500, 300);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.pack();
        this.setVisible(true);

    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Tester();
            }
        });

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub

        if (e.getSource() == start) {

            bt = new BatteryTask();
            bt.addPropertyChangeListener(this);
            bt.execute();
        }

    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        // TODO Auto-generated method stub
        if ("progress" == evt.getPropertyName()) {
            int progress = (Integer) evt.getNewValue();
            batteryMeter.setValue(progress);
        }

    }

    class BatteryTask extends SwingWorker<Void, Void> {
        /*
         * Main task. Executed in background thread.
         */
        @Override
        public Void doInBackground() {

            // Initialize progress property.
            setProgress(0);
            while (true) {
                // Sleep for up to one second.
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignore) {
                }
                // Make random progress.

                setProgress(robot.readBattery());
            }
        }

    }

}

class Robot {

    private Random r = new Random();

    int readBattery() {

        int i = r.nextInt(100 - 1) + 1;
        System.out.println(i);
        return i;
    }

    int readSonic() {

        return r.nextInt(100 - 1) + 1;
    }

    int readLight() {

        return r.nextInt(100 - 1) + 1;
    }
}

Solution

  • The PropertyChangeEvent named "progress" communicates only a single value, and the worker's progress as a whole is irrelevant. Instead, let a single RobotTask background thread publish() a StatusRecord holding the three new values. The worker's process() method, which executes on the event dispatch thread, can update the three progress bars as new records arrive. There are related examples here and here.

    class RobotTask extends SwingWorker<StatusRecord, StatusRecord> {…}