|
Worker and Timer Threads
An Introduction with Examples in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
SwingWorker
SwingWorker
execute() method
doInBackground() is called
PropertyChangeEvents
done() is called
SwingWorker:
setProgress() to generate
PropertyChangeEvent objectspublish() to cause the process()
method to be calledPropertyChangeListener:
propertyChange() messages
and check the name (e.g. "progress", "state")SwingWorker:
isCancelled()
SwingWorker:
cancel() method to change its stateSwingWorker Threadsexecute() is called in.doInBackground() is
called in.process() and done()
are called in (and the thread that is used to
inform PropertyChangeListener objects).
import java.util.*;
import javax.swing.*;
/**
* Compute pi using the van Wijngaarden transformation
* of the Gregory-Leibniz series
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class PiCalculator extends SwingWorker<Double,Double>
{
// SwingWorker<T,V> where:
// T is the result type returned by doInBackground() and get()
// V is the type used for intermediate results by publish() and process()
//
private int digits;
/**
* Explicit Value Constructor
*
* @param digits The number of digits of accuracy
*/
public PiCalculator(int digits)
{
this.digits = digits;
}
/**
* Calculate the i,j term in the transformation
*/
private double pi(int i, int j)
{
if (isCancelled()) return 4.0;
double den, num, result;
if (j == 0) return 0.0;
// We could call setProgress() and pass an int in [0,100]
// to cause PropertyChangeEvent objects to be fired
// to listeners (in the Event Dispatch Thread).
//
// We could also call publish() to cause this object's
// process() method to be called in the Event DispatchThread.
if (i == 0)
{
// pi(0,1): 4/1 pi(0,2): 4/1 - 4/3 pi(0,3): 4/1-4/3+4/5 ...
result = 0.0;
for (int k=1; k<=j; k++)
{
num = Math.pow(-1.0, k+1) * 4.0;
den = (k * 2.0) - 1;
result += num / den;
}
}
else
{
result = (pi(i-1,j) + pi(i-1,j+1))/2.0;
}
return result;
}
/**
* The code to execute in the background thread
*/
public Double doInBackground()
{
double result;
result = pi(digits+1, digits+1);
// This will cause the result to be retrievable
// using the get() method
return Double.valueOf(result);
}
}
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
/**
* An example that uses PiCalculator (which is a the SwingWorker)
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class PiDriver implements ActionListener, PropertyChangeListener
{
private int digits;
private JButton cancelButton, startButton;
private JTextField digitsField, resultArea;
private PiCalculator task;
/**
* The entry point
*/
public static void main(String[] args)
{
new PiDriver();
}
/**
* Default Constructor
*/
public PiDriver()
{
Container contentPane;
JFrame frame;
JPanel north;
frame = new JFrame();
frame.setTitle("Calculate pi");
contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
resultArea = new JTextField();
contentPane.add(resultArea, BorderLayout.SOUTH);
north = new JPanel();
north.add(new JLabel("Digits:"));
digitsField = new JTextField(10);
north.add(digitsField);
startButton = new JButton("Start");
startButton.addActionListener(this);
startButton.setEnabled(true);
north.add(startButton);
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(this);
cancelButton.setEnabled(false);
north.add(cancelButton);
contentPane.add(north, BorderLayout.NORTH);
frame.setSize(400,100);
frame.setVisible(true);
task = null;
}
/**
* Handle actionPerformed() messages (required by
* ActionListener)
*
* @param evt The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent evt)
{
String ac;
ac = evt.getActionCommand();
if (ac.equals("Start") && (task == null))
{
try
{
resultArea.setText("");
digits = Integer.parseInt(digitsField.getText());
task = new PiCalculator(digits);
task.addPropertyChangeListener(this);
startButton.setEnabled(false);
cancelButton.setEnabled(true);
task.execute();
}
catch (NumberFormatException nfe)
{
nfe.printStackTrace();
}
}
else if (ac.equals("Cancel"))
{
if (task != null)
{
startButton.setEnabled(true);
cancelButton.setEnabled(false);
task.cancel(true);
task = null;
}
}
}
/**
* Handle propertyChange() messages (required by
* PropertyChangeListener)
*
* @param evt The PropertyChangeEvent that generated the message
*/
public void propertyChange(PropertyChangeEvent evt)
{
String format;
if (evt.getPropertyName().equals("progress"))
{
// Could do something like:
// progressBar.setValue((Integer)evt.getNewValue());
}
else if (evt.getPropertyName().equals("state"))
{
if (evt.getNewValue().equals(SwingWorker.StateValue.DONE))
{
startButton.setEnabled(true);
cancelButton.setEnabled(false);
format = "%"+(digits+1)+"."+digits+"f\n";
try
{
resultArea.setText(String.format(format, task.get()));
}
catch (Exception e)
{
}
task = null;
}
}
}
}
javax.swing.Timer
addActionListener() (either explicitly
or using the constructor)start() or restart()
ActionListener
BlinkingLabel
package gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* A Jlabel that blinks (in an annoying way)
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BlinkingLabel extends JLabel implements ActionListener
{
private boolean status;
private Color textColor;
private Timer timer;
/**
* Explicit Value Constructor
*/
public BlinkingLabel(String text)
{
super(text);
performCommonConstructionTasks();
}
/**
* Explicit Value Constructor
*/
public BlinkingLabel(String text, Icon icon, int horizontalAlignment)
{
super(text, icon, horizontalAlignment);
performCommonConstructionTasks();
}
/**
* Explicit Value Constructor
*/
public BlinkingLabel(String text, int horizontalAlignment)
{
super(text, horizontalAlignment);
performCommonConstructionTasks();
}
/**
* Handle actionPerformed() message (required by ActionListener)
*
* @param evt The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent evt)
{
if (status) setForeground(textColor);
else setForeground(Color.RED);
status = !status;
}
/**
* Perform common construction tasks
*/
private void performCommonConstructionTasks()
{
timer = new Timer(500, this);
status = false;
}
/**
* Start the blinking
*/
public void start()
{
textColor = getForeground();
timer.restart();
}
/**
* Stop the blinking
*/
public void stop()
{
timer.stop();
setForeground(textColor);
}
}