【Electron】ELECTRON API DEMOS

本文档详细介绍了如何使用 Electron 的 API 进行窗口管理、菜单定制、本机用户界面交互、进程通信以及系统和媒体操作。从创建窗口、处理崩溃和挂起,到创建自定义菜单和上下文菜单,再到系统对话框、通知、托盘和拖放文件的使用,全面覆盖了 Electron 开发中的关键功能。此外,还涉及到了进程间通信、获取系统信息和截屏等高级特性。

Electron官网入门API,进行翻译并整理,官网链接:https://www.electronjs.org/
仅作学习交流,侵删。

ELECTRON API DEMOS

1 窗口

1.1 创建和管理窗口

Electron的BrowserWindow模块允许创建新的或管理已存在的浏览器窗口。

1.1.1 创建一个新的窗口

BrowserWindow模块能在app中创建一个新窗口,该主进程模块能够通过remote模块从渲染进程中使用,例如:

渲染进程

const {BrowserWindow} = require('electron').remote
const path = require('path')

const newWindowBtn = document.getElementById('new-window')

newWindowBtn.addEventListener('click', (event) => {
  const modalPath = path.join('file://', __dirname, '../../sections/windows/modal.html')
  let win = new BrowserWindow({ width: 400, height: 320 })

  win.on('close', () => { win = null })
  win.loadURL(modalPath)
  win.show()
})

path是node自带模块。

path.join(path1, path2, path3, ...)作用是将路径片段使用特定分隔符连接起来规范化形成路径。

__dirname代表当前文件所在路径。

1.1.2 管理窗口状态

该实例中创建一个新窗口并监听它的moveresize事件:

渲染进程

const {BrowserWindow} = require('electron').remote
const path = require('path')

const manageWindowBtn = document.getElementById('manage-window')
let win

manageWindowBtn.addEventListener('click', (event) => {
  const modalPath = path.join('file://', __dirname, '../../sections/windows/manage-modal.html')
  win = new BrowserWindow({ width: 400, height: 275 })

  win.on('resize', updateReply)
  win.on('move', updateReply)
  win.on('close', () => { win = null })
  win.loadURL(modalPath)
  win.show()

  function updateReply () {
    const manageWindowReply = document.getElementById('manage-window-reply')
    const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
    manageWindowReply.innerText = message
  }
})
1.1.3 窗口事件:模糊和聚焦

该实例中创建一个新窗口并监听他的blur事件:

渲染进程

const {BrowserWindow} = require('electron').remote
const path = require('path')

const manageWindowBtn = document.getElementById('listen-to-window')
const focusModalBtn = document.getElementById('focus-on-modal-window')
let win

manageWindowBtn.addEventListener('click', () => {
  const modalPath = path.join('file://', __dirname, '../../sections/windows/modal-toggle-visibility.html')
  win = new BrowserWindow({ width: 600, height: 400 })

  const hideFocusBtn = () => {
    focusModalBtn.classList.add('disappear')
    focusModalBtn.classList.remove('smooth-appear')
    focusModalBtn.removeEventListener('click', clickHandler)
  }

  const showFocusBtn = (btn) => {
    if (!win) return
    focusModalBtn.classList.add('smooth-appear')
    focusModalBtn.classList.remove('disappear')
    focusModalBtn.addEventListener('click', clickHandler)
  }

  win.on('focus', hideFocusBtn)
  win.on('blur', showFocusBtn)
  win.on('close', () => {
    hideFocusBtn()
    win = null
  })
  win.loadURL(modalPath)
  win.show()

  const clickHandler = () => { win.focus() }
})
1.1.4 创建一个无框架窗口

无框架窗口是没有工具栏、标题栏、状态栏等的窗口,创建窗口时设置framefalse即可实现:

渲染进程

const {BrowserWindow} = require('electron').remote
const newWindowBtn = document.getElementById('frameless-window')

const path = require('path')

newWindowBtn.addEventListener('click', (event) => {
  const modalPath = path.join('file://', __dirname, '../../sections/windows/modal.html')
  let win = new BrowserWindow({ frame: false })

  win.on('close', () => { win = null })
  win.loadURL(modalPath)
  win.show()
})

