JProgressBar doesn't update , can't find a clue - java

nice job , now i just wanna know why if i add into while loop the instruction System.out.println below the progress is shown on both , cmd and Pgbar in the Gui ?? :
while(progress < 99){
System.out.println("into while of PBar Thread progress = "+progress);
if(progress != Path.operationProgress){
operationProgressBar.setValue(progress);
progress = Path.operationProgress;
operationProgressBar.repaint(); } }
need some help around , i can't get the JProgressBar to update, i
can't use SwingWorker, i have to solve this without it . the variable
Path.operationProgress is a static variable from a "Path" class
instance, and it's updated from another thread, so i think the PBar
and Path instances are both executed in user's Threads and not in the
EDT . here is the Code of the progress bar :
import javax.swing.*;
public class Pbar extends Thread {
JProgressBar operationProgressBar;
public Pbar(JProgressBar operationProgressBar) {
this.operationProgressBar = operationProgressBar;
}
#Override
public void run() {
int progress = Path.operationProgress;
while(progress < 99) {
if(progress != Path.operationProgress) {
operationProgressBar.setValue(progress);
progress = Path.operationProgress;
operationProgressBar.repaint();
}}}
}
this is the action that launches the threads :
private javax.swing.JProgressBar operationProgressBar;
private javax.swing.JLabel pathImage;
private javax.swing.JButton simulatedAnnelingButton;
public class TSPGUI extends javax.swing.JFrame {
TSPMG tspInstance;
Path p, result;
String filename = "";
int neighborHood_Type = 1, i = 0;
// ......Constructor Stuff and init()
private void simulatedAnnelingButtonActionPerformed(java.awt.event.ActionEvent evt)
{
Thread sa = new Thread(){
#Override
public void run(){
result = p.SimulatedAnnealing(neighborHood_Type);
String lastCostString = result.Cost() + "";
lastCostLabel.setText(lastCostString);
}};
sa.start();
Pbar pb = new Pbar(operationProgressBar);
pb.start();
}
//Some other Stuff ...
}

If you can't use SwingWorker then use SwingUtilities.invokeLater, e.g.:
if (progress != Path.operationProgress) {
final int progressCopy = progress; // Probably not final so copy is needed
SwingUtilities.invokeLater(new Runnable() {
#Override
void run() {
operationsProgressBar.setValue(progressCopy);
}
});
}
Note: When doing this, everything used in run has to be final or there have to be other measures to access the variables. This code is symbolic in that regard.
You need to do operations on Swing components outside the event dispatching thread, there is no way around this.

