I'm working on an Electron-based application, and I don't have much experience with it or JavaScript or Node.js. Currently, I just want to open a window dialoge by a click on a button. But when i click the button nothing happens.
This is my package.json:
{
"name": "Routingtable_Splitter",
"version": "1.0.0",
"description": "...",
"main": "main.js",
"scripts": {
"start": "electron .",
"pack": "build --dir",
"dist": "build",
"package-win": "electron-packager . Splitter --overwrite --asar=true --platform=win32 --arch=ia32"
},
"build": {
"win": {
"target": "squirrel",
"icon": "build/icon.ico"
}
},
"author": "Robert Malleschitz",
"license": "ISC",
"devDependencies": {
"dialog": "^0.3.1",
"document": "^0.4.7",
"electron": "^2.0.4",
"electron-builder": "^20.19.1",
"electron-packager": "^12.1.0",
"electron-winstaller": "^2.6.4",
"fs": "0.0.1-security",
"remote": "^0.2.6"
},
"dependencies": {}
}
this is my HTML code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tool</title>
</head>
<body>
<h1>Routingtable Splitter</h1>
<main>
<div>
<div style="text-align:right;">
<input type="text" placeholder="Bitte Routingtabelle laden" style="width: 260px" id="actual-file"/>
<input type="submit" value="Routingtabelle laden" id="load-file"/>
<br><br>
<textarea id="editor" style="width: 400px; height: 300px;"></textarea><br />
<input type="button" id="splitTable" value="Split" />
</div>
</div>
</main>
</body>
<script src="renderer.js"></script>
</html>
this is my main.js:
const {app, BrowserWindow} = require('electron')
const {Menu} = require('electron')
const {dialog} = require('electron')
const {remote} = require('electron')
const {document} = require('electron')
const {ipcMain} = require('electron')
var path = require('path')
var electronInstaller = require('electron-winstaller')
let win = null;
function createWindow () {
win = new BrowserWindow({
width: 1200,
height: 1000,
})
win.loadFile('index.html')
}
const menuTemplate = [
{
label: 'Datei',
submenu: [
{
label: 'Lade', click: () => {getRoutingTable();}
},
{
label: 'Beenden', click: () => {app.quit();}
}
]
}
];
function getRoutingTable(){
dialog.showOpenDialog(
{
defaultPath: 'c./',
filters: [
{ name: 'Text Files', extensions: ['txt'] }
],
properties:['openFile']
})
}
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
app.on('ready', createWindow)
{
console.log('app')
}
ipcMain.on('ldFileBtn', function (event, arg) {
console.log('ipcMain');
getRoutingTable();
event.sender.send("btnclick-task-finished", "yes");
});
This is my renderer.js:
const ipcRen = require('electron').ipcRenderer;
const ldFileBtn = document.getElementById('load-file');
ldFileBtn.addEventlistener('click', function() {
console.log('btn_click')
var arg = 'secondparam';
ipcRen.send('ldFileBtn', arg);
});
Why won't the button work? I hope someone can help me. Thanks in advance.
Related
Hello Big Brain Internet People,
Problem:
I'm trying to build an app for my raspberry pi but I seem to be having a problem with my serialports. I can build and run the program on my host machine fine but when I create an appimage using Electron-Builder my target device seems to have a problem using the serialport module. I assume it has something to do with raspberry pi using an ARM processor and my host desktop is using 64-bit, but I'm not sure how to solve the problem.
This is my first project with nodejs/electron/html/js so any help would be greatly appreciated!
Host System:
Ubuntu 19.10
Arch: 64-bit
Node: v10.15.2
Target System:
Raspberry Pi 4
Raspbian v10
Arch: Armv7l
Node: v12.15.1 - armv7 version
Error:
/tmp/.mount_sec-trTc9j6w/resources/app.asar/node_modules/bindings/bindings.js:121 Uncaught Error: /tmp/.org.chromium.Chromium.PmaAji: wrong ELF class: ELFCLASS64
at process.func [as dlopen] (electron/js2c/asar.js:138)
at Object.Module._extensions..node (internal/modules/cjs/loader.js:881)
at Object.func [as .node] (electron/js2c/asar.js:147)
at Module.load (internal/modules/cjs/loader.js:701)
at tryModuleLoad (internal/modules/cjs/loader.js:633)
at Function.Module._load (internal/modules/cjs/loader.js:625)
at Module.require (internal/modules/cjs/loader.js:739)
at require (internal/modules/cjs/helpers.js:14)
at bindings (/tmp/.mount_sec-trTc9j6w/resources/app.asar/node_modules/bindings/bindings.js:112)
at Object.<anonymous> (/tmp/.mount_sec-trTc9j6w/resources/app.asar/node_modules/#serialport/bindings/lib/linux.js:2)
package.json
{
"name": "sec-transmitter",
"version": "0.1.0",
"description": "",
"main": "main.js",
"dependencies": {
"modbus-serial": "^7.7.4",
"serialport": "^7.1.5"
},
"build": {
"appId": "sec.com",
"linux": {
"category": "Network",
"target": {
"target": "AppImage",
"arch": "armv7l"
}
}
},
"devDependencies": {
"electron": "^5.0.5",
"electron-builder": "^22.2.0",
"electron-rebuild": "^1.8.8"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "electron .",
"install": "electron-rebuild",
"dist": "electron-builder",
"postinstall": "electron-builder install-app-deps"
},
"keywords": [],
"author": "",
"license": "ISC"
}
main.js
const { app, BrowserWindow} = require('electron');
const path = require('path');
function createWindow() {
//create the browser window
let win = new BrowserWindow({
width: 1024,
height: 600,
center: true,
fullscreen: true,
webPreferences: {
nodeIntegration: true
}
})
//and load the index html of the app
win.loadFile('./source/index.html')
win.webContents.openDevTools();
}
app.on('ready', createWindow)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="../assets/css/main.css">
<script src="./index.js"></script>
</head>
<body>
<div class="inner">
<img class="home_button" src="../assets/images/home.png" alt="Home Button">
<div class="Progressbar">
<h1>0</h1>
<progress value="10" max="100" id="pgb1"></progress>
</div>
</div>
<!--<h2>100 </h2>-->
<!-- <h2>NO3</h2>*/-->
`
</div>
<div class="Progressbar">
<h1>0</h1>
<progress value="32" max="100" id="pgb2"></progress>
</div>
<div class="Progressbar">
<h1>0</h1>
<progress value="56" max="100"name="pgb3"></progress>
</div>
<div class="Progressbar">
<h1>0</h1>
<progress value="80" max="100"name="pgb4"></progress>
</div>
<div class="Progressbar">
<h1>0</h1>
<progress value="100" max="100"name="pgb5"></progress>
</div>
</div>
<div class="Ping">
<button id="pingBtn">Ping Device</button>
<button id="closeBtn" onclick="quitApplication()">Exit</button>
</div>
<!--<script src="./modbus_headers.js"></script>
<script src="./modbusrtu_commands.js"></script> -->
<script src="./bgloop.js"></script>
</body>
</html>
bgloop.js
const ModbusRTU = require('../node_modules/modbus-serial');
//create empty modbus client
const client = new ModbusRTU(
);
//open connection to serial port
client.connectRTUBuffered("/dev/ttyUSB0",
{
baudRate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 2,
autoOpen: false,
});
//set timeout, if slave did not reply back
client.setTimeout(500);
//List of ID's
const IDList = [2];
const getPingDevices = async (sensors) => {
try{
//get response from all sensors
for(let sensor of sensors ) {
//output response to console
console.log(await getPingDevice(sensor));
//wait 100 ms before pinging another sensor
await sleep(100);
}
}catch(e){
//if error, handle them here (it should not)
console.log(e);
}finally{
//close ports
//client.close();
// after all the data from the sensors repear again
setImmediate(() => {
getPingDevices(IDList)
})
}
}
const getPingDevice = async (device_id) => {
try{
//set ID of slave
await client.setID(device_id);
//read the ping register at address 3000
let val = await client.readHoldingRegisters(3001,1)
client.close();
//return the value
return val.data[0];
}catch(e){
//if error return -1
return -1
}
}
const sleep = (ms) => new Promise(resolve => setTimeout(resolve,ms));
//start running
getPingDevices(IDList);
I'm making a game client and I have it where you can launch the game from the browser like 'mygame://whatever'. I'm doing some debugging and I want it to show 'Received this data: whatever'. It does launch the app but it just shows undefined in the data box. How do I fix this?
main.js:
const {app, BrowserWindow} = require('electron');
let mainWindow;
function createWindow () {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
mainWindow.loadFile('index.html');
}
app.on('ready', createWindow);
var link;
app.on('open-url', function (event, data) {
event.preventDefault();
link = data;
});
app.setAsDefaultProtocolClient('mygame');
// Export so you can access it from the renderer thread
module.exports.getLink = () => link;
index.html:
<!DOCTYPE html>
<html>
<body>
<p>Received this data <input id="data"></p>
<script>
const {getLink} = require('electron').remote.require('./main.js');
document.querySelector('#data').value = getLink();
</script>
</body>
</html>
package.json:
{
"name": "mygame",
"version": "1.0.0-dev",
"description": "Development copy.",
"main": "main.js",
"scripts": {
"start": "electron .",
"dist": "electron-builder"
},
"author": "Me",
"build": {
"protocols": {
"name": "mygame-protocol",
"schemes": [
"mygame"
]
}
},
"devDependencies": {
"electron": "^6.0.9",
"electron-builder": "^21.2.0"
}
}
All help is appreciated!
this code work only for iOS
app.on('open-url', function (event, data) {
event.preventDefault();
link = data;
});
In win32 you must use that code
process.argv[1]
so you can add that code in createWindow() like that:
function createWindow () {
// Crea la finestra del browser
let win = new BrowserWindow({
width: 800,
height: 600,
});
//other stuff
link = process.argv[1];
}
Resources that might be useful
electron url scheme “open-url” event
I have a very simple Electron app, using version 5.0.1.
Here is my index.html file:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>Webgl</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script src="./initialization.js"></script>
<link rel="stylesheet" type="text/css" href="application.css">
</head>
<body>
<script>
initialization();
</script>
</html>
Then my main.js file:
const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')
let win
function createWindow () {
// Create the browser window.
win = new BrowserWindow({ width: 1280,
height: 720,
nodeIntegration: true,
resizable: false,
maximizable: false })
// and load the index.html of the app.
win.loadFile('index.html')
// Open the DevTools.
win.webContents.openDevTools()
// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null
})
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
my package.json file:
{
"name": "estimation",
"version": "1.0.0",
"description": "estimation app",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^5.0.1"
}
}
Then my inittialization.js file that contains the initialization() method and const fs = require('fs');
const fs = require('fs');
function initialization(){
}
Now I have asked the same question in multiple places, I have tried multiple solutions, nothing works. I am wondering if this is an Electron bug at this stage.
The error I can't get rid off and I keep getting whatever I do is this Uncaught ReferenceError: require is not defined
I simply want to use require in another JS file. Why is node.js or electron not picking this up ?
Place the nodeIntegration inside webPreferences attribute
{ width: 1280,
height: 720,
webPreferences : { nodeIntegration: true },
resizable: false,
maximizable: false
}
I wrote some tests in Protractor, but sadly I have little problem with import/export files.
HomePage.js
export default class HomePage {
constructor() {
this.path = 'http://automationpractice.com/index.php';
this.searchQuery = element(by.css('.search_query'));
this.searchButton = element(by.name('submit_search'));
this.signInButtonHeader = element(by.css('.header_user_info'));
this.emailInput = element(by.id('email'));
this.passwordInput = element(by.id('passwd'));
this.logInButton = element(by.id('SubmitLogin'));
}
}
homepage-spec.js
import HomePage from '../page_objects/HomePage';
browser.waitForAngularEnabled(false);
const homePage = new HomePage();
beforeEach(async () => {
browser.get(homePage.path);
});
describe('Homepage', () => {
it('should have a title', () => {
expect(browser.getTitle()).toEqual('My Store');
});
it('Log into your account', () => {
homePage.signInButtonHeader.click();
homePage.emailInput.sendKeys('testprotractor#gmail.com');
homePage.passwordInput.sendKeys('xxxx');
homePage.logInButton.click();
});
it('Check categories', () => {
element(by.linkText('Women')).click();
});
});`
conf.js
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
require('#babel/register');
exports.config = {
framework: 'jasmine',
capabilities: {
browserName: 'chrome',
},
suites: {
homePage: 'specs/homepage-spec.js',
searchResults: 'specs/search-results-spec.js',
},
onPrepare: () => {
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: true,
},
}));
},
};
package.json
{
"name": "protractorautomation",
"version": "1.0.0",
"description": "This is my test",
"main": "conf.js",
"dependencies": {
"babel-eslint": "^10.0.1",
"#babel/register": "^7.0.0",
"eslint": "^5.3.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.14.0",
"jasmine-spec-reporter": "^4.2.1"
},
"devDependencies": {
"#babel/core": "^7.2.2",
"babel-preset-env": "^1.7.0",
"babel-register": "^6.26.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Protractor"
],
"author": "Xxxx",
"license": "ISC"
}
Whenever I run tests, receive this error:
(function (exports, require, module, __filename, __dirname) { import
HomePage from '../page_objects/HomePage'; ^^^^^^^^
SyntaxError: Unexpected identifier
at new Script (vm.js:84:7)
at createScript (vm.js:264:10)
at Object.runInThisContext (vm.js:312:10)
at Module._compile (internal/modules/cjs/loader.js:694:28)
at Module._compile (C:\Users\X\Documents\protractorAutomation\node_modules\pirates\lib\index.js:83:24)
at Module._extensions..js (internal/modules/cjs/loader.js:745:10)
at Object.newLoader [as .js] (C:\Users\Mati\Documents\protractorAutomation\node_modules\pirates\lib\index.js:88:7)
at Module.load (internal/modules/cjs/loader.js:626:32)
at tryModuleLoad (internal/modules/cjs/loader.js:566:12)
at Function.Module._load (internal/modules/cjs/loader.js:558:3) [16:53:29] E/launcher - Process exited with error code 100
Anyone can help me fix this problem?
It appears that the es6 style of imports you are attempting to use are not supported natively in NodeJS.
According to this answer you have two options. I have created an example of the first for you to try.
module.exports = class HomePage {
constructor() {
this.path = 'http://automationpractice.com/index.php';
this.searchQuery = element(by.css('.search_query'));
this.searchButton = element(by.name('submit_search'));
this.signInButtonHeader = element(by.css('.header_user_info'));
this.emailInput = element(by.id('email'));
this.passwordInput = element(by.id('passwd'));
this.logInButton = element(by.id('SubmitLogin'));
}
}
Change Import to require
//Change import statement to standard require
let HomePage = require('../page_objects/HomePage');
browser.waitForAngularEnabled(false);
const homePage = new HomePage();
beforeEach(async () => {
browser.get(homePage.path);
});
describe('Homepage', () => {
it('should have a title', () => {
expect(browser.getTitle()).toEqual('My Store');
});
it('Log into your account', () => {
homePage.signInButtonHeader.click();
homePage.emailInput.sendKeys('testprotractor#gmail.com');
homePage.passwordInput.sendKeys('xxxx');
homePage.logInButton.click();
});
it('Check categories', () => {
element(by.linkText('Women')).click();
});
});
The electron app open with the same default menu that appear in the electron-quick-start example app and how to change it?
Also tried the menu example on the docs but nothing changes.
when I hide the menu with mainWindow.setMenu(null); the menu is gone but still can't init the new menu
any ideas?
platform: windows 7
electron ver: 0.36.4
ref files:
package.json:
{
"name": "electric_timer",
"version": "0.1.0",
"description": "a Time sheet & project managment",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "none"
},
"author": "Eyal Ron",
"license": "MIT"
}
app.js:
var app = require('app');
var BrowserWindow = require('browser-window');
app.on('ready', function (){
var mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.setMenu(null);
mainWindow.loadUrl('file://' + __dirname + '/main.html');
});
main.js:
var remote = require('remote');
var Menu = remote.require('menu');
var menu = Menu.buildFromTemplate([
{
label: 'Electron',
submenu: [
{
label: 'Prefs',
click: function(){
alert('hello menu');
}
}
]
}
]);
Menu.setApplicationMenu(menu);
main.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>electron test</title>
</head>
<body>
<h1>hello world</h1>
<script>requier('./main.js')</script>
</body>
</html>
Electron's 'default_app' sets the menu; if you want to avoid this, you need Electron to start your app directly not via the default app (note: if you start your app with something like electron . you actually start the default app).
Electron looks in its resource folder for 'app', 'app.asar' or 'default_app', so in order to start your app directly you need to either copy or link it into Electron's resource folder.
Regardless of how you start your app, you can set your menu using Menu.setApplicationMenu -- you can do it in the main process, you don't need to do it in the Renderer like in your example. Incidentally, there is a typo in your main.html (requier instead of require) so if that's your actual code it would indicate that your main.js does not run at all.
Put your logic to customise the menu into your app('ready') event callback. Give try to following code example
const {app, BrowserWindow, Menu} = require('electron');
let mainWindow;
let menuTemplate = [
{
label: "Window Manager",
submenu: [
{ label: "create New" }
]
},
{
label : "View",
submenu : [
{ role : "reload" },
{ label : "custom reload" }
]
}
];
function appInit () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the main.html of the app.
mainWindow.loadFile('main.html')
let menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
}
app.on('ready', () => {
appInit();
})
It looks like the electron Menu and MenuItem objects are set up to be immutable.
This means if you want to modify them, you have to create new objects and use that instead. This is what my code does for this, to hide the help menu and developer tools:
// main.js
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
...
let defaultMenu = Menu.getApplicationMenu()
let newMenu = new Menu();
defaultMenu.items
.filter(x => x.role != 'help')
.forEach(x => {
if(x.role == 'viewmenu' && process.env.NODE_ENV == 'production') {
let newSubmenu = new Menu();
x.submenu.items.filter(y => y.role != 'toggledevtools').forEach(y => newSubmenu.append(y));
x.submenu = newSubmenu;
newMenu.append(
new MenuItem({
type: x.type,
label: x.label,
submenu: newSubmenu
})
);
} else {
newMenu.append(x);
}
})
Menu.setApplicationMenu(newMenu);
...
})
}
app.on('ready', createWindow)
Where is Electron in your app? Your package.json file should look like this:
{
"name": "todos",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"electron": "electron ."
},
"author":
"Who Cares <email#example.com> (https://doesntmatter.com/)",
"license": "MIT",
"dependencies": {
"electron": "3.0.8"
}
}
Your Electron side will look like this:
const electron = require('electron');
const { app, BrowserWindow, Menu } = electron;
let mainWindow;
app.on('ready', () => {
mainWindow = new BrowserWindow({});
mainWindow.loadURL(`file://${__dirname}/main.html`);
const mainMenu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(mainMenu);
});
const menuTemplate = [
{
label: 'File'
}
];
Even with fixing up the syntax you will still run into the problem of having taken over the default Electron menu and its key binds. You are saying you are going to add your own custom menu and keybinds, which in production if that is your goal then fine, but it sounds like you have lost functionality that you want to maintain.
You could fix it like this:
const menuTemplate = [
{
label: 'File',
submenu: [{ label: 'New Todo' }]
}
];