同理,设置transparenttrue可以使无框架窗口透明化。

1.2 处理窗口崩溃和挂起

当渲染进程崩溃或挂起时BrowserWindow模块将发送事件,监听这些事件让用户选择重载、等待或关闭窗口。

1.2.1 进程崩溃后重启窗口

该实例中我们通过remote模块创建一个新的窗口,并提供一个使用process.crash()强制崩溃的链接。

窗口监听崩溃事件并在事件发生时提醒用户重载或关闭。

渲染进程

const {BrowserWindow, dialog} = require('electron').remote
const path = require('path')

const processCrashBtn = document.getElementById('process-crash')

processCrashBtn.addEventListener('click', (event) => {
  const crashWinPath = path.join('file://', __dirname, '../../sections/windows/process-crash.html')
  let win = new BrowserWindow({ width: 400, height: 320 })

  win.webContents.on('crashed', () => {
    const options = {
      type: 'info',
      title: 'Renderer Process Crashed',
      message: 'This process has crashed.',
      buttons: ['Reload', 'Close']
    }

    dialog.showMessageBox(options, (index) => {
      if (index === 0) win.reload()
      else win.close()
    })
  })

  win.on('close', () => { win = null })
  win.loadURL(crashWinPath)
  win.show()
})

需要注意,最新文档中官方已经移除了回调函数。

dialog.showMessageBox([browserWindow, ]options)返回Promise<Object>包含以下属性:

  • response 数字:被单机按钮索引。
  • checkboxChecked 布尔:如果设置checkboxLabel则是复选框的选中状态,否则为false
1.2.2 进程挂起后重启窗口

该实例中我们通过remote模块创建一个新的窗口,并提供一个使用process.hang()强制挂起的链接。

窗口监听进程正式变为无响应,然后提醒用户重载或关闭。

渲染进程

const {BrowserWindow, dialog} = require('electron').remote
const path = require('path')

const processHangBtn = document.getElementById('process-hang')

processHangBtn.addEventListener('click', (event) => {
  const hangWinPath = path.join('file://', __dirname, '../../sections/windows/process-hang.html')
  let win = new BrowserWindow({ width: 400, height: 320 })

  win.on('unresponsive', () => {
    const options = {
      type: 'info',
      title: 'Renderer Process Hanging',
      message: 'This process is hanging.',
      buttons: ['Reload', 'Close']
    }

    dialog.showMessageBox(options, (index) => {
      if (index === 0) win.reload()
      else win.close()
    })
  })

  win.on('close', () => { win = null })
  win.loadURL(hangWinPath)
  win.show()
})

2 菜单

2.1 自定义菜单

MenuMenuItem模块能被用来创建自定义本机菜单。

菜单分两种:应用(顶部)菜单和上下文(右击)菜单。

2.1.1 创建应用菜单

MenuMenuItem模块允许你自定义你的应用菜单。如果你没有设置任何菜单,默认情况下,Electron将为你的应用生成一个最小菜单。

主进程

const {BrowserWindow, Menu, app, shell, dialog} = require('electron')

