SwingWorker

I have developed a swing application which requires some very memory intensive tasks to be performed which also update the GUI, specifically the progress bars.

I want to provide options as to pause and stopping this task. I have tried using Thread as well as SwingWorker, also I have tried using SwingUtilities methods such as invokeLater() and invokeAndWait(). I am not able to successfully pause or stop the process keeping the GUI responsive at the same time.

Please give me suggestions as to how I might improve my logic so that I can achieve this kind of a task.

Should I be using a mixture of SwingWorker and Thread to achieve this? If yes also give some pointers as to how this may be possible.

Thanks & Regards.

Dhruva Sagar.

[774 byte] By [DhruvaSagara] at [2007-11-27 8:56:15]
# 1

I don't think you should mix swingworker and thread since swingworker is a background thread for use with swing apps (anyone, please correct me if this is wrong).

Please post an SSCCE that demonstrates your problem, or tries to attempt what you want to do, and we may have a better idea of what you are trying to achieve and how to solve it.

Let me correct that. You may need to use some Thread calls. For instance, it may not be a bad idea to call Thread myThread = Thread.currentThread(); at the beginning of your cpu or memory-intensive method, and then sprinkle the method with one or a few myThread.yield(); calls.

Message was edited by:

petes1234

petes1234a at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 2

Would any of this code be helpful? (again, anyone, please correct if mistakes seen).

What this code does is show 4 buttons at the top of a JPanel. The two left try to paint a JPanel below red while calling a method called "busyWork() that does nothing but waste the CPU's time (for about 10 seconds). They also try to inactivate both of the two left buttons in otherwords they inactivate themself and the other button. The button all the way to the left does this straight out without use of threads. The one next to it uses SwingWorker. The 3rd button is there to see if Java is still responding to events and will pop up a JOptionPane if so. The 4th button is to try to cancel the SwingWorker thread.

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

class SwingBckGround2 extends JFrame implements ActionListener

{

private JPanel mainPanel = new JPanel();

private JPanel buttonPanel = new JPanel();

private JButton noThreadButton = new JButton("No Background Thread");

private JButton yesThreadButton = new JButton("With Background Thread");

private JButton otherButton = new JButton("Button to show Swing still working");

private JButton cancelSwingWorkerButton = new JButton("Cancel SwingWorker Thread");

private JPanel redPanel = new JPanel();

private JLabel redPanelLabel = new JLabel("Press left two Buttons to try to turn panel red and inactivate 2 left buttons");

private Color redPanelBkGrndColor;

private SwingWorker<Void, Void> mySW = new SwingWorker<Void, Void>()

{

@Override // do our busy work here in the background

protected Void doInBackground() throws Exception

{

busyWork();

return null;

}

@Override

public void done()

{

// when done reset button to enabled, and

// change redPanel color back to original color

SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

resetButtons();

}

});

}

};

public SwingBckGround2()

{

super("Brief Swing App");

createWidgets();

getContentPane().add(mainPanel);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

pack();

setVisible(true);

}

private void createWidgets()

{

// create buttonPanel

int edgeSize = 10;

FlowLayout fLayO = new FlowLayout();

fLayO.setHgap(2* edgeSize);

buttonPanel.setLayout(fLayO);

buttonPanel.add(noThreadButton);

buttonPanel.add(yesThreadButton);

buttonPanel.add(otherButton);

buttonPanel.add(cancelSwingWorkerButton);

otherButton.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

JOptionPane.showMessageDialog(null, "Swing is active and able to respond!");

}

});

cancelSwingWorkerButton.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

mySW.cancel(true);

resetButtons();

}

});

buttonPanel.setBorder(BorderFactory.createEmptyBorder(edgeSize, edgeSize, edgeSize, edgeSize));

noThreadButton.addActionListener(this);

yesThreadButton.addActionListener(this);

// create redPanel

redPanel.setPreferredSize(new Dimension(40, 80));

redPanel.add(redPanelLabel);

// create mainPanel

mainPanel.setLayout(new BorderLayout());

mainPanel.add(buttonPanel, BorderLayout.NORTH);

