Skip to content

Kapitel 7: Zugriff auf Electron-native Funktionen

In diesem Kapitel lernen Sie, wie Sie auf native Betriebssystem-Funktionen in Electron zugreifen - einer der Hauptvorteile von Electron.

7.1 Desktop-Benachrichtigungen (Notification-Modul)

Electron ermöglicht das Senden von Systembenachrichtigungen.

Grundlegende Benachrichtigung erstellen

javascript
// Im Renderer-Prozess (mit nodeIntegration: true)
const { Notification } = require('electron');

// Einfache Benachrichtigung
const notification = new Notification({
  title: 'Meine Benachrichtigung',
  body: 'Dies ist eine Nachricht an den Benutzer.'
});

notification.show();

Benachrichtigung konfigurieren

javascript
const notification = new Notification({
  title: 'Titel der Benachrichtigung',       // Titel
  body: 'Dies ist der Inhalt der Nachricht.', // Inhalt
  icon: 'path/to/icon.png',                  // Icon (optional)
  silent: false,                             // Ton abspielen?
  timeoutType: 'default',                     // 'default' oder 'never'
  urgency: 'normal'                          // 'normal', 'critical', 'low' (Linux)
});

notification.show();

Benachrichtigungs-Klickereignis verarbeiten

javascript
const { Notification } = require('electron');

const notification = new Notification({
  title: 'Neue Nachricht',
  body: 'Klicken Sie, um zu antworten.'
});

// Klick-Event
notification.on('click', () => {
  console.log('Benachrichtigung geklickt!');
  // Fenster in den Vordergrund bringen
  mainWin.focus();
  mainWin.show();
});

// Benachrichtigung anzeigen
notification.show();

Vollständiges Beispiel: Benachrichtigungssystem

javascript
// main.js oder über IPC vom Renderer aufrufen
const { Notification, ipcMain } = require('electron');

// IPC-Handler für Benachrichtigungen
ipcMain.handle('show-notification', async (event, { title, body, icon }) => {
  const notification = new Notification({
    title: title || 'Benachrichtigung',
    body: body || '',
    icon: icon || undefined
  });

  notification.on('click', () => {
    const win = BrowserWindow.fromWebContents(event.sender);
    if (win) {
      win.focus();
      win.show();
    }
  });

  notification.show();
  return { success: true };
});
javascript
// preload.js
contextBridge.exposeInMainWorld('notifyAPI', {
  showNotification: (title, body) => 
    ipcRenderer.invoke('show-notification', { title, body })
});
html
<!-- index.html -->
<script>
  async function sendNotification() {
    await window.notifyAPI.showNotification(
      'Hallo!',
      'Dies ist eine Electron-Benachrichtigung.'
    );
  }
</script>

<button onclick="sendNotification()">Benachrichtigung senden</button>

7.2 Systemmenüs und Tray (Menu-, Tray-Module)

Anwendungsmenü erstellen (obere Menüleiste)

javascript
// main.js
const { app, Menu, BrowserWindow } = require('electron');

function createWindow() {
  const win = new BrowserWindow({ /* ... */ });
  win.loadFile('index.html');
}

