[Gvsig_desarrolladores] Error creando ventana nueva en ProjectTable.createWindow()

Joaquin del Cerro jjdelcerro.gvsig en gmail.com
Lun Ago 6 10:42:46 CEST 2012


El 03/08/12 18:15, Alberto Calzada escribió:
> Hola a todos,
> 
> Estoy desarrollando una extensión para gvSIG 1.11 y llevo un bueno rato
> intentando averiguar el porqué de este error. He mirado por Internet y
> también antiguos mensajes en las listas de correo pero no he encontrado
> nada al respecto, y para ser sincero, no sé a qué se debe.
> Este es el error en tiempo de ejecución que me salta siempre:
> 
> Exception in thread "Thread-8" java.lang.RuntimeException: No Event
> Dispatch Thr
> 
> ead
> 
>         at
> com.iver.andami.ui.mdiFrame.MDIFrame.enableControls(MDIFrame.java:897
> 
> )
> 
>         at
> com.iver.cit.gvsig.project.documents.table.gui.Table.updateSelection(
> 
> Table.java:340)
> 
>         at
> com.iver.cit.gvsig.project.documents.table.gui.Table.refreshControls(
> 
> Table.java:257)
> 
>         at
> com.iver.cit.gvsig.project.documents.table.gui.Table.setModel(Table.j
> 
> ava:293)
> 
>         at
> com.iver.cit.gvsig.project.documents.table.ProjectTable.createWindow(
> 
> ProjectTable.java:969)
> 
>         at
> model.spatial.GISOutputExporterCSV.closeFile(GISOutputExporterCSV.jav
> 
> a:337)
> 
>         at model.core.Execution$ActualTask.<init>(Execution.java:243)
> 
>         at model.core.Execution$2.construct(Execution.java:88)
> 
>         at model.threadExec.SwingWorker$2.run(SwingWorker.java:60)
> 
>         at java.lang.Thread.run(Unknown Source)
> 
>  Mi código es el siguiente (en GISOutputExporterCSV.java):
> 
>         ProjectExtension ext = (ProjectExtension)
> PluginServices.getExtension(ProjectExtension.class);
> try {
> LayerFactory.getDataSourceFactory().addFileDataSource("csv string",
> tableFile.getName(), tableFile.getAbsolutePath());
>  DataSource dataSource =
> LayerFactory.getDataSourceFactory().createRandomDataSource(tableFile.getName(),
> DataSourceFactory.AUTOMATIC_OPENING);
> 
> dataSource.setDataSourceFactory(LayerFactory.getDataSourceFactory());
>         SelectableDataSource sds = new SelectableDataSource(dataSource);
>         EditableAdapter auxea=new EditableAdapter();
>         auxea.setOriginalDataSource(sds);
> String docName = "RIMER Results in: "+tableFile.getName();
>     projectTable = ProjectFactory.createTable(docName, auxea);
>     projectTable.setModel(auxea);
>         ext.getProject().addDocument(projectTable);
> 
>         IWindow win = projectTable.createWindow();
>          PluginServices.getMDIManager().addWindow(win);
> 
> Donde tableFile es del tipo java.io.File.
> 
> El caso es que la tabla CSV se añade correctamente al proyecto, y además
> puedo abrirla sin problema ninguno manualmente después de que este error
> salte, pero quisiera que la tabla apareciera automáticamente después de que
> se acabara la ejecución de mi extensión.
> 
> He intentado varias formas de evitar el "createWindow", que parece el
> origen del problema (Como crear un Table "t" y hacer un
> "t.setModel(projectTable)" pero entonces el error me salta en este método
> (que como podéis ver está también en el stack del error en tiempo de
> ejecución del "createWindow").
> 


Hola Alberto,
mirando el stack de ejecucion tiene pinta de ser un problema de sincronicacion
entre hilos de ejecucion tuyos y el de swing.
Este tipo de errores suelen ser errores bastante comunes. Solo hay que tratarlos
con algo de cuidado. Estan relacionados con como se lleva swing y el multihilo.
Swing no esta pensado para trabajar en un entorno multihilo asi sin mas. Todos
los eventos de swing deben ejecutarse en el hilo de swing. Si tu creas tu propio
hilo y quieres interactuar con el interface grafico debes protegerte de alguna
manera para asegurarte que todas las acciones que hagas relacionadas con el GUI
se ejecuten en el thread de swing.

Mi primera recomendacion seria que separes la logica de tu proceso del acceso al
GUI, pero a veces eso puede ser bastante complejo. La otra, asegurarte que las
operaciones relacionadas con el GUI se realizan en el hilo de swing, que aunque
parezca algo complejo es relativamente simple.

En el API de swing tienes tres metodos muy utiles para todo esto:

- SwingUtilities.isEventDispatchThread, que nos dice si ya estamos ejecutandonos
  en el hilo de swing

- SwingUtilities.invokeLater(Runnable action) que se encarga de encolar la
  ejecucion de "action" en el hilo de swing para que se ejecute cuando este pueda.

- SwingUtilities.invokeAndWait(Runnable action) que se encarga de encolar la peticion
  en el hilo de swing y esperar a que esta se ejecute.

Con estos tres metodos es facil asegurarnos que las partes que tengan que ver con
GUI de nuestros procesos se ejecuten siempre en el hilo de swing. Asi por ejemplo
podriamos hacer algo como:

  final Project project = ext.getProject();
  final EditableAdapter auxea=new EditableAdapter();
  final String docName = "RIMER Results in: "+tableFile.getName();

  auxea.setOriginalDataSource(sds);
  if( !SwingUtilities.isEventDispatchThread() ) {
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          projectTable = ProjectFactory.createTable(docName, auxea);
          projectTable.setModel(auxea);
          project.addDocument(projectTable);
          IWindow win = projectTable.createWindow();
          PluginServices.getMDIManager().addWindow(win);
        }
      });
  }