I would use a PropertyChangeListener to allow you to make the annealing progress value a "bound" property of the class. Than any observer can follow this property if desired. For example:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
#SuppressWarnings("serial")
public class TspGui2 extends JPanel {
private static final String ANNEALING_PROGRESS = "Annealing Progress";
private JProgressBar progBar = new JProgressBar(0, 100);
private JLabel valueLabel = new JLabel();
private JButton beginAnnealingBtn = new JButton("Begin Annealing");
private MyAnnealing myAnnealing = new MyAnnealing(this);
public TspGui2() {
beginAnnealingBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
beginAnnealing();
}
});
myAnnealing.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(MyAnnealing.ANNEALING)) {
// be sure this is done on the EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int annealedValue = myAnnealing.getAnnealedValue();
setValue(annealedValue);
if (annealedValue >= MyAnnealing.MAX_ANNEALED_VALUE) {
beginAnnealingBtn.setEnabled(true);
}
}
});
}
}
});
progBar.setString(ANNEALING_PROGRESS);
progBar.setStringPainted(true);
JPanel northPanel = new JPanel(new GridLayout(1, 0));
northPanel.add(beginAnnealingBtn);
northPanel.add(valueLabel);
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(northPanel);
add(progBar);
}
public void setValue(int value) {
valueLabel.setText("Value:" + value);
progBar.setValue(value);
}
public void beginAnnealing() {
beginAnnealingBtn.setEnabled(false);
setValue(0);
myAnnealing.reset();
new Thread(new Runnable() {
public void run() {
myAnnealing.beginAnnealing();
}
}).start();
}
private static void createAndShowGui() {
TspGui2 mainPanel = new TspGui2();
JFrame frame = new JFrame("TspGui2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MyAnnealing {
public static final String ANNEALING = "Annealing";
public static final int MAX_ANNEALED_VALUE = 100;
private SwingPropertyChangeSupport propChangeSupport =
new SwingPropertyChangeSupport(this);
private TspGui2 gui;
private int annealedValue;
public MyAnnealing(TspGui2 gui) {
this.gui = gui;
}
public void addPropertyChangeListener(
PropertyChangeListener listener) {
propChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(
PropertyChangeListener listener) {
propChangeSupport.removePropertyChangeListener(listener);
}
public void reset() {
setAnnealedValue(0);
}
// simulate some long process...
public void beginAnnealing() {
long sleepDelay = 100;
while (annealedValue < MAX_ANNEALED_VALUE) {
setAnnealedValue(annealedValue + 1);
try {
Thread.sleep(sleepDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int getAnnealedValue() {
return annealedValue;
}
private void setAnnealedValue(int value) {
final int oldValue = this.annealedValue;
this.annealedValue = value;
propChangeSupport.firePropertyChange(ANNEALING, oldValue, annealedValue);
}
}

Related

Wait for long-running operation and show popup

Is it possible to wait for a method (say METHOD1) to finish, but if it is running longer than X secs, call another method until METHOD1 returns?
Some pseudocode:
method1();
startCountdown(1000); // time in millis
while (method1() still running) {
method2(); // shows a popup with spinner (Swing/AWT)
}
I guess, it must be done with concurrency, but I am not used to concurrent programming. So, I have no idea how to start.
The UI framework used is Swing/AWT.
So, the basic idea would be to use a combination of a SwingWorker and a Swing Timer.
The idea is if the Timer triggers before the SwingWorker is DONE, you execute some other workflow, otherwise you stop the Timer, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private JButton startButton;
boolean hasCompleted = false;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
label = new JLabel("Waiting for you");
startButton = new JButton("Start");
add(label, gbc);
add(startButton, gbc);
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
startButton.setEnabled(false);
startWork();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void startWork() {
label.setText("Something wicked this way comes");
// You could build an isoloated workflow, which allowed you to pass
// three targets, the thing to be executed, the thing to be
// executed if time run over and the thing to be executed when
// the task completed (all via a single interface),
// but, you get the idea
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (hasCompleted) {
return;
}
label.setText("Wickedness is a bit slow today");
}
});
timer.setRepeats(false);
SomeLongRunningOperation worker = new SomeLongRunningOperation();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
switch (worker.getState()) {
case DONE:
hasCompleted = true;
timer.stop();
label.setText("All is done");
startButton.setEnabled(true);
break;
}
}
});
worker.execute();
timer.start();
}
}
public class SomeLongRunningOperation extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
Thread.sleep(5000);
return null;
}
}
}
Play around with the timings to see what different effects you get.
Why use a SwingWorker? Because it has it's own state callbacks, which makes it easier to deal with
As I said in my comments, you could distill the workflow down into a re-usable concept, something like...
public class TimedTask<V> {
public static interface Task<V> {
public V execute() throws Exception;
}
public static interface TimedTaskListener<V> extends EventListener {
public void taskIsTakingLongThenExepected(TimedTask task);
public void taskDidComplete(TimedTask task, V value);
}
private Task<V> task;
private TimedTaskListener<V> listener;
private V value;
private int timeOut;
private Timer timer;
private SwingWorker<V, Void> worker;
private boolean hasCompleted = false;
public TimedTask(int timeOut, Task<V> task, TimedTaskListener<V> listener) {
this.task = task;
this.listener = listener;
this.timeOut = timeOut;
}
public V getValue() {
return value;
}
public int getTimeOut() {
return timeOut;
}
protected Task<V> getTask() {
return task;
}
protected TimedTaskListener<V> getListener() {
return listener;
}
public void execute() {
if (timer != null || worker != null) {
return;
}
hasCompleted = false;
worker = new SwingWorker<V, Void>() {
#Override
protected V doInBackground() throws Exception {
value = task.execute();
return value;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
switch (worker.getState()) {
case DONE:
hasCompleted = true;
timer.stop();
getListener().taskDidComplete(TimedTask.this, value);
break;
}
}
});
timer = new Timer(getTimeOut(), new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (hasCompleted) {
return;
}
getListener().taskIsTakingLongThenExepected(TimedTask.this);
}
});
timer.setRepeats(false);
worker.execute();
timer.start();
}
}
And then you could replace the startWork method in the first example with something like...
protected void startWork() {
label.setText("Something wicked this way comes");
TimedTask.Task<Void> task = new TimedTask.Task<Void>() {
#Override
public Void execute() throws Exception {
Thread.sleep(5000);
return null;
}
};
TimedTask<Void> timedTask = new TimedTask(2000, task, new TimedTask.TimedTaskListener<Void>() {
#Override
public void taskIsTakingLongThenExepected(TimedTask task) {
label.setText("Wickedness is taking it's sweet time");
}
#Override
public void taskDidComplete(TimedTask task, Void value) {
label.setText("Wickedness has arrived");
startButton.setEnabled(true);
}
});
timedTask.execute();
}
While SwingWorker is the appropriate tool for the job, for simple tasks you can get away with a Thread for the off-edt long task and a swing Timer to update the GUI:
import java.awt.*;
import javax.swing.*;
import javax.swing.Timer;
public class Main{
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
}
class TestPane extends JPanel{
private static Dimension size = new Dimension(250, 100);
private final JLabel label;
private final JButton start;
private int counter;
private Timer timer;
public TestPane() {
setLayout(new BorderLayout(10, 10));
label = new JLabel("Click START to run long process", JLabel.CENTER);
add(label,BorderLayout.NORTH);
start = new JButton("START");
start.addActionListener(e-> start() );
add(start, BorderLayout.SOUTH);
}
private void start() {
start.setEnabled(false);
int processRunTime = 10;
int updateTime = 1; //if this value >= processRunTime update() is not invoked
counter = 1;
simulateLongProcessOf(processRunTime);
timer = new Timer(1000*updateTime, e->update(counter++));
label.setText("Long process started");
timer.start();
}
private void stop() {
label.setText("Long process ended");
timer.stop();
start.setEnabled(true);
}
#Override
public Dimension preferredSize() {
return size;
}
private void simulateLongProcessOf(int seconds){
Thread t1 = new Thread(()->{
try {
Thread.sleep(1000*seconds);
} catch (InterruptedException ex) {
ex.printStackTrace();
}finally {
SwingUtilities.invokeLater(()->stop());
}
});
t1.start();
}
private void update(int count){
label.setText("Update # "+ count+" : long process is running" );
}
}
I've struggled with this question before.
What I ended up doing was, creating a separate class that extends AsyncTask. Added an interface/listener to this class that returned my object. Right before I start my AsyncTask, I'll disable buttons and put up a loading spinner. Once the AsyncTask comes back, I'll do my processing and reenable the buttons and take down the loading spinner. Of coarse I'm doing a rest call in the example, but it can be applied to anything that takes awhile. The reason why this is a better option than a while loop is that it's won't be burning cycles checking conditions.
public class RestCall extends AsyncTask {
private Context mContext;
private static final String TAG = "RestCall";
private AsyncResponse mListener;
public RestCall(Context context, URL url, AsyncResponse listener) {
this.mListener = listener;
this.mContext = context;
this.url = url;
}
public interface AsyncResponse {
void processFinish(JSONArray results);
}
#Override
protected Object doInBackground(Object[] objects) {
Log.d(TAG, "doInBackground: Thread: " + Thread.currentThread().getName());
return getResultsInJSONArray(url);
}
private JSONArray getResultsInJSONArray(URL url) {
//Here is where you will be doing the bulk of the work
//Doing a rest call and
//Processing results to JSONArray
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
Log.d(TAG, "onPostExecute: Handing off Object");
mListener.processFinish((JSONArray) o);
}
Now in your original class you'll add the following to your class:
public class myClass
private restCall call;
Than create a listener from that interface you made. Then pass the results to a method.
restCall.AsyncResponse listener = results -> handleResults(results);
With the listener setup you can you can execute your AsyncTask.
//here is were you would throw up the loading bar.
call = new restCall(this, url, listener);
call.execute();
private void handleResults(JSONArray results){
//process what you need to
//take down loading bar
}

Java: Update GUI from another class within same .java file?

so I have this java file, it has two classes:
RPClient which has main method.
and
RPClientOpsImlp is being a listener which accepts messages from server and changes GUI.
here is simplified code.
Here is the file:
import java.io.*;
import java.lang.*;
import org.omg.CORBA.*;
import RPSGame.*;
import org.omg.CosNaming.* ;
import org.omg.CosNaming.NamingContextPackage.*;
import java.net.*;
import javax.swing.JOptionPane;
public class RPClient
{
public static void main(String args[])
{
try{
RPSGU rps = new RPSGU();
rps.pack();
rps.setVisible(true);
String playerName = JOptionPane.showInputDialog(rps, "Please enter your player name.");
rps.SetMyName(playerName);
} catch (Exception e) {
System.out.println("ERROR : " + e) ;
e.printStackTrace(System.out);
}
}
}
class RPClientOpsImpl implements RPClientOpsOperations{
public void callBack(String message) {
RPSGU rps = new RPSGU();
rps.SetMyName("NewName");
}
}
Basically in RPClientOpsImpl I tried calling the GUI and update it's label but that doesn't work.
RPSGU is a .java file of GUI which has this function:
public void SetProgress(String label){
progress.setText(label);
}
You write
RPClientOpsImlp is being a listener which accepts messages from server and changes GUI
The code in your question is not very clear, but if I have to make a guess I would say that the code is trying to update the GUI from a thread that is not the EDT. You can try doing something like this:
class RPClientOpsImpl implements RPClientOpsOperations {
private RPSGU rps = new RPSGU();
public void callBack(String message) {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
rps.SetMyName("NewName");
}
});
}
}
[FIXED] To refresh the same GUI, as per comment:
public class RPClient {
public static void main(String args[]) throws Exception {
final RPClientOpsImpl rpc = new RPClientOpsImpl();
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
RPSGU rps = new RPSGU();
rpc.setRps(rps);
rps.pack();
rps.setVisible(true);
String playerName = JOptionPane.showInputDialog(rps, "Please enter your player name.");
rps.setMyName(playerName);
}
});
}
}
class RPClientOpsImpl implements RPClientOpsOperations {
private RPSGU rps;
public void setRps(RPSGU rps) {
this.rps = rps;
}
public void callBack(String message) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
rps.setMyName("NewName");
}
});
}
}
Note that GUI creation and state change, such as invoking pack() and setVisible(), should be done on the EDT, hence the call to invokeAndWait() in the main() method.
Also, SetMyName() should actually be named setMyName(), with a lower case initial letter as per Java convention.
Consider doing things in the other direction:
Create your GUI
Launch your non-GUI program from within your GUI, using a SwingWorker to allow it to run in a background thread and to allow communication.
For example,...
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import javax.swing.*;
public class RpsMain {
private static void createAndShowGui() {
RpsGui mainPanel = new RpsGui();
JFrame frame = new JFrame("RpsMain");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
String playerName = JOptionPane.showInputDialog(mainPanel,
"Please enter your player name.");
mainPanel.setPlayerName(playerName);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class RpsGui extends JPanel {
private RpClient2 rpClient2;
private JTextArea textArea = new JTextArea(30, 50);
private String playerName;
public RpsGui() {
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
textArea.setFocusable(false);
JButton startRpClientButton = new JButton(
new StartRpClientAction("Start"));
JPanel btnPanel = new JPanel();
btnPanel.add(startRpClientButton);
setLayout(new BorderLayout());
int vsbPolicy = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS;
int hsbPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED;
add(new JScrollPane(textArea, vsbPolicy, hsbPolicy), BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
private class StartRpClientAction extends AbstractAction {
public StartRpClientAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (rpClient2 != null && !rpClient2.isDone()) {
return;
}
rpClient2 = new RpClient2(playerName, RpsGui.this);
rpClient2.execute();
}
}
public void appendText(String text) {
textArea.append(text);
}
}
class RpClient2 extends SwingWorker<Void, String> {
private static final long ARTIFICIAL_SLEEP_TIME = 1000;
private String playerName;
private int count = 0;
private RpsGui gui;
private boolean running = true;
public RpClient2(String playerName, RpsGui gui) {
this.playerName = playerName;
this.gui = gui;
}
#Override
protected Void doInBackground() throws Exception {
// the while loop below is just to simulate a long-running task.
// in a real application, here's where you'd have the code to
// the non-GUI stuff that you don't want to do on the event thread.
while (running) {
String dataForGui = "From RpClient2 background thread. Player: "
+ playerName + "; Count: " + count;
publish(dataForGui); // allows us to communicate with the GUI
count++;
Thread.sleep(ARTIFICIAL_SLEEP_TIME); // to simulate long-running
// activity
}
return null;
}
#Override
protected void process(List<String> chunks) {
for (String chunk : chunks) {
gui.appendText(chunk + "\n");
}
}
}

Is MVC in Swing Thread Safe

I'm trying to touch limits of MVC architecture in Swing, but as I tried everything all (from SwingWorker or Runnable#Thread) are done on EDT
my questions:
is there some limits or strictly depends by order of the implementations
(wrapped into SwingWorker or Runnable#Thread) ?
limited is if is JComponent#method Thread Safe or not ?
essential characteristic of an MVC architecture in Swing, ?
inc. Container Re-Layout ?
note: for my SSCCE I take one of great examples by HFOE, and maybe by holding this principes strictly isn't possible to create any EDT lack or GUI freeze
import java.awt.BorderLayout;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
public class MVC_ProgressBarThread {
private MVC_ProgressBarThread() {
MVC_View view = new MVC_View();
MVC_Model model = new MVC_Model();
MVC_Control control = new MVC_Control(view, model);
view.setControl(control);
JFrame frame = new JFrame("MVC_ProgressBarThread");
frame.getContentPane().add(view);
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() {
MVC_ProgressBarThread mVC_ProgressBarThread = new MVC_ProgressBarThread();
}
});
}
}
class MVC_View extends JPanel {
private static final long serialVersionUID = 1L;
private MVC_Control control;
private JProgressBar progressBar = new JProgressBar();
private JButton startActionButton = new JButton("Press Me and Run this Madness");
private JLabel myLabel = new JLabel("Nothing Special");
public MVC_View() {
startActionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
buttonActionPerformed();
}
});
JPanel buttonPanel = new JPanel();
startActionButton.setFocusPainted(false);
buttonPanel.add(startActionButton);
setLayout(new BorderLayout(10, 10));
add(buttonPanel, BorderLayout.NORTH);
progressBar.setStringPainted(true);
add(progressBar, BorderLayout.CENTER);
myLabel.setIcon(UIManager.getIcon("OptionPane.questionIcon"));
myLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
add(myLabel, BorderLayout.SOUTH);
}
public void setControl(MVC_Control control) {
this.control = control;
}
private void buttonActionPerformed() {
if (control != null) {
control.doButtonAction();
}
}
public void setProgress(int progress) {
progressBar.setValue(progress);
}
public void setProgressLabel(String label) {
progressBar.setString(label);
}
public void setIconLabel(Icon icon) {
myLabel.setIcon(icon);
}
public void start() {
startActionButton.setEnabled(false);
}
public void done() {
startActionButton.setEnabled(true);
setProgress(100);
setProgressLabel(" Done !!! ");
setIconLabel(null);
}
}
class MVC_Control {
private MVC_View view;
private MVC_Model model;
public MVC_Control(final MVC_View view, final MVC_Model model) {
this.view = view;
this.model = model;
model.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pce) {
if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) {
view.setProgress((Integer) pce.getNewValue());
}
if (MVC_Model.PROGRESS1.equals(pce.getPropertyName())) {
view.setProgressLabel((String) pce.getNewValue());
}
if (MVC_Model.PROGRESS2.equals(pce.getPropertyName())) {
view.setIconLabel((Icon) pce.getNewValue());
}
}
});
}
public void doButtonAction() {
view.start();
SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
model.reset();
model.startSearch();
return null;
}
#Override
protected void done() {
view.done();
}
};
swingworker.execute();
}
}
class MVC_Model {
public static final String PROGRESS = "progress";
public static final String PROGRESS1 = "progress1";
public static final String PROGRESS2 = "progress2";
private static final int MAX = 11;
private static final long SLEEP_DELAY = 1000;
private int progress = 0;
private String label = "Start";
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private PropertyChangeSupport pcs1 = new PropertyChangeSupport(this);
private PropertyChangeSupport pcs2 = new PropertyChangeSupport(this);
private final String[] petStrings = {"Bird", "Cat", "Dog",
"Rabbit", "Pig", "Fish", "Horse", "Cow", "Bee", "Skunk"};
private int index = 1;
private Queue<Icon> iconQueue = new LinkedList<Icon>();
private Icon icon = (UIManager.getIcon("OptionPane.questionIcon"));
public void setProgress(int progress) {
int oldProgress = this.progress;
this.progress = progress;
PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS,
oldProgress, progress);
pcs.firePropertyChange(evt);
}
public void setProgressLabel(String label) {
String oldString = this.label;
this.label = label;
PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS1,
oldString, label);
pcs1.firePropertyChange(evt);
}
public void setIconLabel(Icon icon) {
Icon oldIcon = this.icon;
this.icon = icon;
PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS2,
oldIcon, icon);
pcs2.firePropertyChange(evt);
}
public void reset() {
setProgress(0);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
pcs1.addPropertyChangeListener(listener);
pcs2.addPropertyChangeListener(listener);
}
public void startSearch() {
iconQueue.add(UIManager.getIcon("OptionPane.errorIcon"));
iconQueue.add(UIManager.getIcon("OptionPane.informationIcon"));
iconQueue.add(UIManager.getIcon("OptionPane.warningIcon"));
iconQueue.add(UIManager.getIcon("OptionPane.questionIcon"));
for (int i = 0; i < MAX; i++) {
int newValue = (100 * i) / MAX;
setProgress(newValue);
setProgressLabel(petStrings[index]);
index = (index + 1) % petStrings.length;
setIconLabel(nextIcon());
try {
Thread.sleep(SLEEP_DELAY);
} catch (InterruptedException e) {
}
}
}
private Icon nextIcon() {
Icon icon1 = iconQueue.peek();
iconQueue.add(iconQueue.remove());
return icon1;
}
}
This is too long for a comment...
First and this is unrelated to the rest of this answer: there are many different MVCs out there and the one you used in that piece of code you posted here is not the same as the one used in the article you linked to: http://www.oracle.com/technetwork/articles/javase/mvc-136693.html
The article correctly points out that it's just "A common MVC implementation" (one where the view registers a listener listening to model changes). Your implementation is a different type of MVC, where the controller registers a listener listening to model changes and then updates the view.
Not that there's anything wrong with that: there are a lot of different types of MVCs out there (*).
(Another little caveat... Your view is aware of your controller in your example, which is a bit weird: there are other ways to do what you're doing without needing to "feed" the controller to the view like you do with your setControl(...) inside your MVCView.)
But anyway... You're basically nearly always modifying the GUI from outside the EDT (which you shouldn't be doing):
public void setIconLabel(final Icon icon) {
myLabel.setIcon(icon);
}
You can check it by adding this:
System.out.println("Are we on the EDT? " + SwingUtilities.isEventDispatchThread());
This is because you're eventually doing these updates from your SwingWorker thread (the SwingWorker thread is run outside the EDT: it's basically the point of a Swing worker).
I'd rather update the GUI from the EDT, doing something like this:
public void setIconLabel(final Icon icon) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
myLabel.setIcon(icon);
}
});
}

