Reputation: 87
Sorry if the question sounds stupid, I haven't been programming for too long, so I have this kind o newbie questions...
I have a JFrame (JFrame form from netBeans) with some variables(arrayList, int...). After a process, this variables change. The process is an mySQL query through JCDB driver, some arrayList update with data, which I use to fill a Jtable... and so on...
At first (poor me) I made a SwingWorker. Through the SwingWorker constructor, I pass these variables (about 6 variables more or less), and use them to do the processing and fill the tables.
I thought I would be able to update the value of these variables in the overriden Done() method (poor me again), and not just the GUI components.
I learned many things with this failure: 1) even though I passed the variables through the constructor, that doesn't mean they get updated in the place they come from. 2) SwingWorker can only return 1 variable, and modify GUI components.
So, and this is my main point, how can I do what I was trying to do? I learned it can't be done with SwingWorker class but then, how can it be done?
I don't want to put the code in an mouse click event just like that, because it would block my EDT with no notification to the user about what's happening.
I though about doing something like this: place the code inside a mouse click event, and show a Dialog so the user knows that a process is taking place at the moment.
private void jButton_calcular_rutaMousePressed(java.awt.event.MouseEvent evt) {
// BOTON CALCULAR RUTA
int index = jL_empGeo.getSelectedIndex();
JOptionPane optionPane = new JOptionPane("Descargando ruta, espere por favor.", JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, new Object[]{}, null);
JDialog dialog = new JDialog();
dialog.setTitle("Descarga");
dialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
dialog.setLocation(dim.width / 2 - dialog.getSize().width / 2, dim.height / 2 - dialog.getSize().height / 2);
dialog.setContentPane(optionPane);
dialog.pack();
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
//
// THE PROCESSING TAKES PLACE HERE
//
//
// mySQL query
//
// update arrayLists and variables
//
// update GUI components
//
//
dialog.setVisible(false);
}
Would this make any sense? is there any way to do the processing in a different Thread and afterwards get the arrayLists and variables back into the main thread?
Thanks in advance for your help.
EDIT
SwingWorker class
package descargas;
import clases.Empleados;
import clases.InfoPuntoRuta;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
import java.awt.Dimension;
import java.awt.Image;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;
import maps.java.Geocoding;
import maps.java.Route;
import maps.java.StaticMaps;
public class ActualizarRuta2 extends SwingWorker<Void, Void> {
int index;
ArrayList<Empleados> arrayEmpleados;
ArrayList<InfoPuntoRuta> listaPuntosRuta;
DefaultTableModel modeloTablaRutas;
JDialog dialog;
ArrayList<String> listaIntermedios;
JLabel jLab_RutaMAP;
ImageIcon icono;
int origenInt;
int destinoInt;
int destinoMax;
public ActualizarRuta2(int index, int origenInt, int destinoInt, int destinoMax, ArrayList<Empleados> arrayEmpleados, ArrayList<InfoPuntoRuta> listaPuntosRuta, DefaultTableModel modeloTablaRutas, JDialog dialog, JLabel jLab_RutaMAP) {
this.index = index;
this.arrayEmpleados = arrayEmpleados;
this.listaPuntosRuta = listaPuntosRuta;
this.modeloTablaRutas = modeloTablaRutas;
this.dialog = dialog;
this.jLab_RutaMAP = jLab_RutaMAP;
this.origenInt = origenInt;
this.destinoInt = destinoInt;
this.destinoMax = destinoMax;
}
@Override
protected Void doInBackground() throws Exception {
listaIntermedios = new ArrayList<String>();
// recogemos la fecha actual
DateFormat formatoFecha = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
String dateMIN = formatoFecha.format(cal.getTime()) + " 00:00:01";
String dateMAX = formatoFecha.format(cal.getTime()) + " 23:59:59";
// GENERAR MAPA DE LA RUTA DEL DIA
Connection conexion;
conexion = conexiondb.ConexionDB.getConnection();
if (conexion != null) {
try {
Statement st;
ResultSet rs;
st = (Statement) conexion.createStatement();
rs = st.executeQuery("SELECT * \n"
+ "FROM position\n"
+ "WHERE nombre = '" + arrayEmpleados.get(index) + "'\n"
+ "AND position_date BETWEEN '" + dateMIN + "' AND '" + dateMAX + "'\n"
+ "ORDER BY position_date;");
// vaciamos la lista
//listaPuntosRuta.clear();
//System.out.println(rs.);
// rellenamos la lista
rs.beforeFirst();
while (rs.next()) {
InfoPuntoRuta punto = new InfoPuntoRuta(rs.getString("nombre"),
rs.getString("position_date"), rs.getDouble("latitud"),
rs.getDouble("longitud"));
Geocoding ObjGeocod = new Geocoding();
ArrayList<String> resultadoCI = ObjGeocod.getAddress(punto.getLatitud(), punto.getLongitud());
String direccion = resultadoCI.get(0);
punto.setDireccion(direccion);
listaIntermedios.add(direccion);
listaPuntosRuta.add(punto);
}
//
// DESCARGAR MAPA RUTA
//
int posicionUltimo = (listaIntermedios.size()) - 1;
String origen, destino;
ArrayList<String> waypoints = new ArrayList<String>();
if (listaIntermedios.size() < 10) {
origen = listaIntermedios.get(0);
destino = listaIntermedios.get(posicionUltimo);
for (int i = 1; i < posicionUltimo; i++) {
waypoints.add(listaIntermedios.get(i));
}
} else {
origen = listaIntermedios.get(0);
destino = listaIntermedios.get(9);
for (int i = 1; i < 9; i++) {
waypoints.add(listaIntermedios.get(i));
}
}
// ArrayList<String> prueba = new ArrayList<String>();
// prueba.add("coruña avenida finisterre 65");
// prueba.add("coruña ronda de outeiro 125");
// prueba.add("coruña avenida del ejercito 20");
Route ObjRout = new Route();
String[][] resultadoRuta = ObjRout.getRoute(origen, destino, waypoints, Boolean.TRUE, Route.mode.driving, Route.avoids.nothing);
//String[][] resultadoRuta = ObjRout.getRoute("Madrid", "Barcelona", prueba, Boolean.TRUE, Route.mode.driving, Route.avoids.nothing);
// String[][] resultadoRuta = ObjRout.getRoute("coruña virrey ossorio 25", "pla y cancela, 16, 15005 la coruña, españa", prueba, Boolean.TRUE, Route.mode.driving, Route.avoids.nothing);
String polylinea = ObjRout.getGeneralPolyline();
StaticMaps ObjStatMap = new StaticMaps();
Image resultadoMapa = ObjStatMap.getStaticMapRoute(new Dimension(585, 405), 1, StaticMaps.Format.png, StaticMaps.Maptype.hybrid, polylinea);
icono = new ImageIcon(resultadoMapa);
// RELLENAR TABLA DETALLES DE LA RUTA
modeloTablaRutas.setRowCount(0);
} catch (SQLException | UnsupportedEncodingException | MalformedURLException ex) {
Logger.getLogger(ActualizarRuta2.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
conexion.close();
} catch (SQLException ex) {
Logger.getLogger(ActualizarRuta2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return null;
}
@Override
protected void done() {
dialog.dispose();
// RELLENAR TABLA DETALLES DE LA RUTA
for (InfoPuntoRuta p : listaPuntosRuta) {
modeloTablaRutas.addRow(new Object[]{
p.getNombre(), p.getFecha(), p.getDireccion()});
}
// ponemos la imagen de la ruta
jLab_RutaMAP.setIcon(icono);
//
listaIntermedios.clear();
// listaPuntosRuta.clear();
origenInt = 0;
destinoMax = listaPuntosRuta.size() - 1;
if (destinoMax < 9) {
destinoInt = destinoMax;
} else {
destinoInt = 9;
}
}
}
Mouse event where I call the SwingWorker
private void jButton_calcular_rutaMousePressed(java.awt.event.MouseEvent evt) {
// BOTON CALCULAR RUTA
int index = jL_empGeo.getSelectedIndex();
JOptionPane optionPane = new JOptionPane("Descargando ruta, espere por favor.", JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, new Object[]{}, null);
JDialog dialog = new JDialog();
dialog.setTitle("Descarga");
dialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
dialog.setLocation(dim.width / 2 - dialog.getSize().width / 2, dim.height / 2 - dialog.getSize().height / 2);
dialog.setContentPane(optionPane);
dialog.pack();
dialog.setLocationRelativeTo(this);
listaPuntosRuta.clear();
modeloTablaRutas.setRowCount(0);
ActualizarRuta2 task = new ActualizarRuta2(index, origenInt, destinoInt, destinoMax, arrayEmpleados, listaPuntosRuta, modeloTablaRutas, dialog, jLab_RutaMAP);
task.execute();
dialog.setVisible(true);
}
Upvotes: 0
Views: 591
Reputation: 14154
You need communication from your 'SwingWorker', back to the GUI. Java parameters are pass by value -- so therefore, you need some form of value holder.
Making a 'data bean' descending from Observable, is probably the simplest way.
public class QueryResults extends Observable {
protected List<SomeItem> itemList;
// getters and setters.
}
From your SwingWorker, you need to store the results & notify observers. Notification must be done on the event-dispatch thread, and javax.swing.SwingWorker provides built-in functionality to call SwingWorker.done() on the EDT.
I have used 'Void' types as a "result", since the data-holder already exists & we are just loading data into it. The UI should already be bound to (and observing) it. Something like this:
public class QueryWorker<Void,Void> extends SwingWorker {
protected QueryResults holder;
public QueryWorker (QueryResults holder) {
this.holder = holder; // keep the holder & put data into it.
}
@Override
protected Void doInBackground() {
// run the query..
}
@Override
protected void done() {
holder.notifyObservers();
}
}
This is a simplified example. It omits the possibility, for example, of the UI wanting to read the result-hodler (for display refresh) in the middle of loading. Your alternative there is to keep the data bean separate from the observable reference to the bean.
Upvotes: 2
Reputation: 1885
is there any way to do the processing in a different Thread and afterwards get the arrayLists and variables back into the main thread?
Yes, there is. You can (and probably should) start a new thread after dialog.setVisible(true)
using
new Thread() {
@Override
public void run() {
getNewValues();
}
}.start();
Once this job is done, from your "worker"-thread, you can invoke
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
//overwrite existing variables, update GUI elements, trigger repaints, etc.
dialog.setVisible(false);
}
};
However, you should always try to separate your data from your view. That means: You probably should not invoke those actions from your worker thread, but implement the observer pattern to inform your GUI that the data has changed and to refresh itself.
Upvotes: 1