Appearance
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.
