Packaging electron app powered by Python Flask electron - electron

I just created an electron app which is powered by Flask.
It works nicely when I run the app in powershell, but when I build this app with electron-packager, It succeeds, but the app does not work.
It seems python code would not be included in the app.
How can I build the app with integrating all python code and modules I am using in the app?
I am using any python modules like pandas

I was able to package an electron-flask app using guidelines from here which gives better details of the answer given below.
First make sure that the pyinstaller .exe actually starts the server correctly when you run it and that when you direct to the hosted page in your browser that the app does everything you need it to do. Packaging flask app tutorial is here.
And then make sure when you are executing:
var subpy = require('child_process').spawn('./dist/hello/hello');
That you make sure its:
var subpy = require('child_process').spawn('path_to_flask_exe');

Build the flask app with PyInstaller.. You can find various tutorial on it through google.. Pick one that suits your needs. Always good to read the official documentation https://www.pyinstaller.org/ .
Well I don't know your approach of creating the electron entry point. what i did was, on the entry point (usually main.js for me) I have created a function which is called on app is ready. Some of the stuffs i got from Python on Electron framework and from https://github.com/fyears/electron-python-example
main.js
'use strict';
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
// This method will be called when Electron has finished
// initialization and is ready to create browser mainWindow.
// Some APIs can only be used after this event occurs.
var mainWindow = null;
function createWindow(){
// spawn server and call the child process
var rq = require('request-promise');
mainAddr = 'http://localhost:4040/'
// tricks 1 worked for me on dev.. but building installer of electron
// server never started.. didn't find time to fixed that
// var child = require('child_process').spawn('python',
// ['.path/to/hello.py']);
// or bundled py
// var child = require('child_process').spawn('.path/to/hello.exe');
// tricks 2, a little variation then spawn :)
var executablePath = './relative/path/to/your/bundled_py.exe';
var child = require('child_process').execFile;
child(executablePath, function(err, data) {
if(err){
console.error(err);
return;
}
console.log(data.toString());
});
// Create the browser mainWindow
mainWindow = new BrowserWindow({
minWidth: 600,
minHeight: 550,
show: false
});
// Load the index page of the flask in local server
mainWindow.loadURL(mainAddr);
// ready the window with load url and show
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
// Quit app when close
mainWindow.on('closed', function(){
mainWindow = null;
// kill the server on exit
child.kill('SIGINT');
});
// (some more stuff, eg. dev tools) skipped...
};
var startUp = function(){
rq(mainAddr)
.then(function(htmlString){
console.log('server started!');
createWindow();
})
.catch(function(err){
//console.log('waiting for the server start...');
startUp();
});
};
app.on('ready', startUp)
app.on('quit', function() {
// kill the python on exit
child.kill('SIGINT');
});
app.on('window-all-closed', () => {
// quit app if windows are closed
if (process.platform !== 'darwin'){
app.quit();
}
});

Related

Electron: remove beforeunload event listeners

I have an electron-application that is used to show webpages which I have no control over.
The app is used so a different page can be shown every few seconds.
One of the pages shown attaches an 'beforeunload' listener like so
window.addEventListener('beforeunload', function(event) {
event.returnValue="test";
});
This causes electron to fail when loading a new url, so the switching does not work anymore.
This is a known issue: https://github.com/electron/electron/issues/9966
Even worse, is also prevents the whole application from being closed.
Is there anything that can be done from the main process that removes/disables the beforeunload listener, so the switching works again?
To test this, I have a fiddle that shows this behavior:
https://gist.github.com/9a8acc3bf5dface09d46aae36807f6f9
You can simply prevent this event:
const { BrowserWindow, dialog } = require('electron')
const win = new BrowserWindow({ width: 800, height: 600 })
win.webContents.on('will-prevent-unload', (event) => {
event.preventDefault()
})
See Electron docs for details

How to call a function is renderer.js/main.js from web page in electron

Newbie to electron here, I have built a simple web application with React JS and able to view it in a window by calling
window.loadFile('./build/index.html');
Now i would want to call a function located in say renderer.js/main.js which should read file system and return data to the web application.
I have already tried this in renderer.js
const { ipcRenderer } = require('electron');
document.getElementById('#button').addEventListener('click', function (event) {
//read file contents
console.log('file contents');
});
But there are 2 issues around here
The control is from the renderer.js, instead i would want the
control to be on the web page of React.
The data that is read should be returned back to web page, so that it can be displayed in the web page.
You should be able to import/require ipcRenderer directly on your react component scripts and maybe load the file on a lifecycle hook. The 'renderer.js' is just one way to execute client side javascript on the (electron-)web page but any other way also does the trick.
If you can't import or require electron from the webapp (I didn't play with the electron-react-boilerplate yet), then you could write a so called preload script, load that when you create the browser window (see that documentation) and put the ipcRenderer on window like so:
const {ipcRenderer} = require('electron')
window.ipcRenderer = ipcRenderer
Then you can access it from the react app.
You could directly use fs inside the event listener
const { ipcRenderer } = require('electron');
const fs = require("fs");
document.getElementById('#button').addEventListener('click', function (event) {
console.log(fs.readFileSync("some/file/path", "utf8"));
});
Electron exposes full access to Node.js both on main and renderer process
https://www.electronjs.org/docs/tutorial/application-architecture#using-nodejs-apis
You are building your electron renderer using React.
Please check this to clear what main process is and renderer is.
how to communicate between react and electron
This is the answer I posted before. Feel free to check this.
And here is the pre-requirement.
let mainWindow = new BrowserWindow(
{
width: 800,
height: 600,
webPreferences:{
nodeIntegration:true
}
});
You should enable the nodeIntegration flag when you are creating the BrowserWindow. So that you can use any Nodejs API at your renderer