how can i solve this error

This is the complete code :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.Thread;
class jProgressBar {
JProgressBar pb;
JButton start;
int i;
jProgressBar() {
buildGUI();
hookUpEvents();
}
public void buildGUI() {
JFrame fr=new JFrame("Progress Bar");
JPanel p=new JPanel();
p.setLayout(new FlowLayout(FlowLayout.CENTER));
JPanel barPanel=new JPanel();
barPanel.setLayout(new GridLayout(2,0,50,50));
pb=new JProgressBar(0,10);
start=new JButton("Start Demo");
fr.add(p);
barPanel.add(start);
barPanel.add(pb);
p.add(barPanel);
fr.setSize(500,500);
fr.setVisible(true);
}
public void hookUpEvents() {
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
try {
Runnable r=new Runnable() {
public void run() {
action(ae); // LINE 39
}
};
Thread th=new Thread(r);
th.start();
} catch(Exception exc) {
System.out.println(exc);
}
}
});
}
public void action(ActionEvent ae) {
start.setVisible(false);
try {
Runnable rp=new Runnable() {
public void run() {
i++;
pb.setValue(i);
try {
Thread.sleep(2000);
} catch(Exception exc) {
System.out.println(exc);
}
if(i==5) {
pb.setString("Half Done!");
}
else if(i==10) {
pb.setString("Completed!");
}
}
};
Thread th=new Thread(rp);
th.start();
} catch(Exception exc) {
System.out.println(exc);
}
}
public static void main(String args[]) {
new jProgressBar();
}
}
This is the error produced on cmd:
d:\UnderTest>javac jProgressBar.java
jProgressBar.java:39: local variable ae is accessed from within inner class; needs to be declared fina
l
action(ae);
^
1 error
What is this error and how can I solve this error?
Declare the variable ae as final:
public void actionPerformed(final ActionEvent ae) {
This means that it cannot be assigned a new value, which should be fine according to your current code.
a very nice example for SwingWorker
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class SwingWorkerExample extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private final JButton startButton, stopButton;
private JScrollPane scrollPane = new JScrollPane();
private JList listBox = null;
private DefaultListModel listModel = new DefaultListModel();
private final JProgressBar progressBar;
private mySwingWorker swingWorker;
public SwingWorkerExample() {
super("SwingWorkerExample");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new GridLayout(2, 2));
startButton = makeButton("Start");
stopButton = makeButton("Stop");
stopButton.setEnabled(false);
progressBar = makeProgressBar(0, 99);
listBox = new JList(listModel);
scrollPane.setViewportView(listBox);
getContentPane().add(scrollPane);
//Display the window.
pack();
setVisible(true);
}
//Class SwingWorker<T,V> T - the result type returned by this SwingWorker's doInBackground
//and get methods V - the type used for carrying out intermediate results by this SwingWorker's
//publish and process methods
private class mySwingWorker extends javax.swing.SwingWorker<ArrayList<Integer>, Integer> {
//The first template argument, in this case, ArrayList<Integer>, is what s returned by doInBackground(),
//and by get(). The second template argument, in this case, Integer, is what is published with the
//publish method. It is also the data type which is stored by the java.util.List that is the parameter
//for the process method, which recieves the information published by the publish method.
#Override
protected ArrayList<Integer> doInBackground() {
//Returns items of the type given as the first template argument to the SwingWorker class.
if (javax.swing.SwingUtilities.isEventDispatchThread()) {
System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() returned true.");
}
Integer tmpValue = new Integer(1);
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) { //find every 100th prime, just to make it slower
tmpValue = FindNextPrime(tmpValue.intValue());
//isCancelled() returns true if the cancel() method is invoked on this class. That is the proper way
//to stop this thread. See the actionPerformed method.
if (isCancelled()) {
System.out.println("SwingWorker - isCancelled");
return list;
}
}
//Successive calls to publish are coalesced into a java.util.List, which is what is received by process,
//which in this case, isused to update the JProgressBar. Thus, the values passed to publish range from
//1 to 100.
publish(new Integer(i));
list.add(tmpValue);
}
return list;
}//Note, always use java.util.List here, or it will use the wrong list.
#Override
protected void process(java.util.List<Integer> progressList) {
//This method is processing a java.util.List of items given as successive arguments to the publish method.
//Note that these calls are coalesced into a java.util.List. This list holds items of the type given as the
//second template parameter type to SwingWorker. Note that the get method below has nothing to do with the
//SwingWorker get method; it is the List's get method. This would be a good place to update a progress bar.
if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
}
Integer percentComplete = progressList.get(progressList.size() - 1);
progressBar.setValue(percentComplete.intValue());
}
#Override
protected void done() {
System.out.println("doInBackground is complete");
if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
}
try {
//Here, the SwingWorker's get method returns an item of the same type as specified as the first type parameter
//given to the SwingWorker class.
ArrayList<Integer> results = get();
for (Integer i : results) {
listModel.addElement(i.toString());
}
} catch (Exception e) {
System.out.println("Caught an exception: " + e);
}
startButton();
}
boolean IsPrime(int num) { //Checks whether a number is prime
int i;
for (i = 2; i <= num / 2; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
protected Integer FindNextPrime(int num) { //Returns next prime number from passed arg.
do {
if (num % 2 == 0) {
num++;
} else {
num += 2;
}
} while (!IsPrime(num));
return new Integer(num);
}
}
private JButton makeButton(String caption) {
JButton b = new JButton(caption);
b.setActionCommand(caption);
b.addActionListener(this);
getContentPane().add(b);
return b;
}
private JProgressBar makeProgressBar(int min, int max) {
JProgressBar progressBar1 = new JProgressBar();
progressBar1.setMinimum(min);
progressBar1.setMaximum(max);
progressBar1.setStringPainted(true);
progressBar1.setBorderPainted(true);
getContentPane().add(progressBar1);
return progressBar1;
}
private void startButton() {
startButton.setEnabled(true);
stopButton.setEnabled(false);
System.out.println("SwingWorker - Done");
}
#Override
public void actionPerformed(ActionEvent e) {
if ("Start" == null ? e.getActionCommand() == null : "Start".equals(e.getActionCommand())) {
startButton.setEnabled(false);
stopButton.setEnabled(true);
// Note that it creates a new instance of the SwingWorker-derived class. Never reuse an old one.
(swingWorker = new mySwingWorker()).execute(); // new instance
} else if ("Stop" == null ? e.getActionCommand() == null : "Stop".equals(e.getActionCommand())) {
startButton.setEnabled(true);
stopButton.setEnabled(false);
swingWorker.cancel(true); // causes isCancelled to return true in doInBackground
swingWorker = null;
}
}
public static void main(String[] args) {
// Notice that it kicks it off on the event-dispatching thread, not the main thread.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SwingWorkerExample swingWorkerExample = new SwingWorkerExample();
}
});
}
}
There are some counterproductive issues present.
Swing is single-thread based, and all actions must be done on the EDT. For that reason, your JProgressBar doesn't update correctly. See also Concurrency in Swing.
Don't use Thread.sleep(int) in Swing, and certainly not in an action listener.
By using Runnable, it is possible to update JProgressBar; but as mentioned, the method must be run from invokeLater().
For that, SwingWorker would be better, as shown below and here.
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();
}
}