let template = [{
  label: 'Edit',
  submenu: [{
    label: 'Undo',
    accelerator: 'CmdOrCtrl+Z',
    role: 'undo'
  }, {
    label: 'Redo',
    accelerator: 'Shift+CmdOrCtrl+Z',
    role: 'redo'
  }, {
    type: 'separator'
  }, {
    label: 'Cut',
    accelerator: 'CmdOrCtrl+X',
    role: 'cut'
  }, {
    label: 'Copy',
    accelerator: 'CmdOrCtrl+C',
    role: 'copy'
  }, {
    label: 'Paste',
    accelerator: 'CmdOrCtrl+V',
    role: 'paste'
  }, {
    label: 'Select All',
    accelerator: 'CmdOrCtrl+A',
    role: 'selectall'
  }]
}, {
  label: 'View',
  submenu: [{
    label: 'Reload',
    accelerator: 'CmdOrCtrl+R',
    click: (item, focusedWindow) => {
      if (focusedWindow) {
        // on reload, start fresh and close any old
        // open secondary windows
        if (focusedWindow.id === 1) {
          BrowserWindow.getAllWindows().forEach(win => {
            if (win.id > 1) win.close()
          })
        }
        focusedWindow.reload()
      }
    }
  }, {
    label: 'Toggle Full Screen',
    accelerator: (() => {
      if (process.platform === 'darwin') {
        return 'Ctrl+Command+F'
      } else {
        return 'F11'
      }
    })(),
    click: (item, focusedWindow) => {
      if (focusedWindow) {
        focusedWindow.setFullScreen(!focusedWindow.isFullScreen())
      }
    }
  }, {
    label: 'Toggle Developer Tools',
    accelerator: (() => {
      if (process.platform === 'darwin') {
        return 'Alt+Command+I'
      } else {
        return 'Ctrl+Shift+I'
      }
    })(),
    click: (item, focusedWindow) => {
      if (focusedWindow) {
        focusedWindow.toggleDevTools()
      }
    }
  }, {
    type: 'separator'
  }, {
    label: 'App Menu Demo',
    click: function (item, focusedWindow) {
      if (focusedWindow) {
        const options = {
          type: 'info',
          title: 'Application Menu Demo',
          buttons: ['Ok'],
          message: 'This demo is for the Menu section, showing how to create a clickable menu item in the application menu.'
        }
        dialog.showMessageBox(focusedWindow, options, function () {})
      }
    }
  }]
}, {
  label: 'Window',
  role: 'window',
  submenu: [{
    label: 'Minimize',
    accelerator: 'CmdOrCtrl+M',
    role: 'minimize'
  }, {
    label: 'Close',
    accelerator: 'CmdOrCtrl+W',
    role: 'close'
  }, {
    type: 'separator'
  }, {
    label: 'Reopen Window',
    accelerator: 'CmdOrCtrl+Shift+T',
    enabled: false,
    key: 'reopenMenuItem',
    click: () => {
      app.emit('activate')
    }
  }]
}, {
  label: 'Help',
  role: 'help',
  submenu: [{
    label: 'Learn More',
    click: () => {
      shell.openExternal('http://electron.atom.io')
    }
  }]
}]

function addUpdateMenuItems (items, position) {
  if (process.mas) return

  const version = app.getVersion()
  let updateItems = [{
    label: `Version ${version}`,
    enabled: false
  }, {
    label: 'Checking for Update',
    enabled: false,
    key: 'checkingForUpdate'
  }, {
    label: 'Check for Update',
    visible: false,
    key: 'checkForUpdate',
    click: () => {
      require('electron').autoUpdater.checkForUpdates()
    }
  }, {
    label: 'Restart and Install Update',
    enabled: true,
    visible: false,
    key: 'restartToUpdate',
    click: () => {
      require('electron').autoUpdater.quitAndInstall()
    }
  }]

  items.splice.apply(items, [position, 0].concat(updateItems))
}

function findReopenMenuItem () {
  const menu = Menu.getApplicationMenu()
  if (!menu) return

  let reopenMenuItem
  menu.items.forEach(item => {
    if (item.submenu) {
      item.submenu.items.forEach(item => {
        if (item.key === 'reopenMenuItem') {
          reopenMenuItem = item
        }
      })
    }
  })
  return reopenMenuItem
}

if (process.platform === 'darwin') {
  const name = app.getName()
  template.unshift({
    label: name,
    submenu: [{
      label: `About ${name}`,
      role: 'about'
    }, {
      type: 'separator'
    }, {
      label: 'Services',
      role: 'services',
      submenu: []
    }, {
      type: 'separator'
    }, {
      label: `Hide ${name}`,
      accelerator: 'Command+H',
      role: 'hide'
    }, {
      label: 'Hide Others',
      accelerator: 'Command+Alt+H',
      role: 'hideothers'
    }, {
      label: 'Show All',
      role: 'unhide'
    }, {
      type: 'separator'
    }, {
      label: 'Quit',
      accelerator: 'Command+Q',
      click: () => {
        app.quit()
      }
    }]
  })

  // Window menu.
  template[3].submenu.push({
    type: 'separator'
  }, {
    label: 'Bring All to Front',
    role: 'front'
  })

  addUpdateMenuItems(template[0].submenu, 1)
}

