Comunicación entre Proceso Principal y Procesos de Renderizado

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.



Remote RPC

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()})

                



IPC Comunication

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

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

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

  • WebContets. ver Web-contents-sendchannel)) para más información.
  • Con el método event.sender.send(...) del propio ipcMain


  • 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


    Ejemplo de comunicación entre procesos

    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:



    Ejercicio

    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