java - MVC Progress Bar Threading
multithreading model-view-controller (1)
No llame
_progressBar.setValue(_model.getStatus());
desde SwingWorker, ya que este está llamando al código Swing de un hilo de fondo y es de lo que sirve el PropertyChangeListener de todos modos. En cambio, simplemente configure la propiedad de progreso, eso es todo.
Además, no llame a done () desde el método doInBackground, ya que SwingWorker debe llamarlo desde el EDT. Deje que SwingWorker llame a este método cuando esté hecho.
Además, Done () debe hacerse (): la primera letra no debe estar en mayúscula, y debe usar las anotaciones @Override en este código para asegurarse de que está sobrescribiendo los métodos correctamente.
Además, ¿qué hace esto?
_model.startSearch(_view.getTerm());
¿Llama al código que tarda un tiempo en completarse? ¿Debería inicializarse esto desde SwingWorker doInBackground?
Editar: Otra opción es darle al Modelo una propiedad int encuadernada, digamos llamada progreso, y luego agregarle un PropertyChangeListener dejándolo directamente al JProgressBar. Por ejemplo,
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) {}
}
}
}
Estoy usando un patrón MVC para mi diseño, cuando un usuario presiona el botón de búsqueda, llamo a una búsqueda en el modelo, pero también quiero actualizar una barra de progreso con información devuelta por ese modelo.
He intentado usar un swingworker, pero la barra de progreso no se actualiza. Sospecho que estoy haciendo algo mal con mi enhebrado.
Mi botón como se define en el controlador es:
class SearchBtnListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
_view.displayProgress();
}
}
Esto llama a la búsqueda en el modelo y tiene la siguiente llamada en la vista:
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();
}
}
Función simulada definida en el Modelo para la prueba:
public int getStatus(){
Random r = new Random();
return r.nextInt();
}