if (process.platform === 'win32') {
  const helpMenu = template[template.length - 1].submenu
  addUpdateMenuItems(helpMenu, 0)
}

app.on('ready', () => {
  const menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu)
})

app.on('browser-window-created', () => {
  let reopenMenuItem = findReopenMenuItem()
  if (reopenMenuItem) reopenMenuItem.enabled = false
})

app.on('window-all-closed', () => {
  let reopenMenuItem = findReopenMenuItem()
  if (reopenMenuItem) reopenMenuItem.enabled = true
})
2.1.2 创建上下文菜单

一个上下文,或者右击,MenuMenuItem模块也能创建菜单。

主进程

const {
  BrowserWindow,
  Menu,
  MenuItem,
  ipcMain,
  app
} = require('electron')

const menu = new Menu()
menu.append(new MenuItem({ label: 'Hello' }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: 'Electron', type: 'checkbox', checked: true }))

app.on('browser-window-created', (event, win) => {
  win.webContents.on('context-menu', (e, params) => {
    menu.popup(win, params.x, params.y)
  })
})

ipcMain.on('show-context-menu', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender)
  menu.popup(win)
})

渲染进程

const {ipcRenderer} = require('electron')

// Tell main process to show the menu when demo button is clicked
const contextMenuBtn = document.getElementById('context-menu')

contextMenuBtn.addEventListener('click', () => {
  ipcRenderer.send('show-context-menu')
})
2.2 注册键盘快捷方式

globalShortcutMenu模块能用来定义键盘快捷方式。

Electron中,键盘快捷方式被称为加速器,它们可以被分配给应用菜单的操作,或是全局分配,以便即使应用没有键盘焦点时也能触发。

2.2.1 注册一个全局键盘快捷方式

即使应用没有键盘焦点时全局快捷方式也被检测,并且它们必须在应用的ready事件被发出后注册。

主进程

const {app, dialog, globalShortcut} = require('electron')

app.on('ready', () => {
  globalShortcut.register('CommandOrControl+Alt+K', () => {
    dialog.showMessageBox({
      type: 'info',
      message: 'Success!',
      detail: 'You pressed the registered global shortcut keybinding.',
      buttons: ['OK']
    })
  })
})

app.on('will-quit', () => {
  globalShortcut.unregisterAll()
})

3 本机用户界面

3.1 打开外部链接和文件管理器

Electron的shell模块允许你访问某些本机元素,像是文件管理器和默认浏览器。

主进程和渲染进程都能使用该模块。

3.1.1 在文件管理器中打开路径

该示例使用shell模块在特定位置打开系统文件管理器:

渲染进程

const {shell} = require('electron')
const os = require('os')

const fileManagerBtn = document.getElementById('open-file-manager')

fileManagerBtn.addEventListener('click', (event) => {
  shell.showItemInFolder(os.homedir())
})
3.1.2 打开外部链接

如果你不希望应用在应用内打开网络链接,你可以使用shell模块在外部打开它们。

渲染进程

const {shell} = require('electron')

const exLinksBtn = document.getElementById('open-ex-links')

exLinksBtn.addEventListener('click', (event) => {
  shell.openExternal('http://electron.atom.io')
})
3.2 有无自定义图像的通知

Electron的notification模块允许你添加基本的桌面通知。

Electron方便允许开发者使用HTML Notification API发送通知,并使用当前运行操作系统的本机通知API去展示。

**注意:**由于这是一个HTML5 API它只在渲染进程中生效。

3.2.1 基本通知

以下示例演示了一个仅文本的基本通知:

渲染进程

const notification = {
  title: 'Basic Notification',
  body: 'Short message part'
}

const notificationButton = document.getElementById('basic-noti')