mainPanel.add(redPanel, BorderLayout.SOUTH);

}

// any method that will take some time to finish

private void busyWork()

{

Thread thisThread = Thread.currentThread();

int loopSize = 1500;

for (int i = 0; i < loopSize; i++)

{

for (int j = 0; j < loopSize; j++)

{

for (int k = 0; k < loopSize; k++)

{

int foo = i + j + k;

}

thisThread.yield();

}

}

}

private void resetButtons()

{

noThreadButton.setEnabled(true);

yesThreadButton.setEnabled(true);

redPanel.setBackground(redPanelBkGrndColor);

}

public void actionPerformed(ActionEvent e)

{

JButton myBtn = (JButton)e.getSource();

if (myBtn.equals(noThreadButton))

{

noThreadButton.setEnabled(false);

yesThreadButton.setEnabled(false);

redPanelBkGrndColor = redPanel.getBackground();

redPanel.setBackground(Color.red);

busyWork();

resetButtons();

}

else if (myBtn.equals(yesThreadButton)) // the if not needed but here for clarity

{

noThreadButton.setEnabled(false); // disable the other button

yesThreadButton.setEnabled(false);

redPanelBkGrndColor = redPanel.getBackground(); // store original panel color

redPanel.setBackground(Color.red); // set redpanel to red

// create our background thread. Needs Java 6 to work

mySW.execute(); // lets call our SwingWorker object here

}

}

private static void createAndShowGUI()

{

SwingBckGround2 swngBckGrnd = new SwingBckGround2();

}

public static void main(String[] args)

{

javax.swing.SwingUtilities.invokeLater(new Runnable()

{ public void run() { createAndShowGUI();}});

}

}

Good luck!

Message was edited by:

petes1234

petes1234a at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 3

I have tried a similar approach and what I have found is that even though the call to mySW.cancel(true) stops the execution ( at least it stops updating the GUI...) but the process doesn't actually stop and runs in the background.

I don't know why, and untill it finishes the GUI is not responsive, and once it does finish it updates the GUI with what it should be once the execution of the process should end normally (if not tried to stop/pause).

I have also tried using wait() and notifyAll methods to try and make the processing SwingWorker thread to pause, but a similar thing happens, in the GUI it seems as it is paused, but the GUI is not responsive and so on...

I will post a SSCCE soon...

DhruvaSagara at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 4
We'll keep our eyes out for your code. Good luck!/Pete
petes1234a at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 5

I've looked at my app in a little more depth, and it too continues with background processing after the it is not supposed to. I am thinking that putting a call to !SwingWorker.isCancelled() in my busywork() method may help, but the main fact is, I still have quite a bit to learn about SwingWorker.

petes1234a at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 6

I've solved my problem (I think). I've kept the SwingWorker as a class field, but I initialize it each time that the yesThreadButton is pressed. In my busyWork() method, I have an if block that checks to see if the swingworker thread has been cancelled. If so, it bumps out of the busyWork loop.

I've also renamed the variable mySW to mySwingWorker for clarity.

I've created a JLabel called backgroundProgressLabel that is updated in a thread-safe way by the busyWork method and lets me know if the busyWork is still, well, busy.

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

class SwingBckGround2 extends JFrame implements ActionListener

{

private JPanel mainPanel = new JPanel();

private JPanel buttonPanel = new JPanel();

private JButton noThreadButton = new JButton("No Background Thread");

private JButton yesThreadButton = new JButton("With Background Thread");

private JButton otherButton = new JButton("Button to show Swing still working");

private JButton cancelSwingWorkerButton = new JButton("Cancel SwingWorker Thread");

private JPanel redPanel = new JPanel();

private JLabel redPanelLabel = new JLabel("Press left two Buttons to try to turn panel red and inactivate 2 left buttons");

private JLabel backgroundProgressLabel = new JLabel("");

private int counter = 0;

private Runnable setBackGroundProgressLabel = new Runnable()

{

public void run()

{

backgroundProgressLabel.setText(String.valueOf(counter));

}

};

private Color redPanelBkGrndColor;

private SwingWorker<Void, Void> mySwingWorker;

public SwingBckGround2()

{

super("Brief Swing App");

createWidgets();

getContentPane().add(mainPanel);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

pack();

setVisible(true);

}

private void createWidgets()

{

// create buttonPanel

int edgeSize = 10;

FlowLayout fLayO = new FlowLayout();

fLayO.setHgap(2 * edgeSize);

buttonPanel.setLayout(fLayO);

buttonPanel.add(noThreadButton);

buttonPanel.add(yesThreadButton);

buttonPanel.add(otherButton);

buttonPanel.add(cancelSwingWorkerButton);

otherButton.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

JOptionPane.showMessageDialog(null,

"Swing is active and able to respond!");

}

});

