Alptugay
Alptugay

Reputation: 1686

Show an indeterimante progress bar in a JDialog in a thread and run a task in another thread concurrently

When a user clicks a button, a long task of approximately 10 seconds will run. During this time I want to show a progress bar to the user. But the main thread has to wait for the worker thread to finish because the worker thread will set a variable that the main thread will use. If I don't wait the worker thread I will get a NullPointerException when using the variable. So after the worker thread finishes, I will also close the progress bar dialog.

When I wait for the worker thread using join() the progress bar dialog shows (interestingly without the progress bar though) and hangs there.

Thread runnable = new Thread() {

    public void run() {
        try {
            System.out.println("thread basladi");
            threadAddSlaveReturnMessage = request.addSlave(
                ipField.getText(), passField.getText(), 
                nicknameField.getText());
            System.out.println("thread bitti");

        } catch (LMCTagNotFoundException e) {
            e.printStackTrace();
        }

    }
};

Thread runnable_progress = new Thread() {

    public void run() {

        JTextArea msgLabel;
        JDialog dialog;
        JProgressBar progressBar;
        final int MAXIMUM = 100;
        JPanel panel;

        progressBar = new JProgressBar(0, MAXIMUM);
        progressBar.setIndeterminate(true);
        msgLabel = new JTextArea("deneme");
        msgLabel.setEditable(false);


        panel = new JPanel(new BorderLayout(5, 5));
        panel.add(msgLabel, BorderLayout.PAGE_START);
        panel.add(progressBar, BorderLayout.CENTER);
        panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));

        dialog = new JDialog(Frame.getFrames()[0], "baslik", true);
        dialog.getContentPane().add(panel);
        dialog.setResizable(false);
        dialog.pack();
        dialog.setSize(500, dialog.getHeight());
        dialog.setLocationRelativeTo(null);
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        dialog.setAlwaysOnTop(false);
        dialog.setVisible(true);
        msgLabel.setBackground(panel.getBackground());
    }
};

runnable.start();
System.out.println("runnable start");
runnable_progress.start();
System.out.println("progress start");
runnable.join();
System.out.println("runnable join");
runnable_progress.join();
System.out.println("progress join");


if (threadAddSlaveReturnMessage.equalsIgnoreCase("OK")) {
    fillInventoryTable(inventoryTable);
    JOptionPane.showMessageDialog(this, messages.getString("centrum.addslavepanel.SUCCESS"), null, JOptionPane.INFORMATION_MESSAGE);
}

"progress join"

doesn't get printed.

Upvotes: 4

Views: 10385

Answers (3)

Sebastian_H
Sebastian_H

Reputation: 349

As the previous answers already mentioned, SwingWorker is the way to go, if use want to use concurrency with Swing.
I found this SwingWorker and ProgressBar tutorial quite useful in understanding how it all works together.

Coming back to your actual problem: I assume you use a GUI, because you stated the user has to click a button. The question is, what is your "main thread" doing? Does it really have to run all the time? Doesn't look like it. Because you said the thread needs a variable which is set by another thread, which is a result of a user interaction. In short: Why does it need to run if it's dependent on a user interaction anyway?
The usual way would be to first get all the data you need and then run the calculations or whatever. In your case, either run everything in a single background thread(set the variable first then do the rest), started by the ActionListener of your button or run the other thread after the thread where you set the variable has completed.
You could for example use the method done(), provided by SwingWorker, to launch the next task. Or if you really have to, you could wait in a loop for task.isDone()to return true. But don't forget to check for isCancelled() too.

Anyway, I think you should rethink your design. Because what I can see from the limited information provided looks overly complicated to me.

Upvotes: 2

uahakan
uahakan

Reputation: 596

You can use a SwingWorker here. A short example :

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.godel.nio;

import java.awt.BorderLayout;
import java.util.List;
import javax.swing.*;

/**
 *
 * @author internet_2
 */
public class Test {

    public static void main(String[] args) {
        new Test().doJob();
    }

    public void doJob() {

        JTextArea msgLabel;
        JProgressBar progressBar;
        final int MAXIMUM = 100;
        JPanel panel;

        progressBar = new JProgressBar(0, MAXIMUM);
        progressBar.setIndeterminate(true);
        msgLabel = new JTextArea("deneme");
        msgLabel.setEditable(false);

        panel = new JPanel(new BorderLayout(5, 5));
        panel.add(msgLabel, BorderLayout.PAGE_START);
        panel.add(progressBar, BorderLayout.CENTER);
        panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));

        final JDialog dialog = new JDialog();
        dialog.getContentPane().add(panel);
        dialog.setResizable(false);
        dialog.pack();
        dialog.setSize(500, dialog.getHeight());
        dialog.setLocationRelativeTo(null);
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        dialog.setAlwaysOnTop(false);
        dialog.setVisible(true);
        msgLabel.setBackground(panel.getBackground());

        SwingWorker worker = new SwingWorker() {

            @Override
            protected void done() {
                // Close the dialog
                dialog.dispose();
            }

            @Override
            protected void process(List chunks) {
                // Here you can process the result of "doInBackGround()"
                // Set a variable in the dialog or etc.
            }

            @Override
            protected Object doInBackground() throws Exception {
                // Do the long running task here
                // Call "publish()" to pass the data to "process()"
                // return something meaningful
                return null;
            }
        };

        worker.execute();

    }
}

Edit : "publish()" should be called in "doInBackground()" to pass the data to "process()".

Upvotes: 4

mKorbel
mKorbel

Reputation: 109813

  1. you have the issue with Concurency is Swing, your GUI is visible after all thread are done

  2. is possible to moving with JProgressBar (I'm talking about you code) but you have to

    • create and show JDialog, create once time and reuse this container

    • then to start Thread,

    • better could be from Runnable#Thread

    • output to the Swing GUI must be wrapped into invokeLater()

  3. this is exactly job for using SwingWorker and with PropertyChangeListener

Upvotes: 3

Related Questions