Von Max Smolka

In den letzten Jahren wurde eine große Anzahl von Desktop-Anwendungen geschrieben, die sehr beliebt sind und eine enorme Benutzerfreundlichkeit aufweisen. Einige bekanntere Anwendungen darunter sind zum Beispiel Discord, Spotify, Atom, Slack und Visual Studio Code. All diese und weitere Anwendungen basieren auf einem Framework namens Electron.

Was ist Electron?

Electron ist eine Bibliothek, mit der Desktop-Anwendungen mit Hilfe von JavaScript, HTML und CSS erstellt werden können. Die Anwendungen können dann durch Zusatzpakete wie Electron-Packager für das gewünschte Betriebssystem gepackt werden. In der Regel werden Desktop-Anwendungen für jedes Betriebssystem in der jeweiligen Programmiersprache geschrieben. Mit Electron muss man die eigene Anwendung nur ein einziges Mail schreiben. Electron kombiniert Chromium und NodeJs mit einer Reihe von benutzerdefinierten APIs für native Betriebssystemfunktionen wie Dialoge zum Öffnen von Dateien, Benachrichtigungen, Symbole und vieles mehr.

In dieser Projektarbeit wurde eine kleine Anwendung zur Mitarbeiterverwaltung entworfen und implementiert.

Kundenverwaltung

Bild 1 von 3

Main- und Renderer-Prozess

Electron kennt zwei Arten von Prozessen: Main und Renderer. Jede Electron-Anwendung hat genau einen Main-Prozess, üblicherweise eine Datei mit dem Namen main.js oder index.js. Der Main-Prozess arbeitet hinter den Kulissen. Dieser verwaltet den Lebenszyklus einer Anwendung und öffnet die Fenster, die der Endnutzer sehen kann. Jedes Fenster ist ein eigener unabhängiger Renderer-Prozess, der mit der Chromium-Engine ausgeführt wird. Das kann zum Beispiel das Hauptfenster der Anwendung sein oder ein weiteres Fenster für das Hinzufügen von Kunden.

const {app, BrowserWindow} = require('electron');

app.whenReady().then(() => {

    //create a window
    const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true
        }
    });

    //load a webpage
    mainWindow.loadFile('index.html');
})

Ist der Main-Prozess bereit, kann der Renderer-Prozess gestartet werden. Dies geschieht, indem ein Browser-Window instanziiert wird und diesem mitgeteilt wird, welche HTML-Datei verwendet werden soll:

mainWindow.loadFile('index.html')

Der Inhalt der HTML-Datei ist jedem frei überlassen. CSS-Stylesheets und JavaScript-Dateien können wie bei einer gewöhnlichen HTML-Website eingebunden werden:

<!-- index.html -->

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Electron</title>
    <script src="renderer-process/render.js"></script>
    <style>
      @import url('./assets/css/base.css');
    </style>
</head>
<body>
    Ich werde reich!
</body>

Html, CSS und JavaScript werden dann in einem nativen Fenster gerendert.

Zusätzlich kümmert sich der Main-Prozess um weitere native Elemente. Beispielsweise können so – über nachfolgenden Code – native Elemente wie ein Dialog-Fenster aufgerufen werden.

  // delete customer from list
  ipcMain.on('delete-selected-customer', (event, customerId) => {

    let options = {
      type: 'question',
      buttons: ['Yes', 'No'],
      title: 'Confirm',
      message: 'Customer will be deleted. Are you sure?'
    }
    let responseCode = dialog.showMessageBoxSync(mainWindow, options)

    if (responseCode === 0) {
      const entry = customersData.customers[customerId]

      const updatedCustomers = customersData.deleteCustomer(entry).customers

      mainWindow.webContents.send('updated-customers', updatedCustomers)
    }

  })
};

Die Kommunikation
Der Main- und der Renderer-Prozess müssen miteinander kommunizieren können, da beide für unterschiedliche Aufgaben verantwortlich sind. Die Kommunikation erfolgt dabei mit Hilfe von Interprozesskommunikation (IPC, siehe ipcMain und ipcRenderer). Möchte man beim Start der Anwendung die Daten aus einer Datenbank laden und an den Renderer-Prozess übergeben, lässt sich das durch nachfolgendes Code-Beispiel leicht realisieren.

Im Main-Prozess

  // initialize with data stored
  mainWindow.once('ready-to-show', () => {
    mainWindow.webContents.send('updated-customers', customersData.customers)
    mainWindow.show()
  })

Im Renderer-Prozess

// On received signal from main-process, reload the content of the list
ipcRenderer.on('updated-customers', (event, updated_customers) => {

    // lastname
    // firstname
    // age
    let list = updated_customers.reduce((html, customer, index) => {
        // create html string
        html += `
        <li id="${index}">
            <span class="id">${index}</span>
            <span class="name">
                ${customer.lastname}, ${customer.firstname}
            </span>
            <span class="btn btn-delete" job='delete'>
                <i id="delete" class="fa fa-trash" aria-hidden="true"></i>
            </span>
        </li>
        `
        return html
    }, '')

    // add updated customers to the list element 
    ul_customers.innerHTML = list
});

Wie bei NodeJs Anwendungen wird auch bei Electron eine package.json-Datei verwendet, welche diverse Metadaten zum Projekt enthält sowie eine Liste von Abhängigkeiten.

Es gibt zahlreiche Boilerplates für Frameworks wie zum Beispiel React, Angular oder Vue. Diese Templates ermöglichen es ohne großen Aufwand bei der Entwicklung mit Electron einzusteigen. Ist die Anwendung fertig gestellt, ist es an der Zeit, sie für das gewünschte Betriebssystem zu verpacken. Beispiele finden sich online zu genüge:

Nachdem die App erfolgreich erstellt wurde, müssen Distributables für MacOS, Windows und Linux erstellt werden, welche die Endbenutzer herunterladen und verwenden können. Im Rahmen dieses Projekts wurde Electron-Packer verwendet. In der package.json-Datei finden sich plattformspezifische Anweisungen zum Packen der Anwendung.

    "package:win": "electron-packager . --overwrite --platform=win32 --out=build ",
    "package:linux": "electron-packager . --overwrite --platform=linux --out=build"

Mit Hilfe dieser ist die Erstellung der Distributables einfach über die Kommandozeile möglich:

npm run package:<plattform> 

Eine kleine Anwendung zur Mitarbeiterverwaltung, welche im Rahmen des Informatikprojektes erstellt wurde, kann unter nachfolgendem GitHub-Account heruntergeladen und installiert werden:

git clone https://github.com/maxsmolka/rwumanager.git
cd <folder name>
npm install
npm start

Eine kurze Anleitung, was alles in der Anwendung zu finden ist und an welcher Stelle, findet man im GitHub-Repository in der Datei Readme. Damit steht dem ungetrübten Desktop-Vergnügen nichts mehr im Wege!