Search code examples
javamultithreadingswingjprogressbar

progress bar not updating with threads


ok, I'm here again to ask one dummy question. I have a custom dialog frame and have progress bar on it. So also I have a genetic algorithm (which works pretty big amount of time).

As I understand all the situation, I need to execute separate threads for algorithm, progress bar and leave the main thread for the dialog (to give him an opportunity to respond to user actions). So I tried to implement this, and got the following:

public class ScheduleDialog extends JDialog {

    private final JPanel contentPanel = new JPanel();

    private static Data data;
    private static GeneticEngine geneticEngine;
    private static Schedule schedule;

    private static JProgressBar progressBar;
    private static JLabel statusLabel;

    public static void showProgress() {
        try {
            ScheduleDialog dialog = new ScheduleDialog();
            dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            dialog.setVisible(true);
            startScheduleComposing();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void startScheduleComposing() {
        BackGroundWorker backGroundWorker = new BackGroundWorker();
        GeneticEngineWorker geneticEngineWorker = new GeneticEngineWorker();
        new Thread(geneticEngineWorker).start();
        new Thread(backGroundWorker).start();
    }

    private ScheduleDialog() {

        this.data = Data.getInstance();

        setTitle("");
        setModal(true);
        setBounds(100, 100, 405, 150);
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension dialogSize = getSize();
        setLocation((screenSize.width - dialogSize.width) / 2, (screenSize.height - dialogSize.height) / 2);
        getContentPane().setLayout(new BorderLayout());
        contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
        getContentPane().add(contentPanel, BorderLayout.CENTER);
        contentPanel.setLayout(new GridLayout(0, 1, 0, 0));
        {
            progressBar = new JProgressBar();
            progressBar.setBackground(Color.RED);
            contentPanel.add(progressBar);
        }
        {
            statusLabel = new JLabel("");
            contentPanel.add(statusLabel);
        }
        {
            JPanel buttonPane = new JPanel();
            buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
            getContentPane().add(buttonPane, BorderLayout.SOUTH);
            {
                JButton okButton = new JButton("Прервать");
                okButton.setActionCommand("OK");
                buttonPane.add(okButton);
                getRootPane().setDefaultButton(okButton);
            }
        }


    }

    protected static class BackGroundWorker implements Runnable {
        @Override
        public void run() {
          while(true){
            int barValue = 100 - (geneticEngine.getCurrentBestFitness() / geneticEngine.getInitialFitness());
            progressBar.setValue(barValue);
            progressBar.repaint();
            statusLabel.setText(String.valueOf(geneticEngine.getCurrentBestFitness()));
            statusLabel.repaint();
            try{
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();  
            }
          }
        }
    }

    protected static class GeneticEngineWorker implements Runnable{

        @Override
        public void run() {
            geneticEngine = new GeneticEngine(data);
            schedule = geneticEngine.GeneticAlgorithm();
        }
    }
}

it seems, that new Thread(geneticEngineWorker).start(); is working but nothing happens with progress bar. I think this part of code:

private static void startScheduleComposing() {
            BackGroundWorker backGroundWorker = new BackGroundWorker();
            GeneticEngineWorker geneticEngineWorker = new GeneticEngineWorker();
            new Thread(geneticEngineWorker).start();
            new Thread(backGroundWorker).start();
        }

we get new Thread(backGroundWorker).start(); executed only after new Thread(geneticEngineWorker).start(); will be stopped.
So if I'm right could you please tell me how code can be reorganized to make the work, and if I'm wrong could you point me to my mistakes?

Thanks everyone in advance!


ADDITION

I debuged it and as I thought, backGroundWorker starts working only after geneticEngineWorker ends or user closes dialog(exception in this case);


Solution

  • yes there are two basics ways by using

    SwingWorker, required deepest knowledge about Java Essential Classes inc. Generics too

    and

    Runnable#Thread required only wrapping output to the GUI into invokeLater()

    simple example about no special effort required for idea Runnable#Thread, JProgressBar all in JTable, thats means that code line model.setValueAt(value, row, column); should be wrapped into invokeLater, but in other hands setText() is declared as thread safe, I'd to suggest wrapping that into invokeLater in all cases

    enter image description here enter image description here

    import java.awt.Component;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JProgressBar;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.SwingUtilities;
    import javax.swing.table.DefaultTableModel;
    import javax.swing.table.TableCellRenderer;
    
    public class TableWithProgressBars {
    
        private static final int maximum = 100;
    
        public void createGUI() {
            final JFrame frame = new JFrame("Progressing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            Integer[] oneRow = {0, 0, 0, 0};
            String[] headers = {"One", "Two", "Three", "Four"};
            Integer[][] data = {oneRow, oneRow, oneRow, oneRow, oneRow,};
            final DefaultTableModel model = new DefaultTableModel(data, headers);
            final JTable table = new JTable(model);
            table.setDefaultRenderer(Object.class, new ProgressRenderer(0, maximum));
            table.setPreferredScrollableViewportSize(table.getPreferredSize());
            frame.add(new JScrollPane(table));
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    Object waiter = new Object();
                    synchronized (waiter) {
                        int rows = model.getRowCount();
                        int columns = model.getColumnCount();
                        Random random = new Random(System.currentTimeMillis());
                        boolean done = false;
                        while (!done) {
                            int row = random.nextInt(rows);
                            int column = random.nextInt(columns);
                            Integer value = (Integer) model.getValueAt(row, column);
                            value++;
                            if (value <= maximum) {
                                model.setValueAt(value, row, column);
                                try {
                                    waiter.wait(15);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            done = true;
                            for (row = 0; row < rows; row++) {
                                for (column = 0; column < columns; column++) {
                                    if (!model.getValueAt(row, column).equals(maximum)) {
                                        done = false;
                                        break;
                                    }
                                }
                                if (!done) {
                                    break;
                                }
                            }
                        }
                        frame.setTitle("All work done");
                    }
                }
            }).start();
        }
    
        public static class ProgressRenderer extends JProgressBar implements TableCellRenderer {
    
            private static final long serialVersionUID = 1L;
    
            public ProgressRenderer(int min, int max) {
                super(min, max);
                this.setStringPainted(true);
            }
    
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value,
                    boolean isSelected, boolean hasFocus, int row, int column) {
                this.setValue((Integer) value);
                return this;
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new TableWithProgressBars().createGUI();
                }
            });
    
        }
    }