提问人:pieterhop 提问时间:3/23/2023 更新时间:3/23/2023 访问量:301
Electron / NodeJS:如何将异步消息从 IpcMain 发送到 IpcRenderer
Electron / NodeJS: How to send async messages from IpcMain to IpcRenderer
问:
正如标题所述,我想知道如何发送源自 python 进程的消息可以间歇性地发送到我的渲染器进程。我尝试了很多技巧,但我似乎错过了我认为的重点。
首先,我的 js 文件,我在其中对预加载中注册的 API 进行 API 调用(下一个代码段)。我发起呼叫并等待多条消息回复。
console.log('Start installation!');
window.mainAPI.startInstallation('hello');
window.addEventListener("message", (event) => {
console.log(event);
});
Preload.js / IpcRenderer。它涉及最后一个“端点”或通道。
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('mainAPI', {
openConnection: (data) => ipcRenderer.invoke('openConnection', data).then((result) => {
window.postMessage(result)
}),
saveConfig: (data) => ipcRenderer.invoke('saveConfig', data).then((result) => {
window.postMessage(result)
}),
saveModuleSelection: (data) => ipcRenderer.invoke('saveModuleSelection', data).then((result) => {
window.postMessage(result)
}),
startInstallation: (data) => ipcRenderer.invoke('startInstallation', data).then((result) => {
window.postMessage(result)
})
});
Main.js(为清楚起见,完整粘贴):
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const { PythonShell } = require('python-shell')
const fs = require('fs');
function startPython(event, data) {
let pyshell = new PythonShell('install.py', {mode: 'text'});
pyshell.send(JSON.stringify(data));
pyshell.on('message', function(message) {
// HOW TO SEND MESSAGES TO EVENT LISTENER FROM HERE
console.log('Sending: ' + message);
event.sender.send('startInstallation', message); // doesnt work
});
pyshell.end(function(err, code, signal) {
var res = {
'error': []
};
if (err) {
res.error.push(err);
throw err;
}
res.code = code;
res.signal = signal;
console.log('The exit code was: ' + code);
console.log('The exit signal was: ' + signal);
console.log('finished');
return JSON.stringify(res)
});
}
function write_env_vars(filename, string) {
try {
fs.writeFileSync(`./backend/${filename}.env`, string, 'utf-8');
return true
} catch(e) {
console.log(e);
return false
}
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
const createWindow = () => {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
win.loadFile('inethi/front/index.html')
}
var credentials, config, modules;
app.whenReady().then(() => {
ipcMain.handle('openConnection', async (event, args) => {
await sleep(200);
// add call to test_server_connection.py here.
credentials = JSON.parse(args);
console.log(credentials);
return write_env_vars('credentials', `CRED_IP_ADDRESS=${credentials.ip}\nCRED_USERNAME=${credentials.username}\nCRED_PASSWORD=${credentials.password}`);
})
ipcMain.handle('saveConfig', async (event, args) => {
await sleep(200);
config = JSON.parse(args);
console.log(config);
return write_env_vars('config', `CONF_STORAGE_PATH=${config.storagepath}\nCONF_DOMAIN_NAME=${config.domainname}\nCONF_HTTPS=${config.https}\nCONF_MASTER_PASSWORD=${config.master}\n`);
})
ipcMain.handle('saveModuleSelection', async (event, args) => {
await sleep(200);
modules = JSON.parse(args);
console.log(modules);
return write_env_vars('modules', `MODS_DOCKER=${modules.docker}\nMODS_TRAEFIK=${modules.traefik}\nMODS_NGINX=${modules.nginx}\nMODS_KEYCLOAK=${modules.keycloak}\nMODS_NEXTCLOUD=${modules.nextcloud}\nMODS_JELLYFIN=${modules.jellyfin}\nMODS_WORDPRESS=${modules.wordpress}\nMODS_PEERTUBE=${modules.peertube}\nMODS_PAUM=${modules.paum}\nMODS_RADIUSDESK=${modules.radiusdesk}\n`);
})
ipcMain.handle('startInstallation', async (event, args) => {
console.log('Starting installation');
const data = {
'credentials': credentials,
'config': config,
'modules': modules
}
await sleep(3000);
const res = startPython(event, data);
console.log('Installation done');
console.log(res);
return true
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
所以问题是前三个端点或通道工作正常。我返回 true,eventListener 捕获消息。但是,对于最后一个通道,即“startInstallation”通道,它不起作用,我怀疑它与(异步 - 从未真正理解它)python脚本有关。我的目标是 1) 在 pyshell.on(...) 部分中发送间歇性(打印)消息(在 main.js 文件的顶部),如果脚本成功运行,则发送带有 true 的最终消息。
任何帮助将不胜感激!
答:
2赞
Ravi L
3/23/2023
#1
在您的 ContextBridge 中,您似乎没有公开接收事件或处理事件的方法。IPCRenderer
如果将类似这样的内容添加到 contextBridge 代码中
onPythonEvent: (channel, func) => {
let validChannels = ["fromPython"];
if (validChannels.includes(channel)) {
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
然后,在客户端代码中,您应该能够像这样使用它:
window.mainAPI.onPythonEvent("fromPython",(val)=>{console.log(val)});
然后,你可以像这样触发它,将值直接发送到渲染器窗口:startPythonMethod
pyshell.on('message', function(message) {
console.log('Sending: ' + message);
win.webContents.send('fromPython', data)
});
您可能需要重构为先创建 browserWindow,然后注册 ipc 处理程序。您将窗口作为参数传递给您拥有的 startPython 方法。
const win = createWindow();
ipcMain.handle('startInstallation', async (event, args) => {
//...
const res = startPython(win);
// ...
})
评论