Electron中ipc 和global-shortcut

现在咱们通过一项目(如一个音乐的app)流程的形式来学习相关知识:

ipc

ipc是Electron为主进程(main.js)和渲染进程(index.js)提供的跨进程通信模块,ipc模块允许接收和发送通信频道的信息。在主进程使用ipc模块时,ipc负责捕获从渲染进程(网页)发送的同步或者是异步消息.
ipcMain 模块是类 EventEmitter 的实例.当在主进程中使用它的时候,它控制着由渲染进程(web page)发送过来的异步或同步消息.从渲染进程发送过来的消息将触发事件.

  • 把ipc模块包含进来后(require(‘ipc’)),从频道中订阅信息就非常简单了,通过on()方法和频道名称,在加上回调函数就行了。

例:

//main.js
var electron = require('electron');
var ipcMain = electron.ipcMain;
ipcMain.on('close-main-window',function(){
    app.quit();
})

主进程(main.js)通过ipcMain模块来监听频道(channel),从而处理消息,了解更多ipcMain

  • 向main.js中的‘close-main-window’频道发送信息
//index.js
var electron = require('electron');
var ipcRenderer = electron.ipcRenderer;
var closeEl = document.querySlector('.close');
closeEl.addEventListener('click',function(){
    ipcRenderer.send('close-main-window')
});//当点击关闭按钮时,通过send()方法发送一条信息到“关闭主窗口”频道。

上例中ipcRender.send(‘close-main-window’)通过频道(channel)’close-main-window’向主进程发送异步消息,也可以发送任意参数。参数会被JSON序列化,之后就不会包含函数或原型链。了解更多ipcRenderer
ipcRenderer 模块是一个 EventEmitter 类的实例. 它提供了有限的方法,你可以从渲染进程向主进程发送同步或异步消息. 也可以收到主进程的相应.
在index.css中将关闭按钮设置为不可拖拽:

//index.css
.settiings{
    ...
    -webkit-app-region: no-drag;
}

目录结构:

这里写图片描述

global shortcut module

设置全局快捷键

Electron提供了全局快捷模块(global shortcut module),允许开发者捕获组合键并作出相应的反应。在Electron中组合键被称为加速器。它以字符串的形式被记录下来,如:Ctrl+Shift+1。

例:我们想要捕获到原生的GUI事件(全局快捷键),并执行应用窗口事件,我们将使用ipc模块从主进程发送信息到渲染器进程。

注意:1、全局快捷键会在app的ready事件被触发后注册(相关代码片段要被包含在‘ready’中)
2、通过ipc模块从主进程向渲染进程发送信息,你必须使用窗口对象的引用(类似于createdWindow.webContents.send(‘channel’))

//main.js
var electron = require('electron');
var globalShortcut = electron.globalShortcut;

app.on('ready',function(){
    ...//之前写过的代码
    globalShortcut.register('ctrl+shift+1',function(){
        mainWindow.webContents.send('global-shortcut',0);
   });
   globalShortcut.register('ctrl+shift+2',function(){
        mainWindow.webContents.send('global-shortcut',1);
   });
})

说明:添加全局模块需先引入globalShortcut模块,当进入ready状态后,注册两个快捷键‘ctrl+shift+1’、‘ctrl+shift+2’。这连个快捷键可以通过不同的参数向“全局快捷键”频道(‘global-shortcut’channel)发送信息。在index.js中通过参数匹配到需要执行的事情(如:播放音乐)。因此在index.js中增加:

var electron = require'electron');
var ipcRenderer = electron.ipcRenderer;
var soundButtons = documet.querySelectorAll('.button-sound')
ipcRenderer.on('global-shortcut',function(){
    var event = new MouseEvent('click');
    soundButtons[arguments[1]].dispatchEvent(event);//给相应的按钮绑定事件
})

首页的设置按钮相关功能

  • 生成设置窗口和设置按钮的点击同信功能
//index.js
var ipcRender = electron.ipcRenderer;
var settingEl = document.querySelector('.settings');
settingEl.addEventListener('click',function(){
    ipcRenderer.send('open-settings-window');//向主进程main.js发送请求
});//给设置按钮绑定点击事件
//main.js
var settingsWindow = null;
ipcMain.on('open-settings-window',function(){
    if(settingsWindow){return;};//防止接下来的多次实例化设置窗口
    settingsWindow = new BrowserWindow({
        frame:false,
        resizabla:false,
        height:200,
        width:200
   });
    settingsWindow.loadURL('file://'+__dirname+'/app/settings.html');
    settingsWindow.on('closed',function(){
        settingsWindow = null;
   })
})

为’设置页‘添加关闭本页功能

  • 在settings.js中为设置页的关闭按钮添加事件
