En Electron hay varias formas de establecer una comunicación entre el proceso principal y el proceso renderizador, como los módulos ipcRenderer y ipcMain para enviar mensajes, y el módulo remote para una comunicación de tipo RPC.
En este apartado veremos algunos ejemplos de comunicación entre procesos utilizando ambos métodos.
Para la comunicación entre el Principal y el de Renderizado de tipo RCP ( Remote Procedure Call ), ElectronJs provee una funcionalidad definida en el módulo remote, Módulo remote
En electron, los módulos relacionados con GUI (como dialog, menu etc.) están solamente disponibles en el proceso principal, no en el proceso de renderizado.
Para usarlos en el proceso de renderizado se puede utilizar el módulo remote, con el que se pueden invocar métodos del objeto del proceso principal sin enviar explícitamente mensajes entre procesos. Con este método también podemos
usar funciones específicas creadas en el Proceso Principal.
La llamada a remote se hace con el siguiente código en nuestro Proceso de Renderizado:
const remote = require('electron').remote
También se puede instanciar, invocando al método de ElectronJs que necesitemos, de la siguiente manera:
const { BrowserWindow } = require('electron').remote
Como hemos indicado, otra de los métodos que podemos invocar desde nuestros Procesos de Renderizado es el método Menu. Podemos utilizar remote
para la creación de Menús Contextuales en nuestro proceso de Renderizado.
Veamos la siguiente propuesta:
//invocamos a la Api de electron y a los métodos requeridos
const {remote} = require('electron')
const { Menu, MenuItem } = remote
//creamos una nueva instancia de menu y la añadimos nuevos elementos, uno a uno
const menu = new Menu()
menu.append(new MenuItem({ label: 'El Tiempo', click() { console.log('item 1 clicked') } }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: 'Avisos metereológicos', type: 'checkbox', checked: false }))
//prevenimos el comportamiento por defecto del clic derecho del ratón.
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
menu.popup({ window: remote.getCurrentWindow() })
}, false)
Las funciones específicas creadas en el Proceso Principal que se quieran ejecutar desde algún Proceso de Renderizado se han de instanciar con exports, indicando de esta forma
que son funciones exportables.
exports.openWindow=()=>{
let newWin =new BrowserWindow({
width: 400,
height: 200,
webPreferences: {
nodeIntegration: true
}
})
newWin.loadFile('./src/iss.html')
newWin.on('closed', () => {
// Elimina la referencia al objeto window, normalmente guardarías las ventanas
// en un vector si tu aplicación soporta múltiples ventanas, este es el momento
// en el que deberías borrar el elemento correspondiente.
win = null
})
}
Así las podemos usar en nuestro Proceso de Renderizado
const remote = require('electron').remote
//creamos esta variable para comunicarnos con el Proceso Principal
const main = remote.require('./main.js')
let button = document.createElement('button')
button.textContent='Abrir Ventana'
document.body.appendChild(button)
button.addEventListener('click', ()=>{main.openWindow()})
ElectronJs provee distintas alternativas para la comunicación mediante el pase de mensajes entre el Proceso Principal y los distintos Procesos de Renderizado. Fundamentalmente encontramos dos módulos específicos, un módulo para el Proceso Principal ipcMain y un módulo para los Procesos de Renderizado ipcRenderer. A continuación pasamos a explicar el funcionamiento de cada módulo ilustrándolo con un sencillo ejemplo.
ipcRenderer (Módulo ipcRenderer) comunica por defecto de forma asincrona un Proceso de Renderizado con el Proceso Principal. Proporciona también métodos para la comunicación síncrona,
aunque nosostros, para este tutorial, vamos a desarrollar un ejemplo de comunicación Asíncrona.
Para poder utilizarlo hay que integrarlo en el Proceso
const {ipcRenderer} =require('electron')
En el ejemplo, vamos a utilizar dos métodos:
ipcRenderer.on(channel, listener)
Este método sirve para escuchar los mensajes que haya en un determinado canal, chanel
donde chanel, es el canal por el cual escuchar los mensajes y listerner es una función con los siguientes argumentos, event que es el evento del Proceso de Comunicación y args se corresponde con el mensaje recibido
, que puede estar serializado en un archivo json
Para el envío de mensajes ipcRenderer facilita el siguiente método:
ipcRenderer.send(channel,args)
donde chanel, es el canal por el cual enviar los mensajes al Proceso Principal y args es el mensaje enviado, que puede estar serializado en un archivo json
ipcMain (Módulo ipcMain) se comunica de forma asincrónica desde el proceso principal a los procesos de renderizado. Proporciona también métodos para la comunicación síncrona,
aunque nosostros, para este tutorial, vamos a desarrollar un ejemplo de comunicación Asíncrona. También es posible enviar mensajes desde el proceso principal al proceso de renderizado, a través de distintos métodos
Para poder utilizarlo hay que integrarlo en el Proceso
const {ipcMain} =require('electron')
En el ejemplo, vamos a utilizar dos métodos:
ipcMain.on(channel, listener)
Este método sirve para escuchar los mensajes que haya en un determinado canal, chanel
donde chanel, es el canal por el cual escuchar los mensajes y listerner es una función con los siguientes argumentos, event que es el evento del Proceso de Comunicación y args se corresponde con el mensaje recibido
, que puede estar serializado en un archivo json
Para el envío de mensajes ipcRenderer facilita el siguiente método:
event.sender.send(channel, args)
donde chanel, es el canal por el cual enviar los mensajes al Proceso Principal y args es el mensaje enviado, que puede estar serializado en un archivo json
Vamos a realizar un ejemplo en el que el Proceso de Renderizado, envía un mensaje al Proceso Principal cuando pulsamos un botón y este devuelve un mensaje al Proceso de Renderizado que lo imprimirá
en la propio vista de la ventana corresondiente al Proceso de Renderizado.
Proceso de Renderizado
<button class="boton_personalizado" id="btnEnvio" onclick='clickThing()'>Envío de mensaje!!!!</button>
<div id="respuesta"></div>
<script>
function clickThing(){
//enviamos por el canal 'ping' un mensaje. También podemos enviar una variable
ipcRenderer.send('ping', 'hola Proceso Principal');
}
//escuchamos el canal de respuesta esperando al Proceso Principal
ipcRenderer.on('respuesta', (event, arg)=>{ //esperamos respuesta y recibimos la variable en arg
//imprimirmos la respuesta en el div con id respuesta
document.getElementById('respuesta').innerHTML=arg;
})
</script>
Proceso Principal
//se recibe en el arg la variable enviada por renderder a través del canal ping
ipcMain.on('ping', (event, arg)=>{
//enviamos por el canal respuesta, la respuesta del Proceso Principal
event.sender.send('respuesta', 'Soy el Proceso Principal y como respuesta a tu mensaje, te envío hola');
})
El resultado lo vemos en la siguiente imagen:
El ejercicio consiste en implementar otro botón para llamar a otra ventana en la que añadimos información acerca de nosotros mismos o de lo que queramos.
También deseamos enviar un mensaje al Proceso Principal y que nos respuesta devolviéndonos una variable cuyo contenido imprimiermos en la Ventana correspondiente al Proceso de Renderizado que ha enviado el mensaje.
Nota: con el procedimiento remote podemos invocar funciones propias de main en nuestro propio Proceso de Renderizado como el método BrowserWindow
La solución en nuestro en Github Tutorial Electron