notificationButton.addEventListener('click', () => {
  const myNotification = new window.Notification(notification.title, notification)

  myNotification.onclick = () => {
    console.log('Notification clicked')
  }
})
3.2.2 带图像的通知

以下示例演示了一个包含文本和图像的基本通知:

渲染进程

const path = require('path')

const notification = {
  title: 'Notification with image',
  body: 'Short message plus a custom image',
  icon: path.join(__dirname, '../../../assets/img/programming.png')
}
const notificationButton = document.getElementById('advanced-noti')

notificationButton.addEventListener('click', () => {
  const myNotification = new window.Notification(notification.title, notification)

  myNotification.onclick = () => {
    console.log('Notification clicked')
  }
})
3.3 使用系统对话框

Electron的dialog模块允许你使用本机系统对话框来打开文件或目录,保存文件或者展示信息消息。

这是一个主进程模块因为该进程在本机实用程序中效率更高,并且它允许调用发生而不中断你页面渲染进程的可见元素。

3.3.1 打开文件或目录

在这个示例,ipc模块被用来从渲染进程发送信息指示主进程启动打开文件或目录对话框。如果有文件被选中,主进程能将信息发回渲染进程。

渲染进程

const {ipcRenderer} = require('electron')

const selectDirBtn = document.getElementById('select-directory')

selectDirBtn.addEventListener('click', (event) => {
  ipcRenderer.send('open-file-dialog')
})

ipcRenderer.on('selected-directory', (event, path) => {
  document.getElementById('selected-file').innerHTML = `You selected: ${path}`
})

主进程

const {ipcMain, dialog} = require('electron')

ipcMain.on('open-file-dialog', (event) => {
  dialog.showOpenDialog({
    properties: ['openFile', 'openDirectory']
  }, (files) => {
    if (files) {
      event.sender.send('selected-directory', files)
    }
  })
})
3.3.2 错误对话框

在这个示例,ipc模块被用来从渲染进程发送信息指示主进程启动错误对话框。

你能在应用的ready事件前使用错误对话框,这对于在启动时显示错误非常有用。

渲染进程

const {ipcRenderer} = require('electron')

const errorBtn = document.getElementById('error-dialog')

errorBtn.addEventListener('click', (event) => {
  ipcRenderer.send('open-error-dialog')
})

主进程

const {ipcMain, dialog} = require('electron')

ipcMain.on('open-error-dialog', (event) => {
  dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
})
3.3.3 信息对话框

在这个示例,ipc模块被用来从渲染进程发送信息指示主进程启动信息对话框。可提供响应选项,可以中继回渲染进程。

渲染进程

const {ipcRenderer} = require('electron')

const informationBtn = document.getElementById('information-dialog')

informationBtn.addEventListener('click', (event) => {
  ipcRenderer.send('open-information-dialog')
})

ipcRenderer.on('information-dialog-selection', (event, index) => {
  let message = 'You selected '
  if (index === 0) message += 'yes.'
  else message += 'no.'
  document.getElementById('info-selection').innerHTML = message
})

主进程

const {ipcMain, dialog} = require('electron')

ipcMain.on('open-information-dialog', (event) => {
  const options = {
    type: 'info',
    title: 'Information',
    message: "This is an information dialog. Isn't it nice?",
    buttons: ['Yes', 'No']
  }
  dialog.showMessageBox(options, (index) => {
    event.sender.send('information-dialog-selection', index)
  })
})
3.3.4 保存对话框

在这个示例,ipc模块被用来从渲染进程发送信息指示主进程启动保存对话框。它返回用户选择的路径,可以中继回渲染进程。

渲染进程

const {ipcRenderer} = require('electron')

const saveBtn = document.getElementById('save-dialog')

saveBtn.addEventListener('click', (event) => {
  ipcRenderer.send('save-dialog')
})

ipcRenderer.on('saved-file', (event, path) => {
  if (!path) path = 'No path'
  document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
})

主进程

const {ipcMain, dialog} = require('electron')

ipcMain.on('save-dialog', (event) => {
  const options = {
    title: 'Save an Image',
    filters: [
      { name: 'Images', extensions: ['jpg', 'png', 'gif'] }
    ]
  }
  dialog.showSaveDialog(options, (filename) => {
    event.sender.send('saved-file', filename)
  })
})
3.4 将应用放入托盘

