TLDR: Best way to close a renderer window from main (or the renderer itself).
I am splitting up a process between X invisible renderer windows. When a renderer finishes its work, I want it to send an event to the main process and then close. Right now I have
//invisibleRenderer.js
doStuff().then(() => {
ipcRenderer.invoke('finish');
}
What is the best way to close the window? Is it in the ipcMain.handle? I can't figure out which method to call with which id.
ipcMain.handle('finish', (event, args) => {
//do what? event.frameId, event.processId, event.sender.id...
})
You can use BrowserWindow.fromId(event.sender.id) to get a reference to the window from where the IPC call originated, as demonstrated in this question.
This works fine in the main process. The following example opens 5 windows and loads the same contents. Each renderer script invocation picks a random delay (to simulate some work) and then dispatches the finish message to the main process. There, the main process picks up the BrowserWindow reference using event.sender.id and closes the corresponding window.
app.js
const {app, BrowserWindow, ipcMain} = require("electron");
const windows = [];
app.on("ready", function() {
for (let i=0; i<5; i++) {
let win;
win = new BrowserWindow( {
width: 300,
height: 300,
x: i*100,
y: i*100,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
},
});
win.loadURL(`file://${__dirname}/index.html`);
windows.push(win);
}
});
ipcMain.handle("finish", (event, args) => {
const win = BrowserWindow.fromId(event.sender.id);
win.close();
});
index.html
<html>
<body>
Renderer
</body>
<script type="text/javascript" src="renderer.js"></script>
</html>
renderer.js
const {ipcRenderer} = require("electron");
// Simulate some work by choosing a random delay..
const delay = Math.round(Math.random()*10000);
setTimeout(() => ipcRenderer.invoke("finish"), delay);
Related
After a whole day trying to make this changes without succeed, I'd like to ask for help.
Based on this example from codepen, I'm trying to change the background color from 'black' to the color '#ECEFF1' which is the same color from the background I use on my page.
Code JS:
(async ({
document,
setTimeout,
clearTimeout,
addEventListener,
installFont,
Fireworks,
Math
}) => {
const { body } = document;
await installFont(
"Noto Sans JP",
"#import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght#100&display=swap');"
);
const isMobile = "ontouchend" in window;
const touchText = document.createElement("div");
touchText.innerText = isMobile ? "touch to start" : "click to start";
touchText.classList.add("start");
const overlay = document.createElement("div");
overlay.classList.add("overlay");
overlay.appendChild(touchText);
body.appendChild(overlay);
const triggerKey = isMobile ? "touchend" : "click";
overlay.addEventListener(triggerKey, () => {
body.removeChild(overlay);
const get_rect = () => [
Math.min(1200, window.innerWidth),
Math.min(400, window.innerHeight)
];
const fireworks = Fireworks.init();
fireworks.amount(7);
document.body.appendChild(fireworks.view);
const resize = () => requestAnimationFrame(() => fireworks.resize(...get_rect()));
resize();
let id;
window.addEventListener('resize', () => {
clearTimeout(id);
id = setTimeout(resize, 100);
});
fireworks.start();
});
})(window);
Code running:
https://codepen.io/nuton0413/pen/QWNgzKO
On codepen, in the code settings, there is a library being accessed to sync and run the function.
Library:
https://k-nuton.github.io/fireworks_ts/dist/fireworks.min.js?date=202208070212
I've already tried to apply the background color through CSS or through the browser's development tool and nothing happens.
Any modification made on this page can only be done directly through the JS file.
When clicking on the phrase on the home page "click to start", I need to remove the loop that the function execute. The Fireworks will be executed just by clicking a button. Fireworks will only play when the button is clicked. Once per click, no loop.
The whole JS code has only 55 lines of code.
I tried changing the background color to the desired background color, but everything went wrong.
What should I do please? Any Light?
I am trying to have my communicate between different rendering processes in electron. Rather than have multiple windows I am using electron-tabs which can be found here. To start I simply want my main window to be able to send a message using ipcRenderer to each tab. With multiple windows, you could store each window in a global variable like this stack overflow question and then use the web-contents to send a message.
UPDATE: In the electron-tabs documentation, it suggests you can use the tabs webview the same as you would a windows web-contents (see here). Unfortunately, either I am missing something or it is a little more complicated.
For the code: I have a main window mainWindow.html
<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
<div class="etabs-tabs" ></div>
<div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">
<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;
var tabs = [];
// Create a new tabGroup
let tabGroup = new TabGroup();
// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
tabs.push(tabGroup.addTab({
src: 'file://' + __dirname + '/tab.html',
webviewAttributes: {
nodeintegration: true
}
}));
// Send test message to tabs... !!!! DOES NOT WORK !!!!
circuitTabs[i].webview.send('testMessage',i);
}
</script>
</html>
tab.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
const electron = require('electron') ;
const {ipcRenderer} = electron;
const { remote } = require('electron');
ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });
</script>
</body>
</html>
main.js
const electron = require('electron');
const {app, BrowserWindow} = electron;
let mainWindow = null;
app.on('ready', function () {
mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
mainWindow.show();
mainWindow.focus();
});
Try following this:
<webview>.send(channel, ...args)
channel String
...args any[]
Returns Promise<void>
Send an asynchronous message to renderer process via channel, you can also send arbitrary arguments. The renderer process can handle the message by listening to the channel event with the ipcRenderer module.
See webContents.send for examples.
Look at the docs below for more information.
https://www.electronjs.org/docs/api/webview-tag#webviewsendchannel-args
I figured it out. You can get the webcontentsID from the tab and use ipcRenderer.sendTo function.
For the code: I have a main window mainWindow.html
<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
<div class="etabs-tabs" ></div>
<div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">
<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;
var tabs = [];
// Create a new tabGroup
let tabGroup = new TabGroup();
// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
tabs.push(tabGroup.addTab({
src: 'file://' + __dirname + '/tab.html',
webviewAttributes: {
nodeintegration: true
}
}));
// Send test message to tabs...
var webContentsID = tabs[i].webview.getWebContentsId();
ipcRenderer.sendTo(webContentsID,'testMessage');
}
</script>
</html>
tab.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
const electron = require('electron') ;
const {ipcRenderer} = electron;
const { remote } = require('electron');
ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });
</script>
</body>
</html>
main.js
const electron = require('electron');
const {app, BrowserWindow} = electron;
let mainWindow = null;
app.on('ready', function () {
mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
mainWindow.show();
mainWindow.focus();
});
I have tried many things and can not seem to get the robotjs code to execute even with rebuild, and other examples I've seen to try.
I still get the Loading non-context-aware native module in renderer error.
I have a button, and on click of the button I want the robotjs code to be executed.
Here is my current code :
bot.js
const robot = require('robotjs')
const electron = require('electron')
const ipc = electron.ipcRenderer
const Bot = () => {
const button = document.getElementById('start');
// button.addEventListener('click', () => console.log('Click'))
button.addEventListener('click', function (e) {
// Get mouse position.
var mouse = robot.getMousePos();
// Get pixel color in hex format.
var hex = robot.getPixelColor(mouse.x, mouse.y);
console.log("#" + hex + " at x:" + mouse.x + " y:" + mouse.y);
});
}
Bot();
My main file :
index.js
const { app, BrowserWindow, ipcMain } = require('electron');
const robot = require('robotjs')
const path = require('path');
// const Bot = require('./bot')
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
}
});
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
// 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);
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
Here is the index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div class="App">
<div class="Header">
<h1>Checkout-Bot</h1>
</div>
<div class="Wrapper">
<p>Click Start</p>
<button id="start">Start</button>
</div>
</div>
<script src="bot.js">
Bot();
</script>
</body>
</html>
Have you read this issue on Github?
They removed the ability to use non-NAPI and non-context aware modules.
If you're not using Electron 11 yet, you can still use those modules, if you add the following line in the main process:
app.allowRendererProcessReuse = false
As an alternative solution, Try https://www.npmjs.com/package/#nut-tree/nut-js instead of robotjs. Electron (Last checked with 18.2.0) works well with nutjs.
Please refer to https://www.npmjs.com/package/#nut-tree/nut-js & https://nutjs.dev/docs/tutorial-first_steps/get_moving to see the documentation and working samples.
I am creating an electron application with a custom window bar in place of the default Windows one. I need to know when the window is maximized or un-maximized in order to change the icon on the window bar to reflect the window's state.
maximize / unmaximized event is availble for browserwindow. https://github.com/electron/electron/blob/master/docs/api/browser-window.md#event-maximize
you can use:
const { remote } = require("electron");
var win = remote.BrowserWindow.getFocusedWindow();
if(win.isFullScreen()) {
// your code here
}
or :
const { remote } = require("electron");
var win = remote.BrowserWindow.getFocusedWindow();
if(win.isMaximized()) {
// your code here
}
I have been following through a tutorial for Electron and I am using a html document in a new BrowserWindow as a pop-up. The problem is, when I use an onclick handler on a html tag, it doesn't seem to fire. If I open the developer tools, I can type my function into the console and it behaves as expected!
Not sure if I'm missing something really obvious here but would appreciate anyone who could help me solve the issue.
Here is the body tag from the html for BrowserWindow:
<body id="page-info">
<main class="info-content">
<h3 class="page-headline">Wisdom Pet</h3>
<p>Welcome to the Wisdom Pet App.<br>
<strong>Version 1.0.0</strong><br>
<small>Built with love by Ray Villalobos</small>
</p>
<p>close window</p>
</main>
<script type="text/javascript">enter code here
var electron = require('electron');
var shell = electron.shell;
var ipc = electron.ipcRenderer;
/* Also tried using event listeners without html onclick
var closeAbout= document.getElementById("closeAbout");
closeAbout.addEventListener("click", function(){
send();
});
*/
function send(){
console.log("sending...");
ipc.sendSync('closeInfoWindow')
}
</script>
And here is the main.js file:
var Electron = require("electron");
var Menu = Electron.Menu;
var BrowserWindow = Electron.BrowserWindow;
var app = Electron.app;
var ipc = Electron.ipcMain;
var myAppMenu, menuTemplate;
app.on("ready", function(){
var appWindow = new BrowserWindow({
show: false
});
appWindow.loadURL("file://" + __dirname + "/index.html");
var infoWindow = new BrowserWindow({
show: false,
width: 400,
height: 300,
frame: false,
transparent: true,
});
infoWindow.loadURL('file://' + __dirname + '/info.html');
appWindow.once("ready-to-show", function(){
appWindow.show();
});
ipc.on("openInfoWindow", function(event, arg){
event.returnValue = "";
infoWindow.show();
})
ipc.on('closeInfoWindow', function(event, arg){
event.returnValue = '';
infoWindow.hide();
}); //closeInfoWindow
});
The ipc.on("openInfoWindow") work fine from the main window, but once the infoWindow is open, I can't close it unless I send the code directly from the developer tools.