非常感谢您的支持。根据您的建议和一些研究,我开发了一个可行的解决方案,并愿意与您分享。
这两个链接对我帮助很大:
https://github.com/electron/electron/issues/11865 https://github.com/electron/electron/issues/11865
https://github.com/electron/electron/issues/10764 https://github.com/electron/electron/issues/10764
尤其是 MarshallOfSound 的这篇文章 - 描述得很清楚:
- 主进程中的钩子事件
- 使用设备列表向渲染器进程发送消息
- 在渲染器进程中显示 UI
- 将选定的设备发送到主进程
- 呼叫回调
要获取有关主进程和渲染器进程、事件及其 API 的更多信息,请阅读以下内容:
https://www.electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes https://www.electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes
https://www.electronjs.org/docs/api/ipc-main https://www.electronjs.org/docs/api/ipc-main
https://www.electronjs.org/docs/api/web-contents#contentssendchannel-args https://www.electronjs.org/docs/api/web-contents#contentssendchannel-args
https://www.electronjs.org/docs/api/ipc-renderer https://www.electronjs.org/docs/api/ipc-renderer
https:// Electronjs.org/docs/api/web-contents#event-select-bluetooth-device https://electronjs.org/docs/api/web-contents#event-select-bluetooth-device(已由 Gerrit 发布)
https://www.electronjs.org/docs/api/structs/bluetooth-device https://www.electronjs.org/docs/api/structures/bluetooth-device
对于我的应用程序,我想要一个设备选择器,如 Chrome 中所示。我想要实现的顺序是:
- 开始申请
- 搜索设备
- 弹出设备选择器
- 选择设备
- 设备选择器关闭
- 查看应用程序中的数据
教程流程代码参考及代码片段:
电子应用程序:main.js(主进程)renderer.js(渲染进程)devicepicker GUI:devicepicker.js(渲染进程)devicepicker.html&layout.css(GUI)
1)使用 GUI(我使用了两个)和脚本创建 devicepicker
2)在你的 main.js 中创建一个select-bluetooth-device
内的事件'ready'
您的应用程序对象的事件(上面链接中的文档)当您开始时navigator.bluetooth.requestDevice()
在 renderer.js 中,事件 get 被触发,并且设备列表位于主进程中。和console.log(deviceList)
它在外壳中可见。要处理它,您需要将其发送到渲染器进程(您的应用程序窗口)。
3)为了实现这一目标,我们实施webContents.send
webContents.on 事件中的 BrowserWindow 对象。现在主进程每次通过通道发现新设备时都会发送一个设备列表channelForBluetoothDeviceList
4)在 renderer.js 中创建startDevicePicker()
. devicePicker()
必须在与以下相同的函数中启动navigator.bluetooth.requestDevice()
. startDevicePicker()
实例化一个新的BrowserWindow()
加载的对象devicepicker.html
5)要从主进程获取列表ipcRenderer.on()
侦听器必须在中实现startDevicePicker()
那个听channelForBluetoothDeviceList
我们的主要流程的通道。现在我们可以在电子应用程序(渲染器过程)中获取列表。要将其发送到 devicepicker UI,我们需要将其从我们的电子应用程序(渲染器进程)转发到 devicepicker(也是渲染器进程)
6)为了实现这一目标,我们需要ipcRenderer.sendTo()
发件人在devicePicker()
,它将消息从渲染器进程转发到特定的其他渲染器进程。频道附加bluetoothDeviceDiscoverList
我们需要BrowserWindow.id
设备选择器的。由于我们刚刚实例化了它,因此我们可以使用 devicepicker 对象。我有一个只发送一次的设备,主进程比 devicepicker 的构建速度更快,并且我的列表从未发送到 devicepicker。所以我用了一个Promise()
等待ipcRenderer.sendTo()
直到设备选择器可供使用。
7)为了在我们的 devicepicker GUI 上接收设备列表,我们需要监听bluetoothDeviceDiscoverList
with ipcRenderer.on()
(devicepicker.js)。我现在将设备列表插入到<option>
对于 devicepicker,您当然可以使用其他元素(devicepicker.html)。请注意:实现一个查询,将发送的列表与当前的列表进行比较。否则,您会获得多个设备,并且您的选择会变得很长。我还需要这样做,它还没有完成:-)
8)要选择一个设备navigator.bluetooth.requestDevice()
(renderer.js) 得到解决,我们需要发回BluetoothDevice.deviceId
我们选择的设备到主进程,我们使用 deviceId 作为回调参数回调“callback”(main.js)。
9)现在我们可以使用ipcRenderer.sendTo()
发送所选的BluetoothDevice.deviceId
到主进程(devicepicker.js)。
10)在我们的电子应用程序的主进程(main.js)中,我们监听通道channelForSelectingDevice
with ipcMain.on()
并回调收到的BluetoothDevice.deviceId
。设备发现停止,navigator.bluetooth.requestDevice()
得到解决,我们在应用程序(renderer.js)中从设备接收数据。要取消设备的发现,请使用ipcMain.on()
在另一个频道channelForTerminationSignal
只是主进程(main.js)的一个信号,例如单击(devicepicker.js)后并使用空字符串调用回调(如文档中所写)
我承认如果没有设备选择器,它可以更简单地完成。然后只需将设备列表从主进程(main.js)发送到您的应用程序(渲染器进程)即可。但这对我理解电子过程有很大帮助。我希望本教程对您有用:-)!
main.js
const { ipcMain, app, BrowserWindow } = require('electron')
let win = null;
var callbackForBluetoothEvent = null;
// Create the browser window.
function createWindow () {
win = new BrowserWindow({
webPreferences: {
nodeIntegration: true //to activate require()
}
})
win.maximize()
win.show()
//This sender sends the devicelist from the main process to all renderer processes
win.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
event.preventDefault(); //important, otherwise first available device will be selected
console.log(deviceList); //if you want to see the devices in the shell
let bluetoothDeviceList = deviceList;
callbackForBluetoothEvent = callback; //to make it accessible outside createWindow()
win.webContents.send('channelForBluetoothDeviceList', bluetoothDeviceList);
});
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
//cancels Discovery
ipcMain.on('channelForTerminationSignal', _ => {
callbackForBluetoothEvent(''); //reference to callback of win.webContents.on('select-bluetooth-device'...)
console.log("Discovery cancelled");
});
//resolves navigator.bluetooth.requestDevice() and stops device discovery
ipcMain.on('channelForSelectingDevice', (event, DeviceId) => {
callbackForBluetoothEvent(sentDeviceId); //reference to callback of win.webContents.on('select-bluetooth-device'...)
console.log("Device selected, discovery finished");
})
renderer.js
function discoverDevice() {
navigator.bluetooth.requestDevice()
startDevicepicker()
}
function startDevicepicker(){
let devicepicker = null;
let mainProcessDeviceList = null;
devicepicker = new BrowserWindow({
width: 350,
height: 270,
show: false, //needed to resolve promise devicepickerStarted()
webPreferences: {
nodeIntegration: true
}
})
devicepicker.loadFile('devicePicker.html');
//electron application listens for the devicelist from main process
ipcRenderer.on('channelForBluetoothDeviceList', (event, list) => {
mainProcessDeviceList = list;
devicepickerStarted.then(_=> {
console.log("Promise resolved!");
ipcRenderer.sendTo(devicepicker.webContents.id, 'bluetoothDeviceDiscoverList', mainProcessDeviceList);
})
})
//Promise that ensures that devicepicker GUI gets the list if the device only sends once
var devicepickerStarted = new Promise(
function (resolve, reject) {
console.log("Promise started");
devicepicker.once('ready-to-show', () => {
devicepicker.show();
resolve();
console.log("Devicepicker is ready!")
})
}
)
//remove listeners after closing devicepicker
devicepicker.on('closed', _ => {
devicepicker = null;
ipcRenderer.removeAllListeners('channelForBluetoothDeviceList');
ipcRenderer.removeAllListeners('currentWindowId');
ipcRenderer.removeAllListeners('receivedDeviceList');
})
}
devicepicker.js
//save received list here
var myDeviceList = new Array();
//Html elements
const devicePickerSelection = document.getElementById("devicePickerSelection");
const buttonSelect = document.getElementById("Select");
const buttonCancel = document.getElementById("Cancel");
//eventListeners for buttons
buttonSelect.addEventListener('click', selectFromDevicePicker);
buttonCancel.addEventListener('click', cancelDevicePicker);
//listens for deviceList
ipcRenderer.on('receivedDeviceList', (event, bluetoothDeviceDiscoverList) => {
console.log("list arrived!")
//code: add list to html element
});
function selectFromDevicePicker() {
let selectedDevice = devicePickerSelection.value;
let deviceId = //depends on where you save the BluetoothDevice.deviceId values
//sends deviceId to main process for callback to resolve navigator.bluetooth.requestDevice()
ipcRenderer.send('channelForSelectingDevice', deviceId);
ipcRenderer.removeAllListeners('receivedDeviceList');
closeDevicePicker();
}
function cancelDevicePicker() {
ipcRenderer.send('channelForTerminationSignal');
closeDevicePicker();
}
function closeDevicePicker() {
myDevicePicker.close();
}}