MVC Progress Bar Threading

I am using an MVC pattern for my design, when a user presses the search button, I call a search in the model, but I also want to update a progress bar with information returned from that model.
I have tried using a swingworker, but the progress bar does not update. I suspect I am doing something wrong with my threading.
My button as defined in the controller is:
class SearchBtnListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
_view.displayProgress();
}
}
This calls the search in the model and has the following call in the view:
public void displayProgress() {
TwoWorker task = new TwoWorker();
task.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent e) {
if ("progress".equals(e.getPropertyName())) {
_progressBar.setValue((Integer) e.getNewValue());
}
}
});
task.execute();
}
private class TwoWorker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
_model.startSearch(getTerm()); // time intensive code
File file = new File("lock");
while (file.exists()){
setProgress(_model.getStatus());
System.out.println(_model.getStatus()); // never called
}
return null;
}
protected void done(){
updateMain();
}
}
Dummy function defined in Model for testing:
public int getStatus(){
Random r = new Random();
return r.nextInt();
}
Don't call
_progressBar.setValue(_model.getStatus());
from within your SwingWorker as this is calling Swing code from a background thread and is what the PropertyChangeListener is for anyway. Instead, just set the progress property, that's all.
Also, don't call done() from within the doInBackground method as this needs to be called from the EDT by the SwingWorker. So let the SwingWorker itself call this method when it is in fact done.
Also, Done() should be done() -- the first letter shouldn't be capitalized, and you should use #Override annotations in this code so you can be sure that you're overriding methods correctly.
Also, what does this do?
_model.startSearch(_view.getTerm());
Does it call code that takes a while to complete? Should this be initialized from within the SwingWorker doInBackground itself?
Edit:
Another option is to give the Model a bound int property, say called progress, and then add a PropertyChangeListener to it directly letting it update the JProgressBar. For example,
import java.awt.BorderLayout;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.*;
public class MVC_ProgressBarThread {
private static void createAndShowUI() {
MVC_View view = new MVC_View();
MVC_Model model = new MVC_Model();
MVC_Control control = new MVC_Control(view, model);
view.setControl(control);
JFrame frame = new JFrame("MVC_ProgressBarThread");
frame.getContentPane().add(view);
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() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class MVC_View extends JPanel {
private MVC_Control control;
private JProgressBar progressBar = new JProgressBar();
private JButton startActionButton = new JButton("Start Action");
public MVC_View() {
startActionButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonActionPerformed();
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(startActionButton);
setLayout(new BorderLayout());
add(buttonPanel, BorderLayout.NORTH);
add(progressBar, BorderLayout.CENTER);
}
public void setControl(MVC_Control control) {
this.control = control;
}
private void buttonActionPerformed() {
if (control != null) {
control.doButtonAction();
}
}
public void setProgress(int progress) {
progressBar.setValue(progress);
}
public void start() {
startActionButton.setEnabled(false);
}
public void done() {
startActionButton.setEnabled(true);
setProgress(100);
}
}
class MVC_Control {
private MVC_View view;
private MVC_Model model;
public MVC_Control(final MVC_View view, final MVC_Model model) {
this.view = view;
this.model = model;
model.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent pce) {
if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) {
view.setProgress((Integer)pce.getNewValue());
}
}
});
}
public void doButtonAction() {
view.start();
SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
model.reset();
model.startSearch();
return null;
}
#Override
protected void done() {
view.done();
}
};
swingworker.execute();
}
}
class MVC_Model {
public static final String PROGRESS = "progress";
private static final int MAX = 100;
private static final long SLEEP_DELAY = 100;
private int progress = 0;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void setProgress(int progress) {
int oldProgress = this.progress;
this.progress = progress;
PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS, oldProgress, progress);
pcs.firePropertyChange(evt);
}
public void reset() {
setProgress(0);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void startSearch() {
for (int i = 0; i < MAX; i++) {
int newValue = (100 * i) / MAX;
setProgress(newValue);
try {
Thread.sleep(SLEEP_DELAY);
} catch (InterruptedException e) {}
}
}
}

Categories