1

I would like to make JProgressBar in new JDialog, witch will be in separate thread from main logic. So I can start indeterminate progress with just creating new JDialog and completing that progress with disposing JDialog. But it gives me hard time to achieve that because after JDialog appears it doesn't show any components (including JProgressBar) until the logic in main thread (SwingUtilities) is done.

Thread including JDialog:

package gui.progress;

public class ProgressThread extends Thread {
    private ProgressBar progressBar = null;

    public ProgressThread() {
        super();
    }

    @Override
    public void run() {
        progressBar = new ProgressBar(null);
        progressBar.setVisible(true);
    }

    public void stopThread() {
        progressBar.dispose();
    }
}

JProgressBar toggle method:

private static ProgressThread progressThread = null;
...
public static void toggleProcessBar() {
    if(progressThread == null) {
        progressThread = new ProgressThread();
        progressThread.start();
    } else {
        progressThread.stopThread();
        progressThread = null;
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
Minutis
  • 1,193
  • 1
  • 17
  • 47
  • Never call swing code off [EDT](http://en.wikipedia.org/wiki/Event_dispatching_thread). – predi Jun 26 '12 at 13:54
  • See [this answer](http://stackoverflow.com/questions/8916721/java-swing-update-label/8917565#8917565) for an example – Robin Jun 26 '12 at 14:01
  • Sounds like you need a [ProgressMonitor](http://docs.oracle.com/javase/7/docs/api/javax/swing/ProgressMonitor.html), with some [example of how to use it](http://docs.oracle.com/javase/tutorial/uiswing/components/progress.html) – Bugasu Jun 26 '12 at 14:28

2 Answers2

9

But it gives me hard time to achieve that because after JDialog appears it doesn't show any components (including JProgressBar) until the logic in main thread (SwingUtilities) is done.

you have issue with Concurrency in Swing, Swing is single threaded and all updates must be done on EventDispatchThread, there are two ways

  • easies to use Runnable#Thread, but output to the Swing GUI must be wrapped into invokeLater

  • use SwingWorker, example about SwingWorker is in the Oracles JProgressBar and SwingWorker tutorial

EDIT

this code simulate violating EDT and correct workaround for SwingWorker too

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

public class TestProgressBar {

    private static void createAndShowUI() {
        JFrame frame = new JFrame("TestProgressBar");
        frame.getContentPane().add(new TestPBGui().getMainPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowUI();
            }
        });
    }

    private TestProgressBar() {
    }
}

class TestPBGui {

    private JPanel mainPanel = new JPanel();

    public TestPBGui() {
        JButton yourAttempt = new JButton("Your attempt to show Progress Bar");
        JButton myAttempt = new JButton("My attempt to show Progress Bar");
        yourAttempt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                yourAttemptActionPerformed();
            }
        });
        myAttempt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                myAttemptActionPerformed();
            }
        });
        mainPanel.add(yourAttempt);
        mainPanel.add(myAttempt);
    }

    private void yourAttemptActionPerformed() {
        Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
        JDialog progressDialog = new JDialog(thisWin, "Uploading...");
        JPanel contentPane = new JPanel();
        contentPane.setPreferredSize(new Dimension(300, 100));
        JProgressBar bar = new JProgressBar(0, 100);
        bar.setIndeterminate(true);
        contentPane.add(bar);
        progressDialog.setContentPane(contentPane);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(null);
        Task task = new Task("Your attempt");
        task.execute();
        progressDialog.setVisible(true);
        while (!task.isDone()) {
        }
        progressDialog.dispose();
    }

    private void myAttemptActionPerformed() {
        Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
        final JDialog progressDialog = new JDialog(thisWin, "Uploading...");
        JPanel contentPane = new JPanel();
        contentPane.setPreferredSize(new Dimension(300, 100));
        final JProgressBar bar = new JProgressBar(0, 100);
        bar.setIndeterminate(true);
        contentPane.add(bar);
        progressDialog.setContentPane(contentPane);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(null);
        final Task task = new Task("My attempt");
        task.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equalsIgnoreCase("progress")) {
                    int progress = task.getProgress();
                    if (progress == 0) {
                        bar.setIndeterminate(true);
                    } else {
                        bar.setIndeterminate(false);
                        bar.setValue(progress);
                        progressDialog.dispose();
                    }
                }
            }
        });
        task.execute();
        progressDialog.setVisible(true);
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }
}

class Task extends SwingWorker<Void, Void> {

    private static final long SLEEP_TIME = 4000;
    private String text;

    public Task(String text) {
        this.text = text;
    }

    @Override
    public Void doInBackground() {
        setProgress(0);
        try {
            Thread.sleep(SLEEP_TIME);// imitate a long-running task
        } catch (InterruptedException e) {
        }
        setProgress(100);
        return null;
    }

    @Override
    public void done() {
        System.out.println(text + " is done");
        Toolkit.getDefaultToolkit().beep();
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • @Max thanks, true is one of real Swing gurus (on this forum) post me similair code as answer to my question (another forum) – mKorbel Jun 26 '12 at 17:38
1

Show the progress bar in the main thread (where all your swing stuff is going on), and do the intensive background work on a separate thread.

The UI will be responsive all the time (since you don't block the main thread), and you can notify it when you long-running task is done.

WhyNotHugo
  • 9,423
  • 6
  • 62
  • 70
  • Please specify that main thread is the Event Dispatch Thread, as all Swing components should be accessed on that thread (which is different from the thread in which your main method is executed) – Robin Jun 26 '12 at 14:14