How do I subscribe to a "window move" event in the Atom edtor?

I want to subscribe to the window moving event that electron provides, but I don't know how to code it in an atom package.
When I was reading the electron docs I found an example that I think is similar to what I want:
const {BrowserWindow} = require('electron')
let win = new BrowserWindow()
win.on('move', (e) => {
// . . .
})
But this appears to require creating a new electron window, and I don't know how to get the current BrowserWindow in an existing atom window.
I also can hook into the window.onresize event in atom, but there is no window.onmove.
Lastly, I found a way to get the window position in the atom docs, but I don't know how that would be useful without polling.
First, we should note that according to the official documentation, there are two events, move and moved. The latter is labeled as MacOS only.
In order to listen to the event need to fetch the current window. On the client side this can be done like this
const electron = require('electron');
const currentWindow = electron.remote.getCurrentWindow();
currentWindow.on('move', function() {
// Do move event action
});
On the application side there is no remote, so the window is fetched this way
const { BrowserWindow } = require('electron');
const currentWindow = BrowserWindow.getFocusedWindow();
currentWindow.on('move', function() {
// Do move event action
});

electron-packager - Generated exe stays in memory after closing the window

So I am using the electron-packager cli tool and everything is working EXCEPT the generated exe (only tried on Windows so far) doesn't unload from memory when I close the window (my one and only renderer process). I have to close it (kill the process) using task manager.
Is there something inside of electron I can call to ensure that this happens or is this a bug in electron-packager or what?
After thinking about this I considered that unloading the window doesn't automatically unload the node process and added the following 'closed' event to my mainWindow:
app.on('ready', function () {
mainWindow = new BrowserWindow({
width: 1024,
height: 768
});
// This 'closed' handler solves the problem
mainWindow.on('closed', function () {
mainWindow = null;
process.exit(0);
});
var menu = Menu.buildFromTemplate(menuTemplate);
mainWindow.setMenu(menu);
mainWindow.loadURL('file://' + __dirname + '/index.html');
});

Using console.log() in Electron app