//settings.js
'use strict';
var electron = require('electron');
var ipcRenderer = electron.ipcRenderer;
var closeEl = document.querySelector('.close');
closeEl.addEventListener('click',fucntion(){
    ipcRenderer.send('close-settings-window');
})
  • 在main.js中监听该点击事件
ipcRenderer.on('close-settings-window',function(){
    settingsWindow.close();
})

更改设置内的快捷键时的读写

我们可以把实现读写设置的部分直接写进main.js中,但为了将来能随处引用,现在把这部分写成独立的模块。

使用JSON做配置文件。我们需创建一个configuration.js文件,再将这个文件引入到项目中。Node.js使用了CommonJs作为编写模块的规范,即你需要将你的API和这个API中可用的函数都要暴露出来。

为了更简单的读写文件,我们将会使用nconf模块(此模块实现了对JSON文件的读写)。通过npm将此模块包含到项目中来:

npm install --save nconf //但save-dev参数会使安装的模块只出现在开发阶段,发布应用的时候不会被包含进去

这行命令意味着nconf模块将会作为应用依赖安装到项目中,当我们发布应用的时候,这个模块会被一起打包给用户。

现在在根目录下创建configuration.js文件:

'use strict';
var conf = require('conf').file({file:getUserHome()  + '/sound-machine-config.json'});
function saveSettings(settingKey,settingValue){
    nconf.set(settingKey,settingValue);
    nconf.save();
};
function readSettings(settingKey,settingValue){
    nconf.load();
    return nconf.get(settingKey);
};
function getUserHome(){
    return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}

module.exports = {
    saveSettings:saveSettings,
    readSettings:readSettings
};

我们要把文件位置和文件名传给nconf模块(用Node.js的process.env获取到文件的位置),具体路径会根据平台而异。

通过nconf模块的set()和get()方法结合文件操作的save()和load(),我们可以实现设置文件的读写操作,然后通过module.exports将接口暴露到外部。

初始化默认的快捷键设置

为了避免用户是第一次打开这个应用,要先初始化一个设置文件。我们将会以数组的形式存储热键,对应的键是“shortcutKeys”,存储在main.js中,我们需要把configuration模块包含到项目中:

'use strict';
var configuration = require('./configuration');
app.on('ready',function(){
    if(!configuration.readSettings('shortcutKeys')){
        configuration.saveSettings('shortcutKeys',['ctrl','shift']);        
   };//先检测键‘shortcutKeys’是否已经有对应的值,如果没有初始化一个‘ctrl’和‘shift’
})

接着,重写之前已注册的快捷键方法,之后我们更新设置的时候,会直接调用这个方法。将之前的注册的代码改写如下:

app.on('ready',function(){
    ...
    setGlobalShortcuts();
})
//注册全局快捷键
function setGlobalShortcuts(){
    globalShortcut.unregisterAll();//清楚所有的全局快捷键
    var shortcutKeysSetting = configuration.readSettings('shortcutKeys');//读取configuration中的快捷键
    var shortcutPrefix = shortcutKeysSetting.jion('+') + '+';
    globalShortcut.register(shortcutPerfix + '1',function(){
        mainWindow.webContents.send('global-shortcut',0)
   });
    globalShortcut.register(shortcutPerfix + '2',function(){
        mainWindow.webContents.send('global-shortcut',1);
   });
}

设置窗口的交互

给settings.html页的复选框绑定事件来改变我们的全局快捷键。

//settings.js
var modifierCheckboxes = document.querySelector('.global-shortcut');
var configuration = require('../configuration.js');
for(var i = 0 ; i<modifierCheckboxes.length;i++){
    var modifierKey = modifierCheckboxes[i].attributes['data-modifier-key'].value;
    var shortcutKeys = configuration.readSettings('shortcutKeys');
    modifierCheckboxes[i].checked = shortcutKeys.indexOf('modifierKey') !== -1;
};//打开settings.html页时根据'shortcutKeys'键的值,对相应的复选框勾选

绑定复选框的行为

//settings.js
for(var i =0;i<modifierCheckboxes.length;i++){
    ...
    modifierCheckboxes[i].addEventListener('click',function(e){
        bindModifierCheckboxes(e);
   })
}
function bindModifierCheckboxes(e){
    var shortcutKeys = configuration.readSettings('shortcutKeys');//返回一个数组
    var modifierKey = e.target.attributes['data-modifier-key'].value;
    if(shortcutKey.indexOf(modifierKey) !==-1){
        //解绑快捷键(从shortcutKeys中删除本次点击对应的快捷键)
       shortcutKeys.splice(shortcutKey.indexOf(modifierKey) ,1); 
   }else{
       //绑定快捷键(向shortcutKeys中添加本次点击对应的快捷键)
        shortcutKeys.push(modifierKey);
   }
   configuration.saveSettings('shortcutKeys',shortcutKeys);
   ipcRenderer('set-global-shortcuts')
}