cancelSwingWorkerButton

.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

mySwingWorker.cancel(true);

resetButtons();

}

});

buttonPanel.setBorder(BorderFactory.createEmptyBorder(

edgeSize, edgeSize, edgeSize, edgeSize));

noThreadButton.addActionListener(this);

yesThreadButton.addActionListener(this);

// create redPanel

redPanel.setPreferredSize(new Dimension(40, 80));

redPanel.add(redPanelLabel);

redPanel.add(backgroundProgressLabel);

// create mainPanel

mainPanel.setLayout(new BorderLayout());

mainPanel.add(buttonPanel, BorderLayout.NORTH);

mainPanel.add(redPanel, BorderLayout.SOUTH);

}

// any method that will take some time to finish

private void busyWorkWithoutThreading()

{

Thread thisThread = Thread.currentThread();

int loopSize = 1500;

for (int i = 0; i < loopSize; i++)

{

for (int j = 0; j < loopSize; j++)

{

for (int k = 0; k < loopSize; k++)

{

int foo = i + j + k;

}

}

counter = i;

SwingUtilities.invokeLater(setBackGroundProgressLabel);

}

}

private void busyWorkWithThreading()

{

Thread thisThread = Thread.currentThread();

int loopSize = 1500;

for (int i = 0; i < loopSize; i++)

{

if (!mySwingWorker.isCancelled())

{

for (int j = 0; j < loopSize; j++)

{

for (int k = 0; k < loopSize; k++)

{

int foo = i + j + k;

}

}

thisThread.yield();

counter = i;

SwingUtilities.invokeLater(setBackGroundProgressLabel);

}

else

{

break;

}

}

}

private void resetButtons()

{

noThreadButton.setEnabled(true);

yesThreadButton.setEnabled(true);

redPanel.setBackground(redPanelBkGrndColor);

}

public void actionPerformed(ActionEvent e)

{

JButton myBtn = (JButton) e.getSource();

if (myBtn.equals(noThreadButton))

{

noThreadButton.setEnabled(false);

yesThreadButton.setEnabled(false);

redPanelBkGrndColor = redPanel.getBackground();

redPanel.setBackground(Color.red);

busyWorkWithoutThreading();

resetButtons();

}

else if (myBtn.equals(yesThreadButton))

{

noThreadButton.setEnabled(false);

yesThreadButton.setEnabled(false);

redPanelBkGrndColor = redPanel.getBackground();

redPanel.setBackground(Color.red);

mySwingWorker = new SwingWorker<Void, Void>()

{

@Override

// do our busy work here in the background

protected Void doInBackground() throws Exception

{

busyWorkWithThreading();

return null;

}

@Override

public void done()

{

// when done reset button to enabled, and

// change redPanel color back to original color

SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

resetButtons();

}

});

}

};

// create our background thread. Needs Java 6 to work

mySwingWorker.execute(); // lets call our SwingWorker object

// here

}

}

private static void createAndShowGUI()

{

SwingBckGround2 swngBckGrnd = new SwingBckGround2();

}

public static void main(String[] args)

{

javax.swing.SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

createAndShowGUI();

}

});

}

}

petes1234a at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 7

Some comments on your use of SwingWorker.

The done() method is invoked on the EDT so there is no need to call invokeLater there. The same is true for the process method, which btw would come in handy here.

SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {

@Override public Void doInBackground() {

Thread thisThread = Thread.currentThread();

int loopSize = 1500;

for (int i = 0; i < loopSize; i++) {

if (!mySwingWorker.isCancelled()) {

for (int j = 0; j < loopSize; j++) {

for (int k = 0; k < loopSize; k++) {

int foo = i + j + k;

}

}

thisThread.yield();

counter = i;

publish(counter); // leads to an eventual call to process.

} else {

break;

}

}

}

@Override public void process(List<Integer> chunks) {

// everything here is happening on the EDT

int value = chunks.get(chunks.size() - 1);

backgroundProgressLabel.setText(String.valueOf(value));

}

@Override public void done() {

// everything here is happening on the EDT

resetButtons();

}

};

Another way would be to use a PropertyChangeListener to listen to the bound property "progress" and call SwingWorker's setProgress method from within doInBackground.

dwga at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 8
> Some comments on your use of SwingWorker....dwg,... Thanks for your suggestions. They were all to the point, understandable, and quite helpful./Pete
petes1234a at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 9

I have a class MyWorker that extends SwingWorker to perform my tasks.

This is a short SSCCE that I have made to show the general functionality of my application. Perhaps it gives you a better idea of what I am trying to achieve.

public class MyWorker extends SwingWorker {

boolean paused;

public boolean getMyState() {

return paused;

}

public void setMyState(boolean paused) {

this.paused = paused;

}

private void duHeavyStuff() {

//Here I do very heavy/time consuming and memory intensive work,

//I also keep updating the GUI progress bar along with this tasks

//completion. I call separate functions from within this function to do

//certain tasks. I have put this code to pause the thread.

//I have also used this same code from within those called functions

//since each function call also takes time for execution.

//The functions I call from here are al synchronized. But from the

//application point of view I have only one MyWorker Thread working

//at a time.

synchronized(this) {

while(paused) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

@Override

protected Object doInBackground() throws Exception {

doHeavyStuff();

return null;

}

}

public class Main extends JApplet {

JButton stopWorking, startWorking;

MyWorker worker;

boolean started;

public Main() {

JPanel controlButtons=new JPanel();

controlButtons.setLayout(new BoxLayout(controlButtons, BoxLayout.Y_AXIS));

stopWorking = new JButton("Stop Working");

stopWorking.setVisible(false);

stopWorking.setIcon(stopImage);

stopWorking.setCursor(Cursor.getDefaultCursor());

stopWorking.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

stopWork();

}

});

startWorking = new JButton("Start Working");

startWorking.setIcon(crackImage);

startWorking.setCursor(Cursor.getDefaultCursor());

startWorking.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent ae) {

startWork();

}

});

controlButtons.add(startWorking);

controlButtons.add(stopWorking);

getContentPane().add(controlButtons);

}

private void startWork() {

stopWorking.setVisible(true);

startWorkng.setText("Pause");

started=!started;

if(started) {

if(worker == null)

doWork();

else {

synchronized(worker) {

worker.setMyState(false);

worker.notifyAll();

}

}

} else {

synchronized(worker) {

worker.setMyState(true);

}

startWork.setText("Resume Working");

}

}

private void stopWork() {

startWorking.setText("Start Working");

stopWorking.setVisible(false);

started=false;

worker.setCrackerState(true);

worker = null;

}

private void doWork() {

worker = new MyWorker();

worker.execute();

}

public static void main(String[] args) {

new Main().setVisible(true);

}

}

The pausing mechanism that I am using here doesn't work that well on Thread, I was using thread.suspend() and thread.resume() for those even though it is depracated. But it worked, but the thread doesn't keep my GUI responsive.

Swing worker on the other hand does keep my GUI responsive (after all it is designed to function like that.) but doesn't really pause the way I want it to.

In fact it doesn't even stop executing...

DhruvaSagara at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...
# 10
I am using the SwingWorker that comes with jdk 6.0
DhruvaSagara at 2007-7-12 21:18:46 > top of Java-index,Desktop,Core GUI APIs...