// Menüvorlage definieren
const menuTemplate = [
  {
    label: 'Datei',
    submenu: [
      {
        label: 'Neu',
        accelerator: 'CmdOrCtrl+N',
        click: () => {
          console.log('Neu ausgewählt');
        }
      },
      {
        label: 'Öffnen',
        accelerator: 'CmdOrCtrl+O',
        click: async () => {
          const result = await dialog.showOpenDialog({ /* ... */ });
          console.log(result.filePaths);
        }
      },
      { type: 'separator' },
      {
        label: 'Beenden',
        accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
        click: () => {
          app.quit();
        }
      }
    ]
  },
  {
    label: 'Bearbeiten',
    submenu: [
      { label: 'Rückgängig', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
      { label: 'Wiederholen', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' },
      { type: 'separator' },
      { label: 'Ausschneiden', accelerator: 'CmdOrCtrl+X', role: 'cut' },
      { label: 'Kopieren', accelerator: 'CmdOrCtrl+C', role: 'copy' },
      { label: 'Einfügen', accelerator: 'CmdOrCtrl+V', role: 'paste' }
    ]
  },
  {
    label: 'Ansicht',
    submenu: [
      { label: 'Neu laden', accelerator: 'CmdOrCtrl+R', role: 'reload' },
      { label: 'Entwicklerwerkzeuge', accelerator: 'F12', role: 'toggleDevTools' }
    ]
  }
];

// Menü erstellen und setzen
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);

app.whenReady().then(createWindow);

Kontextmenü (Rechtsklick-Menü)

javascript
// main.js oder preload.js
const { Menu, ipcMain } = require('electron');

// Kontextmenü im Renderer anzeigen
ipcMain.on('show-context-menu', (event) => {
  const template = [
    {
      label: 'Kopieren',
      click: () => {
        // Kopieraktion
      }
    },
    {
      label: 'Einfügen',
      click: () => {
        // Einfügeaktion
      }
    },
    { type: 'separator' },
    {
      label: 'Inspektieren',
      click: () => {
        // Entwicklerwerkzeuge öffnen
        BrowserWindow.fromWebContents(event.sender).webContents.openDevTools();
      }
    }
  ];

  const menu = Menu.buildFromTemplate(template);
  menu.popup(BrowserWindow.fromWebContents(event.sender));
});
html
<!-- index.html -->
<script>
  document.addEventListener('contextmenu', (e) => {
    e.preventDefault();
    window.api.sendMessage('show-context-menu');
  });
</script>

System-Tray erstellen (Desktop-Symbolleiste)

javascript
// main.js
const { app, Tray, Menu, BrowserWindow } = require('electron');
const path = require('path');

let tray = null;
let mainWin;

function createWindow() {
  mainWin = new BrowserWindow({ /* ... */ });
  mainWin.loadFile('index.html');
}

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

  // Tray-Icon erstellen
  tray = new Tray(path.join(__dirname, 'assets', 'icon.png'));

  // Tray-Menüvorlage
  const trayMenuTemplate = [
    {
      label: 'Fenster anzeigen',
      click: () => {
        if (mainWin) {
          mainWin.show();
          mainWin.focus();
        }
      }
    },
    {
      label: 'Benachrichtigung senden',
      click: () => {
        new Notification({
          title: 'Vom Tray',
          body: 'Tray-Icon wurde geklickt!'
        }).show();
      }
    },
    { type: 'separator' },
    {
      label: 'Beenden',
      click: () => {
        app.quit();
      }
    }
  ];

  const trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
  tray.setContextMenu(trayMenu);

  // Tray-Tooltip
  tray.setToolTip('Meine Electron-App');

  // Tray-Klick-Event
  tray.on('click', () => {
    if (mainWin.isVisible()) {
      mainWin.hide();
    } else {
      mainWin.show();
      mainWin.focus();
    }
  });
});

Tray-Menü-Klickereignisse verarbeiten

javascript
// Tray-Ereignisse
tray.on('click', () => {
  console.log('Tray geklickt');
  mainWin.show();
});

tray.on('right-click', () => {
  console.log('Tray rechts geklickt');
  tray.popUpContextMenu();
});

tray.on('double-click', () => {
  console.log('Tray doppelt geklickt');
  mainWin.show();
});

7.3 Dateisystemoperationen (Node.js fs-Modul kombiniert mit lokalem Zugriff)

Dateilesen, Schreiben, Löschen (im Hauptprozess ausführen)

javascript
// main.js
const fs = require('fs').promises;
const { ipcMain } = require('electron');