tray模块允许你在操作系统的通知区域创建图标。

此图标还可以附加上下文菜单。

3.4.1 托盘

示例按钮使用ipc模块发送一个消息给主进程。在主进程中,应用被告知在托盘中放置一个带上下文菜单的图标。

主进程

const path = require('path')
const {ipcMain, app, Menu, Tray} = require('electron')

let appIcon = null

ipcMain.on('put-in-tray', (event) => {
  const iconName = process.platform === 'win32' ? 'windows-icon.png' : 'iconTemplate.png'
  const iconPath = path.join(__dirname, iconName)
  appIcon = new Tray(iconPath)

  const contextMenu = Menu.buildFromTemplate([{
    label: 'Remove',
    click: () => {
      event.sender.send('tray-removed')
    }
  }])

  appIcon.setToolTip('Electron Demo in the tray.')
  appIcon.setContextMenu(contextMenu)
})

ipcMain.on('remove-tray', () => {
  appIcon.destroy()
})

app.on('window-all-closed', () => {
  if (appIcon) appIcon.destroy()
})

渲染进程

const ipc = require('electron').ipcRenderer

const trayBtn = document.getElementById('put-in-tray')
let trayOn = false

trayBtn.addEventListener('click', function (event) {
  if (trayOn) {
    trayOn = false
    document.getElementById('tray-countdown').innerHTML = ''
    ipc.send('remove-tray')
  } else {
    trayOn = true
    const message = 'Click demo again to remove.'
    document.getElementById('tray-countdown').innerHTML = message
    ipc.send('put-in-tray')
  }
})
// Tray removed from context menu on icon
ipc.on('tray-removed', function () {
  ipc.send('remove-tray')
  trayOn = false
  document.getElementById('tray-countdown').innerHTML = ''
})
3.5 拖放文件

Electron支持将文件和网页内容拖入操作系统世界。

3.5.1 拖动文件

示例中,webContents.startDrag()API被调用响应ondragstart事件。

渲染进程

const {ipcRenderer} = require('electron')

const dragFileLink = document.getElementById('drag-file-link')

dragFileLink.addEventListener('dragstart', (event) => {
  event.preventDefault()
  ipcRenderer.send('ondragstart', __filename)
})

主进程

const {ipcMain} = require('electron')
const path = require('path')

ipcMain.on('ondragstart', (event, filepath) => {
  const iconName = 'codeIcon.png'
  event.sender.startDrag({
    file: filepath,
    icon: path.join(__dirname, iconName)
  })
})

4 通信

4.1 两个进程间通信

ipc(inter-process communication)模块允许你在主进程和渲染进程间发送和接受同步异步消息。

4.1.1 异步消息

使用ipc在进程间异步发送消息是首选方法,因为它将在结束时返回而不阻塞相同进程的其他操作。

这个例子中从渲染进程向主进程发送一个“ping”,主进程答复“pong”。

渲染进程

const {ipcRenderer} = require('electron')

const asyncMsgBtn = document.getElementById('async-msg')

asyncMsgBtn.addEventListener('click', () => {
  ipcRenderer.send('asynchronous-message', 'ping')
})

ipcRenderer.on('asynchronous-reply', (event, arg) => {
  const message = `Asynchronous message reply: ${arg}`
  document.getElementById('async-reply').innerHTML = message
})

主进程

const {ipcMain} = require('electron')

ipcMain.on('asynchronous-message', (event, arg) => {
  event.sender.send('asynchronous-reply', 'pong')
})
4.1.2 同步消息

你也能使用ipc模块在进程间发送同步消息,但注意此方法的同步性质意味着在完它的任务时它将阻塞其他操作。

这个例子从渲染进程向主进程发送一个同步消息“ping”,之后主进程答复“pong”。

渲染进程

const {ipcRenderer} = require('electron')

const syncMsgBtn = document.getElementById('sync-msg')