He usado el InvokeLater para encolar la accion de actualizar el proyecto y crear
la ventana de la tabla sin esperar a que esto se produzca. Si precisas esperar
a que esto se produzca para seguir haciendo cosas puedes cambiar el
InvokeLater por un InvokeAndWait.

Lo he picado directamente en el mensaje de correo, asi que no se si he podido
cometer algun error de sintaxis, si es asi disculpas por ello.


> El caso es que no sé a qué se puede deber este error. Si al menos pudiera
> encontrar el motivo por el que en el método "enableControls" de la clase
> org.gvsig.andami.ui.mdiFrame salta el runtime error en este bloque:
> 
> *public* *void* enableControls() {
> 
>       *if* (!*SwingUtilities*.isEventDispatchThread()) {
> 
> *             throw* *new* *RuntimeException*("No Event Dispatch Thread");
> 
> }
> 
> 
> ¿Alguien tiene alguna idea de porqué me
> !SwingUtilities.isEventDispatchThread() en este caso?
> 
> Cualquier opinión, consejo o ayuda es más que bienvenido, porque esto me
> tiene un poco atascado!
> 


Precisamente aqui, el metodo enableControls, lo que esta haciendo es protegerse
contra que puedas invocar al metodo enableControls desde un hilo que no sea el
de swing. En la version 2 de gvSIG se ha mejorado el soporte multihilo en gvSIG
de forma que muchos metodos, este incluido, se ejecutan automaticamente
en el hilo de swing, sincronizandose ellos mismos sin tener que hacerlo
tu desde fuera. Supongo que aun quedaran muchos por tocar, pero estamos
en ello.

Espero que te sirva de algo el comentario.

Por otro lado estoy terminando un pequeño post para el blog de gvSIG
donde habla un poco de todo esto, a ver si lo termino en los proximos
dias.

Un saludo
Joaquin


> Muchísimas gracias de antemano a todos!
> 
> Un saludo
> 
> Alberto
> 
> 
> 
> 
> 
> _______________________________________________
> gvSIG_desarrolladores mailing list
> gvSIG_desarrolladores en listserv.gva.es
> Para ver histórico de mensajes, editar sus preferencias de usuario o darse de baja en esta lista, acuda a la siguiente dirección: http://listserv.gva.es/cgi-bin/mailman/listinfo/gvsig_desarrolladores


-- 
--------------------------------------
Joaquin Jose del Cerro
Development and software arquitecture manager.
jjdelcerro en gvsig.com
gvSIG Association
www.gvsig.com
www.gvsig.org


Más información sobre la lista de distribución gvSIG_desarrolladores