How can I log data or messages to the console in my Electron app?
This really basic hello world opens the dev tools by default, by I am unable to use console.log('hi'). Is there an alternative for Electron?
main.js
var app = require('app');
var BrowserWindow = require('browser-window');
require('crash-reporter').start();
var mainWindow = null;
app.on('window-all-closed', function() {
// Mac OS X - close is done explicitly with Cmd + Q, not just closing windows
if (process.platform != 'darwin') {
app.quit();
}
});
app.on('ready', function(){
mainWindow = new BrowserWindow({ width: 800, height: 600});
mainWindow.loadUrl('file://' + __dirname + '/index.html');
mainWindow.openDevTools();
mainWindow.on('closed', function(){
mainWindow = null;
});
});
console.log works, but where it logs to depends on whether you call it from the main process or the renderer process.
If you call it from the renderer process (i.e. JavaScript that is included from your index.html file) it will be logged to the dev tools window.
If you call it from the main process (i.e. in main.js) it will work the same way as it does in Node - it will log to the terminal window. If you're starting your Electron process from the Terminal using electron . you can see your console.log calls from the main process there.
You can also add an environment variable in windows:
ELECTRON_ENABLE_LOGGING=1
This will output console messages to your terminal.
There is another way of logging to the console from inside the renderer process. Given this is Electron, you can access Node's native modules. This includes the console module.
var nodeConsole = require('console');
var myConsole = new nodeConsole.Console(process.stdout, process.stderr);
myConsole.log('Hello World!');
When this code is run from inside the renderer process, you will get Hello World! in the terminal you ran Electron from.
See https://nodejs.org/api/console.html for further documentation on the console module.
Yet another possibility is accessing the main process console using remote.getGlobal(name):
const con = require('electron').remote.getGlobal('console')
con.log('This will be output to the main process console.')
Adding to M. Damian's answer, here's how I set it up so I could access the main process's console from any renderer.
In your main app, add:
const electron = require('electron');
const app = electron.app;
const console = require('console');
...
app.console = new console.Console(process.stdout, process.stderr);
In any renderer, you can add:
const remote = require('electron').remote;
const app = remote.app;
...
app.console.log('This will output to the main process console.');
process.stdout.write('your output to command prompt console or node js ')
You can use the npm package electron-log https://www.npmjs.com/package/electron-log
It will log your error, warn, info, verbose, debug, silly outputs in your native os log.
var log = require('electron-log');
log.info('Hello, log');
log.error('Damn it, an error');
Sorry to raise an old thread but this is the top result for "how to output console.log to terminal" (or similar searches.
For anyone looking to gain a bit more control over what is output to the terminal you can use webContents.on('console-message') like so:
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
console.log(message + " " +sourceId+" ("+line+")");
});
See:
webContents Documentation
webContents entry on BrowserWindow docs
This is a follow up to cscsandy5's answer for some addition information, info from here
process.stdout.write('your output to command prompt console or node js ')
This code works great for just outputting a simple debug message to the terminal window you launched the electron app from and is is what console.log is build on top of.
Here is an example snippet (based on tutorialspoint electon tutorial) of a jQuery script that will write hello to the terminal every time the button is pressed (warning: you need to add your own line breaks in the output strings!)
let $ = require('jquery')
var clicks = 0;
$(function() {
$('#countbtn').click(function() {
//output hello <<<<<<<<<<<<<<<<<<<<<<<
process.stdout.write('hello')
$('#click-counter').text(++clicks);
});
$('#click-counter').text(clicks);
});
This is what I use:
let mainWindow // main window reference, you should assign it below 'mainWindow = new BrowserWindow'
function log(...data) {
mainWindow.webContents.executeJavaScript("console.log('%cFROM MAIN', 'color: #800', '" + data + "');");
}
Example use (same as console.log):
log('Error', { msg: 'a common log message' })
log('hello')
Source: https://github.com/fuse-box/fuse-box-electron-seed/tree/master/src/main in the logger.js file, here you can see a real use case.
After some investigation, here my understanding:
Code
(1) main.js
const createPyProc = () => {
console.log('In createPyProc')
...
console.log('scriptStart=%s', scriptStart)
...
console.log('scriptOther=%s', scriptOther)
...
}
...
let mainWindow = null
const createWindow = () => {
mainWindow = new BrowserWindow(
{
width: 1024,
height: 768,
webPreferences: {
nodeIntegration: true,
}
}
)
mainWindow.loadURL(require('url').format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
mainWindow.webContents.openDevTools()
mainWindow.on('closed', () => {
mainWindow = null
})
}
...
Note: which use openDevTools to opened Electron Dev Tools
(2) render.js
const zerorpc = require("zerorpc")
...
console.log("clientStart %d server is ready", PORT_START)
...
})
(3) render.js is called by: index.html
<!DOCTYPE html>
<html>
...
<script>
require('./renderer.js')
</script>
</html>
console.log
Output Logic
two process and its console.log output:
main process = NodeJS process = here Electron UI process
-> console.log in main.js will output log to here
render process
-> console.log in render.js will output log to here
Screenshot Example
DEBUG=Development mode
run ./node_modules/.bin/electron .
Production=Release mode = the xxx.app pacakged by eletron-builder
run /path_to_your_packaged_mac_app/xxx.app/Contents/MacOS/yourBinaryExecutable
added export ELECTRON_ENABLE_LOGGING=true, render.js console.log ALSO output to main process terminal
console.log() will work fine for debugging. As the electron is built on top of browser, it has DevTools support you can use devtools for debugging purpose. However, there is a hysterical behaviour of console.log() method. When you call the console.log() from main process of electron app, it will output to the terminal window from where you just launched the app and when you call it from renderer process it will output to the DevTools console.
Everything Alex Warren wrote is true. Important here is how Electron is started. If you use the standard script in the package.json file it will not work. To make console.log() work replace the old script with this new one.
Old one:
"scripts": {
"start": "electron ."
}
New one:
"scripts": {
"start": ".\\node_modules\\electron\\dist\\electron.exe ."
}
Now all console.log() calls are displayed in the terminal as well.
With this You can use developer tools of main Browser window to see logs
function logEverywhere(s) {
if (_debug === true) {
console.log(s);
// mainWindow is main browser window of your app
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.executeJavaScript(`console.log("${s}")`);
}
}
}
Example logEverywhere('test')
will output // test in console panel of main browser window's developer tools
You may need enhance this method to accept multiple args (You can done it with spread operator by es6)
Well, this is 2019 and I cant believe no one mentioned this trick in all the answers above.
Ok, so, how about logging directly to the browser console directly from the main?
I provided my answer here: https://stackoverflow.com/a/58913251/8764808
Take a look.
A project I'm working on was using electron-react-boilerplate. That has electron-devtools-installer#2.4.4, which somehow via cross-unzip causes a process to crash with Error: Exited with code 9 .
Upgrading to electron-devtools-installer#3.1.1, as proposed in electron-react-boilerplate#v2.0.0 resolved it so my console.log, console.error, etc statements worked as expected.
for log purpose, i would recommend you to use the electron-log package

Resources