syncMsgBtn.addEventListener('click', () => {
  const reply = ipcRenderer.sendSync('synchronous-message', 'ping')
  const message = `Synchronous message reply: ${reply}`
  document.getElementById('sync-reply').innerHTML = message
})

主进程

const {ipcMain} = require('electron')

ipcMain.on('synchronous-message', (event, arg) => {
  event.returnValue = 'pong'
})
4.1.3 与不可见窗口通信

通常做法是渲染进程创建新的不可见浏览器窗口,以便在不影响主应用窗口性能的情况下运行任务。

在这个示例中我们使用remote模块从渲染进程中创建新的不可见浏览器窗口。当新的页面被加载,我们用ipc发送一条新窗口正在侦听消息。

渲染进程

const {BrowserWindow} = require('electron').remote
const ipcRenderer = require('electron').ipcRenderer
const path = require('path')

const invisMsgBtn = document.getElementById('invis-msg')
const invisReply = document.getElementById('invis-reply')

invisMsgBtn.addEventListener('click', (clickEvent) => {
  const windowID = BrowserWindow.getFocusedWindow().id
  const invisPath = `file://${path.join(__dirname, '../../sections/communication/invisible.html')}`
  let win = new BrowserWindow({
    width: 400,
    height: 400,
    show: false
  })
  win.loadURL(invisPath)

  win.webContents.on('did-finish-load', () => {
    const input = 100
    win.webContents.send('compute-factorial', input, windowID)
  })
})

ipcRenderer.on('factorial-computed', (event, input, output) => {
  const message = `The factorial of ${input} is ${output}`
  invisReply.textContent = message
})

不可见的窗口HTML页面

<html>
  <script type="text/javascript">
    const ipc = require('electron').ipcRenderer
    const BrowserWindow = require('electron').remote.BrowserWindow

    ipc.on('compute-factorial', function (event, number, fromWindowId) {
      const result = factorial(number)
      const fromWindow = BrowserWindow.fromId(fromWindowId)
      fromWindow.webContents.send('factorial-computed', number, result)
      window.close()
    })

    function factorial (num) {
      if (num === 0) return 1
      return num * factorial(num - 1)
    }
  </script>
</html>

5 系统

5.1 获取应用或系统信息

通过一些Node.js和Electron模块你能收集关于用户系统,应用或屏幕的信息。

5.1.1 获取应用信息

主进程的app模块能用来获取应用位于用户计算机上的路径。

在这个示例,为了从渲染进程获取信息,我们使用ipc模块发消息给主进程请求应用路径。

渲染进程

const {ipcRenderer} = require('electron')

const appInfoBtn = document.getElementById('app-info')

appInfoBtn.addEventListener('click', () => {
  ipcRenderer.send('get-app-path')
})

ipcRenderer.on('got-app-path', (event, path) => {
  const message = `This app is located at: ${path}`
  document.getElementById('got-app-info').innerHTML = message
})

主进程

const {app, ipcMain} = require('electron')

ipcMain.on('get-app-path', (event) => {
  event.sender.send('got-app-path', app.getAppPath())
})
5.1.2 获取版本信息

下面示例获取应用正在使用的Electron版本。

渲染进程

const versionInfoBtn = document.getElementById('version-info')

const electronVersion = process.versions.electron

versionInfoBtn.addEventListener('click', () => {
  const message = `This app is using Electron version: ${electronVersion}`
  document.getElementById('got-version-info').innerHTML = message
})
5.1.3 获取系统信息

下面这个示例中我们require模块并在之后返回主目录位置。

渲染进程

const os = require('os')
const homeDir = os.homedir()

const sysInfoBtn = document.getElementById('sys-info')

sysInfoBtn.addEventListener('click', () => {
  const message = `Your system home directory is: ${homeDir}`
  document.getElementById('got-sys-info').innerHTML = message
})
5.1.4 获取屏幕信息

Electron的screen模块检索关于屏幕尺寸,显示,光标位置等的信息。下面的示例中我们检索正在使用的显示器的尺寸。

渲染进程

const {screen} = require('electron')

const screenInfoBtn = document.getElementById('screen-info')
const size = screen.getPrimaryDisplay().size