// Datei lesen
ipcMain.handle('read-file', async (event, filePath) => {
  try {
    const content = await fs.readFile(filePath, 'utf-8');
    return { success: true, content };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Datei schreiben
ipcMain.handle('write-file', async (event, filePath, content) => {
  try {
    await fs.writeFile(filePath, content, 'utf-8');
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Datei löschen
ipcMain.handle('delete-file', async (event, filePath) => {
  try {
    await fs.unlink(filePath);
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Verzeichnis auflisten
ipcMain.handle('list-directory', async (event, dirPath) => {
  try {
    const files = await fs.readdir(dirPath);
    return { success: true, files };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

Dateidialoge (dialog-Modul, Datei öffnen/speichern)

javascript
// main.js
const { dialog, ipcMain, BrowserWindow } = require('electron');

// Datei öffnen Dialog
ipcMain.handle('open-file-dialog', async (event) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  
  const result = await dialog.showOpenDialog(win, {
    title: 'Datei öffnen',
    defaultPath: '.',
    buttonLabel: 'Öffnen',
    filters: [
      { name: 'Textdateien', extensions: ['txt', 'md'] },
      { name: 'Alle Dateien', extensions: ['*'] }
    ],
    properties: [
      'openFile',
      // 'openDirectory',  // Verzeichnis auswählen
      // 'multiSelections'  // Mehrfachauswahl
    ]
  });

  if (!result.canceled && result.filePaths.length > 0) {
    return { success: true, filePath: result.filePaths[0] };
  }
  return { success: false, error: 'Abgebrochen' };
});

// Datei speichern Dialog
ipcMain.handle('save-file-dialog', async (event, defaultName) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  
  const result = await dialog.showSaveDialog(win, {
    title: 'Datei speichern',
    defaultPath: defaultName || 'untitled.txt',
    buttonLabel: 'Speichern',
    filters: [
      { name: 'Textdateien', extensions: ['txt'] },
      { name: 'Markdown', extensions: ['md'] },
      { name: 'Alle Dateien', extensions: ['*'] }
    ]
  });

  if (!result.canceled) {
    return { success: true, filePath: result.filePath };
  }
  return { success: false, error: 'Abgebrochen' };
});

// Verzeichnis auswählen Dialog
ipcMain.handle('select-directory-dialog', async (event) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  
  const result = await dialog.showOpenDialog(win, {
    title: 'Verzeichnis auswählen',
    properties: ['openDirectory']
  });

  if (!result.canceled && result.filePaths.length > 0) {
    return { success: true, dirPath: result.filePaths[0] };
  }
  return { success: false, error: 'Abgebrochen' };
});

Vollständiges Beispiel: Datei-Auswahl und -Lesen

javascript
// preload.js
contextBridge.exposeInMainWorld('fileAPI', {
  openFile: () => ipcRenderer.invoke('open-file-dialog'),
  saveFile: (defaultName) => ipcRenderer.invoke('save-file-dialog', defaultName),
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
  writeFile: (filePath, content) => ipcRenderer.invoke('write-file', filePath, content)
});
html
<!-- index.html -->
<input type="text" id="filePath" placeholder="Dateipfad" readonly>
<button onclick="selectFile()">Durchsuchen...</button>
<br>
<textarea id="fileContent" placeholder="Dateiinhalt" style="width:100%; height:200px;"></textarea>
<br>
<button onclick="saveFile()">Speichern</button>

<script>
  let currentFilePath = null;

  async function selectFile() {
    const result = await window.fileAPI.openFile();
    if (result.success) {
      currentFilePath = result.filePath;
      document.getElementById('filePath').value = currentFilePath;

      const readResult = await window.fileAPI.readFile(currentFilePath);
      if (readResult.success) {
        document.getElementById('fileContent').value = readResult.content;
      }
    }
  }

  async function saveFile() {
    if (!currentFilePath) {
      const result = await window.fileAPI.saveFile('neue_datei.txt');
      if (result.success) {
        currentFilePath = result.filePath;
      } else {
        return;
      }
    }

    const content = document.getElementById('fileContent').value;
    const saveResult = await window.fileAPI.writeFile(currentFilePath, content);
    if (saveResult.success) {
      alert('Datei gespeichert!');
    }
  }
</script>

7.4 Praxisbeispiel: Systembenachrichtigungen, benutzerdefiniertes Menü, lokale Dateiauswahl

Projekt: Benachrichtigungs- & Dateimanager

main.js

javascript
const { app, BrowserWindow, Notification, Menu, Tray, dialog, ipcMain } = require('electron');
const path = require('path');
const fs = require('fs').promises;

let mainWin;
let tray;

function createWindow() {
  mainWin = new BrowserWindow({
    width: 1000,
    height: 700,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

  mainWin.loadFile('index.html');
}

// Benachrichtigung senden
ipcMain.handle('send-notification', async (event, { title, body }) => {
  const notification = new Notification({ title, body });
  notification.on('click', () => {
    mainWin.focus();
    mainWin.show();
  });
  notification.show();
  return { success: true };
});

// Datei-Dialoge
ipcMain.handle('open-file', async (event) => {
  const result = await dialog.showOpenDialog(mainWin, {
    properties: ['openFile'],
    filters: [{ name: 'Text', extensions: ['txt', 'md'] }]
  });
  return result.canceled ? { success: false } : { success: true, path: result.filePaths[0] };
});

ipcMain.handle('save-file', async (event, content, defaultPath) => {
  const result = await dialog.showSaveDialog(mainWin, {
    defaultPath,
    filters: [{ name: 'Text', extensions: ['txt'] }]
  });
  if (!result.canceled) {
    await fs.writeFile(result.filePath, content, 'utf-8');
    return { success: true, path: result.filePath };
  }
  return { success: false };
});

// Tray erstellen
function createTray() {
  tray = new Tray(path.join(__dirname, 'assets', 'icon.png'));
  
  const trayMenu = Menu.buildFromTemplate([
    { label: 'App öffnen', click: () => { mainWin.show(); mainWin.focus(); } },
    { type: 'separator' },
    { label: 'Beenden', click: () => app.quit() }
  ]);

  tray.setContextMenu(trayMenu);
  tray.setToolTip('Meine Electron-App');
  tray.on('click', () => {
    if (mainWin.isVisible()) mainWin.hide();
    else { mainWin.show(); mainWin.focus(); }
  });
}

// Menü erstellen
function setAppMenu() {
  const menu = Menu.buildFromTemplate([
    {
      label: 'Datei',
      submenu: [
        { label: 'Öffnen', accelerator: 'CmdOrCtrl+O', click: () => mainWin.webContents.send('menu-open-file') },
        { label: 'Speichern', accelerator: 'CmdOrCtrl+S', click: () => mainWin.webContents.send('menu-save-file') },
        { type: 'separator' },
        { label: 'Beenden', accelerator: 'CmdOrCtrl+Q', click: () => app.quit() }
      ]
    },
    {
      label: 'Hilfe',
      submenu: [
        { label: 'Über', click: () => {
          new Notification({ title: 'Über', body: 'Meine Electron-App v1.0' }).show();
        }}
      ]
    }
  ]);
  Menu.setApplicationMenu(menu);
}

app.whenReady().then(() => {
  createWindow();
  createTray();
  setAppMenu();
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

preload.js

javascript
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('appAPI', {
  sendNotification: (title, body) => ipcRenderer.invoke('send-notification', { title, body }),
  openFile: () => ipcRenderer.invoke('open-file'),
  saveFile: (content, defaultPath) => ipcRenderer.invoke('save-file', content, defaultPath),
  onMenuOpenFile: (callback) => ipcRenderer.on('menu-open-file', callback),
  onMenuSaveFile: (callback) => ipcRenderer.on('menu-save-file', callback)
});

index.html (Teilauszug)

html
<button onclick="sendNotification()">Benachrichtigung senden</button>
<button onclick="openFile()">Datei öffnen</button>
<button onclick="saveFile()">Datei speichern</button>

<script>
  async function sendNotification() {
    await window.appAPI.sendNotification('Hallo!', 'Dies ist eine Nachricht.');
  }

  async function openFile() {
    const result = await window.appAPI.openFile();
    if (result.success) {
      // Datei lesen und anzeigen
      console.log('Datei geöffnet:', result.path);
    }
  }

  async function saveFile() {
    const content = 'Beispielinhalt';
    const result = await window.appAPI.saveFile(content, 'meine_datei.txt');
    if (result.success) {
      console.log('Datei gespeichert:', result.path);
    }
  }
</script>

Zusammenfassung

In diesem Kapitel haben Sie gelernt:

  • Desktop-Benachrichtigungen mit dem Notification-Modul zu erstellen
  • Systemmenüs und Tray-Icons zu erstellen und zu verwalten
  • Dateisystemoperationen mit Node.js fs-Modul durchzuführen
  • Dateidialoge mit dem dialog-Modul zu nutzen
  • Ein praktisches Beispiel mit Benachrichtigungen, Menüs und Dateiauswahl zu implementieren

Im nächsten Kapitel werden wir die Integration von Frontend-Frameworks behandeln.

Frei für alle Anfänger