然后在主进程中增加个对‘设置全局快捷键’频道(’set-global-shortcut’)的监听:

//main.js
ipcRenderer.on('set-global-shortcuts',function(){
    setGlobalShortcuts();//重置全局快捷键
})

给这个桌面应用设置‘菜单’

菜单是桌面应用中的一个重要内容,如右键菜单、托盘菜单、应用菜单等等。

接下来给上面的应用添加一个托盘菜单。这里我们需要使用Electron中的remote模块。remote模块提供了一种在渲染器进程和主进程之间进行进程间通讯的简便途径。
Electron中,与GUI相关的模块(dialog、menu等)只存在于主进程,而不在渲染进程中,想要从渲染进程使用它们还必须通过ipcRenerer模块来给主进程发送进程休息。但是remote模块,可以调用主进程对象的方法,而无需显式的发送进程消息。将remote模块引入的时候,这个模块是在主进程中被实例化的,所以它们的方法也会在主进程中被执行。实际开发中,这个行为是main.js在远程地请求index.js中的原生GUI模块,然后又在main.js中调用GUI的方法。这么做的话,你需要在index.js中将BrowserWindow模块引入,然后实例化一个新的流浪器窗口。其实在主进程中有一个同步的调用,实际上时这个调用创建了新的流浪器窗口。

下面实现创建一个菜单,并在渲染器进程中将它绑定到一个托盘图标上:

//index.js
var electron = require('electron');
var remote = electron.remote;
var Tray = remote.Tray;
var Menu = remote.Menu;
var path = require('path');

if(process.platform === 'darwin'){
    //如果是苹果系统 托盘菜单的图标
    trayIcon = new Tray(path.join(__dirname,'img/tray-iconTemplate.png'));
}else{
    trayIcon = new Tray(path.join(__dirname,'img/tray-icon-alt.png'));
}

var trayMenuTemplate =[
    {
        lable:'Sound Machine',
        enabled:false
   },
   {
        lable:'Settings',
        click:function(){
                ipcRenderer.send('open-settings-window');
     },
     {
        lable:'Quit',
        click:function(){
                ipcRenderer.send('close-main-window')
    }
    }
   }
];
var trayMenu = Menu.buildFormTemplate(trayMenuTemplate);
trayIcon.setContextMenu(trayMenu);

OS X支持图片模板(将图片文件名以“Template”结尾,就会被定义为图片模板)

Tray

Tray用来表示一个图标,这个图标处于正在运行的系统的通知区(如右下角的托盘菜单区),通常被添加到一个contextMenu上。about more

菜单项模块允许你向应用或menu添加选项。

electron-packager

electron 可以使用electron-packager来实现打包。
electron-packager 可以用来打包 Electron 应用。生成各个平台的最终可运行文件,如 .app 和 .exe。more about electron-packager
首先将electron-packager作为development dependency安装:

npm install --save-dev electron-packager

命令规则:

electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]
  • : 项目的位置
  • : 应用名
  • –platform=: 要构建哪个平台的应用(darwin、win32、linux、all)
  • –arch=: architecture决定了使用x86(ia32)还是x64(x64)还是两个都可以(all)
  • –icon=: 指定应用图标(Mac为图片格式为.icns,Window为.ico或.png)
  • –out : 指定输出的目录
  • –version=: 决定了使用的Electron版本

例:

//package.json
"scripts": {
    "start": "electron .",
    "package":"electron-packager ./ soundMachine --platform=all --arch=all --out ~/Desktop/SoundMachine --version=0.30.2 --overwrite --icon=./app/img/app-icon.ico"
  },

注释:~/是应用程序根目录

第一次打包应用需要的时间较长,因为所有平台的二进制文件都需要下载,之后打包引用会比较快。

接下来是另一种简单的打包方法:

根据官方的打包发布方法,我们可以这样理解。官方提供了一个套模板和框架,我们只需要把我们用到的项目源文件通过命令打包放置在模板之中就可以把项目发布出去而实现快速的打包发布。

  • 使用命令行完成打包工具的的安装:npm install -g asar
  • 在命令行中输入:asar pack your-app app.asar
    例如:
    这里写图片描述
    这里写图片描述这里写图片描述
    打包好的.asar压缩文件就是我们发布需要的素材,这里介绍的打包的特性就是只读而不可修改,同时通过asar命令是可以查看当前压缩包种的文件(命令行:asar list app.asar)。
  • 新增没有打包的文件到压缩包种:asar pack app app.asar –unpack *.node
    打包完成后就可以进行下一步的发布了。从官网上下载模板(如:electron-v1.3.2-win32-ia32.zip),将我们打包好的文件放入到解压后的模板的\resources目录下,接下来可以双击electron.exe来查看了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值