screenInfoBtn.addEventListener('click', () => {
  const message = `Your screen is: ${size.width}px x ${size.height}px`
  document.getElementById('got-screen-info').innerHTML = message
})
5.2 从剪贴板复制和粘贴

clipboard模块提供方法执行复制和粘贴操作。

5.2.1 复制

这个示例中我们复制一个短语到剪贴板。点击‘Copy’后使用文本区从剪贴板粘贴短语。

渲染进程

const {clipboard} = require('electron')

const copyBtn = document.getElementById('copy-to')
const copyInput = document.getElementById('copy-to-input')

copyBtn.addEventListener('click', () => {
  if (copyInput.value !== '') copyInput.value = ''
  copyInput.placeholder = 'Copied! Paste here to see.'
  clipboard.writeText('Electron Demo!')
})
5.2.2 粘贴

这个示例中我们复制一个字符串到剪贴板然后把结果粘贴进上面的消息。

渲染进程

const {clipboard} = require('electron')

const pasteBtn = document.getElementById('paste-to')

pasteBtn.addEventListener('click', () => {
  clipboard.writeText('What a demo!')
  const message = `Clipboard contents: ${clipboard.readText()}`
  document.getElementById('paste-from').innerHTML = message
})
5.3 从协议处理程序启动应用

app模块提供方法处理协议。

这些方法允许你设置和取消设置你的应用为默认应用的协议。类似于浏览器要求成为你查看网页的默认方式。

5.3.1 从另一个应用的URL中启动应用

你能设置你的应用作为默认应用去打开特定协议。例如,在示例中我们设置应用作为默认打开electron-app-demo://的方式。

渲染进程

const {shell} = require('electron')
const path = require('path')

const protocolHandlerBtn = document.getElementById('protocol-handler')

protocolHandlerBtn.addEventListener('click', () => {
  const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
  const pagePath = path.join('file://', pageDirectory, '../../sections/system/protocol-link.html')
  shell.openExternal(pagePath)
})

主进程

const {app, dialog} = require('electron')
const path = require('path')

if (process.defaultApp) {
  if (process.argv.length >= 2) {
    app.setAsDefaultProtocolClient('electron-api-demos', process.execPath, [path.resolve(process.argv[1])])
  }
} else {
  app.setAsDefaultProtocolClient('electron-api-demos')
}

app.on('open-url', (event, url) => {
  dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
})

6 媒体

6.1 截屏

Electron的desktopCapturer模块可以用来访问任何媒体,例如音频,视频,屏幕和窗口。

6.1.1 截屏

这个示例使用desktopCapturer模块收集使用中的屏幕,选择整个屏幕,对可见部分拍摄快照。

渲染进程

const {desktopCapturer, screen, shell} = require('electron')

const fs = require('fs')
const os = require('os')
const path = require('path')

const screenshot = document.getElementById('screen-shot')
const screenshotMsg = document.getElementById('screenshot-path')

screenshot.addEventListener('click', (event) => {
  screenshotMsg.textContent = 'Gathering screens...'
  const thumbSize = determineScreenShotSize()
  let options = { types: ['screen'], thumbnailSize: thumbSize }

  desktopCapturer.getSources(options, (error, sources) => {
    if (error) return console.log(error)

    sources.forEach((source) => {
      if (source.name === 'Entire screen' || source.name === 'Screen 1') {
        const screenshotPath = path.join(os.tmpdir(), 'screenshot.png')

        fs.writeFile(screenshotPath, source.thumbnail.toPNG(), (error) => {
          if (error) return console.log(error)
          shell.openExternal(`file://${screenshotPath}`)

          const message = `Saved screenshot to: ${screenshotPath}`
          screenshotMsg.textContent = message
        })
      }
    })
  })
})

function determineScreenShotSize () {
  const screenSize = screen.getPrimaryDisplay().workAreaSize
  const maxDimension = Math.max(screenSize.width, screenSize.height)
  return {
    width: maxDimension * window.devicePixelRatio,
    height: maxDimension * window.devicePixelRatio
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值