I'm trying to dynamically update the GUI with a String. Can anyone see why the process method is not overriding correctly? I've looked at other questions and still cannot see how I am not overriding this correctly.
public class WorkerDemo extends JFrame {
private JLabel counterLabel = new JLabel("Not started");
private Worker worker = new Worker();
private JButton startButton = new JButton(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent arg0) {
worker = new Worker();
worker.execute();
}
});
private JButton stopButton = new JButton(new AbstractAction("Stop") {
#Override
public void actionPerformed(ActionEvent arg0) {
worker.cancel(true);
}
});
public WorkerDemo() {
add(startButton, BorderLayout.WEST);
add(counterLabel, BorderLayout.CENTER);
add(stopButton, BorderLayout.EAST);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
class Worker extends SwingWorker<Void, Integer> {
int counter = 0;
String abc = "abc";
#Override
protected Void doInBackground() throws Exception {
while(true) {
abc += abc;
publish(abc);
Thread.sleep(60);
}
}
#Override
protected void process(List<Object> chunk) {
// get last result
String to_return = (String) chunk.get(chunk.size()-1);
counterLabel.setText(to_return);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new WorkerDemo();
}
});
}
}
Since you have SwingWorker<Void, Integer>, you have defined Integer as a type to carry out intermediate results by publish and process methods. That means proper publish() and process() methods should use Integer:
#Override
protected Void doInBackground() throws Exception {
int someResult = 0;
...
publish(someResult);
...
}
#Override
protected void process(List<Integer> chunk) {
}
See SwingWorker for more details.
Related
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
}
I have this code to open a custom Java browser:
private void openNavigator(){
Navigator browser = new Navigator();
SwingUtilities.invokeLater(() -> {
browser.initComponents();
browser.setVisible(true);
browser.loadURL("http://XXXXXXXX:8888/YYYYYY/ZZZZ");
});
}
In the other hand i have the code of the navigator:
public class Navigator extends JFrame {
private static final Logger LOG = Logger.getLogger(Navegador.class.getName());
private static final long serialVersionUID = -1951385676682823399L;
private WebView view;
private JFXPanel javaFxPanel;
private WebEngine engine;
private JLabel labelStatus;
private JTextField direction;
private JProgressBar progressBar;
private java.net.CookieManager cookiesManager;
public void initComponents() {
if (cookiesManager != null) {
cookiesManager = new java.net.CookieManager();
java.net.CookieHandler.setDefault(cookiesManager);
}
javaFxPanel = new JFXPanel();
labelStatus = new JLabel();
JPanel panelTodo = new JPanel(new BorderLayout());
JButton botonBuscar = new JButton("Search");
direction = new JTextField();
progressBar = new JProgressBar();
createScene();
ActionListener direcctionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
loadURL(direction.getText());
}
};
botonBuscar.addActionListener(direcctionListener);
direction.addActionListener(direcctionListener);
progressBar.setPreferredSize(new Dimension(150, 18));
progressBar.setStringPainted(true);
JPanel topBar = new JPanel(new BorderLayout(5, 0));
topBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
topBar.add(direction, BorderLayout.CENTER);
topBar.add(botonBuscar, BorderLayout.EAST);
JPanel statusBar = new JPanel(new BorderLayout(5, 0));
statusBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
statusBar.add(labelStatus, BorderLayout.CENTER);
statusBar.add(progressBar, BorderLayout.EAST);
panelTodo.add(topBar, BorderLayout.NORTH);
panelTodo.add(javaFxPanel, BorderLayout.CENTER);
panelTodo.add(statusBar, BorderLayout.SOUTH);
getContentPane().add(panelTodo);
setPreferredSize(new Dimension(1024, 600));
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
pack();
}
private void createScene() {
Platform.runLater(() -> {
view = new WebView();
engine = view.getEngine();
engine.setOnAlert((WebEvent<String> wEvent) -> {
System.out.println("JS alert() message: " + wEvent.getData());
});
engine.titleProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Navegador.this.setTitle(newValue);
}
});
}
});
engine.setOnStatusChanged(new EventHandler<WebEvent<String>>() {
#Override
public void handle(final WebEvent<String> event) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
labelStatus.setText(event.getData());
}
});
}
});
engine.locationProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String viejoValor, final String nuevoValor) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
direction.setText(nuevoValor);
}
});
}
});
engine.getLoadWorker().workDoneProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, final Number newValue) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressBar.setValue(newValue.intValue());
}
});
}
});
engine.getLoadWorker().exceptionProperty()
.addListener(new ChangeListener<Throwable>() {
#Override
public void changed(ObservableValue<? extends Throwable> o, Throwable old, final Throwable value) {
if (engine.getLoadWorker().getState() == FAILED) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//TODO Handling Exception
}
});
}
}
});
javaFxPanel.setScene(new Scene(view));
});
}
public void loadURL(String url) {
Platform.runLater(new Runnable() {
#Override
public void run() {
String urlTemporal = toURL(url);
if (urlTemporal == null) {
urlTemporal = toURL("http://" + url);
}
engine.load(urlTemporal);
}
});
}
private static String toURL(String str) {
try {
return new URL(str).toExternalForm();
} catch (MalformedURLException exception) {
return null;
}
}
}
My problem is that when i open the navigator for the first time it works , but when i close it and then open again it only shows the TextField and the Button
Notes:
I use a javaswing button to open the navigator whit the
openNavigator() method
When i left one Navigator without closing and at the same time i
open one or more Navigators it works perfectly
First time I open the Navigator:
Second time I open the Navigator:
The JavaFX thread/toolkit must be running. Apparently, creating a JFXPanel is enough to initialize it, but once it is closed the thread is terminated automatically.
To stop it from automatically closing call
Platform.setImplicitExit(false);
so the JavaFX toolkit only closes when the Platform#exit method is called, or the entire application terminates.
I write Java desktop app to fetch and post some data from my online rails backend app. The App have to call a get request every 5 second to update the relay state(example Arduino). here is my code:
public class GUI extends javax.swing.JFrame {
private Serial serial = null;
private Service service = null;
private volatile boolean connected = false;
private Thread updateThread;
public GUI() {
initComponents();
init_serial();
service = new Service();
updateThread = new Thread() {
public void run() {
while (connected) {
updateJob();
}
}
};
updateThread.start();
}
private void init_serial() {
serial = new Serial();
serial.searchForPorts();
serial.connect();
serial.initIOStream();
serial.initListener();
}
private void updateJob() {
ActionListener actListner = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
updateState();
}
};
Timer timer = new Timer(5000, actListner);
timer.start();
}
private void updateState() {
String portState = service.get_port_state();
serial.write(portState);
System.out.println(portState);
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
connected = true;
logger.setText(null);
logger.setText("connected");
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
logger.setText(null);
logger.setText("disconnected");
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new GUI().setVisible(true);
}
});
}
}
but it didn't work as expected, my question is how can i fix my code and how to put the thread correctly?
You can use a Thread object in class's member and you can start and stop in button click action events. Here is the sample to start/stop thread.
public class GUI extends javax.swing.JFrame {
Thread updateThread = null;
public GUI() {
JButton btnStart = new JButton("Start");
JButton btnStop = new JButton("Stop");
JPanel jPanel = new JPanel();
jPanel.setBounds(0, 0, 100, 200);
jPanel.add(btnStart);
jPanel.add(btnStop);
add(jPanel);
btnStart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
System.out.println("Work updated");
try {
Thread.sleep(1000);//Time to wait for next routine
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
updateThread.start();
}
});
btnStop.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateThread.stop();
}
});
setVisible(true);
setBounds(0, 0, 100, 200);
}
public static void main(String[] args) {
new GUI();
}
}
You can possibly use thread.join();
I am using swingworker to run a method in the background and periodically update the gui with information, but from what I've found publish can't be called from another class. Here's where my Swingworker is called:
private void start() {
worker = new SwingWorker <Void, String>() {
#Override
protected Void doInBackground() throws Exception {
navigator.navigator();
return null;
}
#Override
protected void process(List<String> chunks) {
for (String line : chunks) {
txtrHello.append(line);
txtrHello.append("\n");
}
}
#Override
protected void done() {
}
};
worker.execute();
}
And now from the navigator method I want to call publish(String);, how would I do this? Moving all of my methods into doInBackground() would be impossible.
Possible solution is to add an observer to your Navigator object, the key being to somehow allow the Navigator to communicate with any listener (here the SwingWorker) that its state has changed:
Give Navigator a PropertyChangeSupport object as well as an addPropertyChangeListener(PropertyChangeListener listener) method that adds the passed in listener to the support object.
Give Navigator some type of "bound" property, a field that when its state is changed, often in a setXxxx(...) type method, notifies the support object of this change.
Then in your SwingWorker constructor, add a PropertyChangeListener to your Navigator object.
In this listener, call the publish method with the new data from your Navigator object.
For example:
import java.awt.event.ActionEvent;
import java.beans.*;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class PropChangeSupportEg extends JPanel {
private MyNavigator myNavigator = new MyNavigator();
private JTextField textField = new JTextField(10);
public PropChangeSupportEg() {
textField.setFocusable(false);
add(textField);
add(new JButton(new StartAction("Start")));
add(new JButton(new StopAction("Stop")));
}
private class StartAction extends AbstractAction {
public StartAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
if (myNavigator.isUpdatingText()) {
return; // it's already running
}
MyWorker worker = new MyWorker();
worker.execute();
}
}
private class StopAction extends AbstractAction {
public StopAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
myNavigator.stop();
}
}
private class MyWorker extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
if (myNavigator.isUpdatingText()) {
return null;
}
myNavigator.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MyNavigator.BOUND_PROPERTY_TEXT.equals(evt.getPropertyName())) {
publish(evt.getNewValue().toString());
}
}
});
myNavigator.start();
return null;
}
#Override
protected void process(List<String> chunks) {
for (String chunk : chunks) {
textField.setText(chunk);
}
}
}
private static void createAndShowGui() {
PropChangeSupportEg mainPanel = new PropChangeSupportEg();
JFrame frame = new JFrame("Prop Change Eg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_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 MyNavigator {
public static final String BOUND_PROPERTY_TEXT = "bound property text";
public static final String UPDATING_TEXT = "updating text";
private static final long SLEEP_TIME = 1000;
private PropertyChangeSupport pcSupport = new PropertyChangeSupport(this);
private String boundPropertyText = "";
private String[] textArray = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
private int textArrayIndex = 0;
private volatile boolean updatingText = false;
public void start() {
if (updatingText) {
return;
}
updatingText = true;
while (updatingText) {
textArrayIndex++;
textArrayIndex %= textArray.length;
setBoundPropertyText(textArray[textArrayIndex]);
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {}
}
}
public void stop() {
setUpdatingText(false);
}
public String getBoundPropertyText() {
return boundPropertyText;
}
public boolean isUpdatingText() {
return updatingText;
}
public void setUpdatingText(boolean updatingText) {
boolean oldValue = this.updatingText;
boolean newValue = updatingText;
this.updatingText = updatingText;
pcSupport.firePropertyChange(UPDATING_TEXT, oldValue, newValue);
}
public void setBoundPropertyText(String boundPropertyText) {
String oldValue = this.boundPropertyText;
String newValue = boundPropertyText;
this.boundPropertyText = boundPropertyText;
pcSupport.firePropertyChange(BOUND_PROPERTY_TEXT, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
}
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) {}
}
}
}