In this tutorial, you'll learn to build GUIs for your Python applications using Electron and web technologies i.e HTML, CSS and JavaScript-this means taking advantage of the latest advancements in front-end web development to build desktop applications but also taking advantages of Python and the powerful libraries it has to easily implement advanced requirements.
You can find the code in this GitHub repository.
Electron Tutorial
Electron allows you to develop desktop applications with web technologies such as JavaScript, HTML and CSS by providing a web container and a runtime with rich native cross-platform APIs. You could also think of it as a Node.js environment for desktop apps.
Electron Applications Architecture
In Electron, you have two types of processes; the Main and Renderer processes.
The main process is the one that runs the main script in the package.json file. This script can create and display GUI windows, also many Electron APIs are only available from the main process. An Electron application has always only one main process.
Electron makes use of the chromium browser to display web pages. Each web page runs on its own process called the renderer process.
You could also think of Electron as a web browser but unlike typical browsers (such as Chrome, Firefox and Edge etc.) web pages don't run inside isolated or sandboxed environments since they have access to Node.js APIs and by result can communicate with the low level APIs of the underlying operating system.
Note that Electron is not a JavaScript binding for GUI libraries but a browser/Node.js runtime that uses web pages as its GUI.
Electron Main vs. Renderer Processes
The main process uses the BrowserWindow to create native GUI Windows. A window runs a web page in its own renderer process.
Renderer processes are not able to call native GUI APIs so they need to communicate with the main process, via different mechanisms, which will handle the native operations and return any results to the requesting renderer process.
Communication Between Renderer and Main Processes
Electron provides different ways to allow communication between main and renderer processes, such as:
- Sending messages using
ipcRendererandipcMainmodules; - RPC communication using the remote module.
Sharing Data Between Renderer Processes
Each renderer process is isolated and only manages its own web page but in many situations, you need to share data between web pages (i.e renderer processes). There are multiple ways to achieve that, such as:
- using the HTML5 APIs like Storage API,
localStorage,sessionStorage, and IndexedDB; - using the main process as global storage area via the IPC (Inter-Process Communication) system in Electron.
For example; in the main script, add the following code:
global.sharedObject={aProperty:'value'}We simply, add variables and objects to the global namespace.
Then, in scripts running in the web pages, add:
require('electron').remote.getGlobal('sharedObject').aProperty='new value';We import the electron module and we use the getGlobal() method of the remote property to access and modify global objects.
Using Node.js in Electron
Electron provides complete access to Node.js in main and renderer processes. That means, you have access to a full and rich ecosystem of APIs and also the modules available from npm which is the biggest repository of open-source modules in the world.
Compiling Native Node.js Modules for Electron
Keep in mind that native Node.js modules, such as SQLite3, require re-compilation to target the Electron ABI. You need to use the electron-rebuild package for rebuilding native APIs to target the Electron API
You can follow this tutorial for more information on how to compile native Node.js module for Electron.
Accessing Electron APIs
Electron provides a rich and cross-platform ecosystem of APIs. APIs can be accessed from only the remote process or only the renderer processes or both.
To access APIs, you need to import/require theelectron module:
constelectron=require('electron')For example, the BrowserWindow API, which is only available from the main process, can be imported using the following syntax:
const{BrowserWindow}=require('electron');constwindow=newBrowserWindow();If you want to access it from a renderer process, you can simply run:
const{BrowserWindow}=require('electron').remoteconstwindow=newBrowserWindow()Creating your First Electron Application
Let's now see how to create our first Electron application. You can develop Electron apps just like you would normally develop Node.js apps.
You first need to start with creating or generating a package.json file inside your project's folder using the following command:
npm init -y
This will create a basic package.json file with default values:
{"name":"electronjs-python","version":"1.0.0","description":"","main":"index.js","scripts":{"test":"echo \"Error: no test specified\"&& exit 1"},"keywords":[],"author":"","license":"ISC"}Next, create the two index.html and main.js files inside the project's folder.
touch main.js index.html
The main.js file is the main script so we need to change the main property of our package.json file to main.js instead of the default index.js file (It's only a preference not required):
"main":"main.js",Next, you need to install electron from npm:
npm install --save-dev electron
This will install electron locally; you can also follow the official guide for more available options for installing electron.
Next, add the start script to run the main.js file. Open the package.json file and add:
"scripts":{"start":"electron .","test":"echo \"Error: no test specified\"&& exit 1"},Now, let's add the code which runs a GUI window in the main process. Open the main.js file and add, the first line to import the electron module:
const{app,BrowserWindow}=require('electron')Next, add the following function which makes an instance of BrowserWindow and load the index.html file:
functioncreateWindow(){window=newBrowserWindow({width:800,height:600})window.loadFile('index.html')}When the application is ready, run the createWindow() method:
app.on('ready',createWindow)We can also handle different events such as when closing all Windows using:
app.on('window-all-closed',()=>{// On macOS it is common for applications and their menu bar// to stay active until the user quits explicitly with Cmd + Qif(process.platform!=='darwin'){app.quit()}})Finally, let's add the following content to the index.html file:
<!DOCTYPE html><html><head><metacharset="UTF-8"><title>Hello Python from Electron!</title></head><body><h1>Hello Python!</h1></body></html>Now, you can run the application using:
npm start
This is a screenshot of the application running:

Running a Python Script from Electron
Since we want to develop our application using Python and use Electron to build the GUI frontend with the web; we need to be able to communicate between Python and Electron.
Let's see how to run a basic Python script from Electron. First create a hello.py file and add the following Python code which prints Hello from Python! to the standard output:
importsysprint('Hello from Python!')sys.stdout.flush()In your main.js file, run the following code to spawn a Python process and execute the hello.py script:
functioncreateWindow(){/*...*/varpython=require('child_process').spawn('python',['./hello.py']);python.stdout.on('data',function(data){console.log("data: ",data.toString('utf8'));});}
Using python-shell to Communicate between Python and Node.js/Electron
A better way to communicate with Node.js/Electron and Python is through using the python-shell package.
python-shell provides an easy way to run Python scripts from Node.js with basic and efficient inter-process communication and better error handling.
Using python-shell, you can:
- spawn Python scripts in a child process;
- switch between text, JSON and binary modes;
- use custom parsers and formatters;
- perform data transfers through
stdinandstdoutstreams; - get stack traces when an error is thrown.
Head back to your terminal, make sure you are inside the root folder of your project and run the following command to install python-shell from npm:
npm install --save python-shell
You can then simply run a Python shell using:
varpyshell=require('python-shell');pyshell.run('hello.py',function(err,results){if(err)throwerr;console.log('hello.py finished.');console.log('results',results);});
Conlusion
In this tutorial, we've seen how to use Electron and Python to build a simple desktop application.
We've also seen how to use the python-shell module to run a Python shell from a Node.js/Electron application and communicate between Electron and Python.