Using the Electron framework to weed out images
Ready, Steady, Go
An article on setting up the Electron Framework [3] was published in Linux Magazine a few months ago, so I mention the preparation only briefly and then head on directly to processing the photo folders.
The following commands install the Electron framework on Ubuntu:
sudo apt-get install npm nodejs-legacy
The additional nodejs-legacy
package only installs some symlinks that many older node modules need during build and execution. In a fresh directory, then run
npm init npm install electron --save-dev
to create a new project that not only installs the Electron framework locally, but also adds its dependencies to its dependency list, helping adopters to modify and rebuild the code to their heart's content. The npm init
command prompts the user to enter some project parameters, such as the application name, its version, or the author name (Figure 2). The --save-dev
option of the npm install
statement appends the name of the installed package to the devDependencies
list in package.json
. For comparison, --save
would list the package as a run-time dependency.

If you also add the following to the scripts
section inside package.json
,
"start": "electron ."
the application can be launched later by using npm start
. Electron then initially loads the main.js
start script in Listing 1 [4] (specified in the configuration file under main
) and passes it to the Node.js interpreter for execution.
Courage to be Different
As is well known, the asynchronous functional approach used by Node.js means a very different programming style compared with "normal" languages like Python or Perl. Instead of sequentially processing calls, Node.js code often adds a callback to a function call. The function later resumes execution by calling it at the end.
The GUI code builds a state machine, between whose states the code jumps back and forth, as controlled by events. At the same time, the event loop always needs to watch out for new events such as mouse clicks, to which it must respond promptly. This would not work if the code were just blocked for a while because it was reading a large file from disk.
The code in Listing 1 does not execute anything at first but waits until the node environment reports the ready
event. If this occurs, it starts the callback from line 25 and first submits a web page to the renderer process for display with createWindow()
. This happens from line 8 and with an object of the BrowserWindow
class, whose loadURL()
method is given the path to the index.html
file in Listing 2.
Listing 1
main.js
01 const {app,globalShortcut,BrowserWindow} = 02 require('electron'); 03 const path = require('path'); 04 const url = require('url'); 05 06 let win; 07 08 function createWindow(){ 09 win = new BrowserWindow({ 10 width:800, height:600}); 11 12 win.loadURL(url.format({ 13 pathname: 14 path.join(__dirname, 'index.html'), 15 protocol: 'file:', slashes: true 16 })); 17 18 win.webContents.openDevTools(); 19 20 win.on('closed', () => { 21 win = null; 22 }); 23 } 24 25 app.on('ready', () => { 26 createWindow(); 27 globalShortcut.register('l', () => { 28 win.webContents.send('nextImage'); 29 }); 30 globalShortcut.register('h', () => { 31 win.webContents.send('prevImage'); 32 }); 33 globalShortcut.register('d', () => { 34 win.webContents.send('deleteImage'); 35 }); 36 win.webContents.send('prevImage'); 37 }); 38 39 app.on('will-quit', () => { 40 ['h','l','d'].forEach(function(key){ 41 globalShortcut.unregister(key); 42 }); 43 }); 44 45 app.on('window-all-closed', () => { 46 app.quit(); 47 });
Listing 2
index.html
01 <html> 02 <head> </head> 03 04 <body> 05 <h1>iNuke My Photos</h1> 06 07 <script> 08 require('./renderer.js'); 09 </script> 10 11 <img id="image"></img> 12 13 </body> 14 </html>
At the same time, Listing 1 uses the global variable win
to store a reference to the browser window. It can reset the variable during later callbacks, as being performed by the handler of the closed
event, which gets triggered by the windowing system, and handles freeing up memory before shutting down the program.
During the debug phase of a new Electron application, it is extremely useful to open Chromium's debug window in the browser's main window using openDevTools()
(line 18) and either read the warnings at the command line or analyze the HTML of the dynamically refreshed web page (Figure 3).

Short and Sweet
Intercepting keyboard input is also a task of the main process in main.js
. The register
calls in lines 27, 30, and 33 ensure that the user can move to the next image with L and to the previous image with H (just as you move left or right in Vim) and delete the displayed image with D.
Among other things, these keystroke commands affect the displayed web page, which is why the main process main.js
sends them as events to the renderer process in Listing 3 via IPC and win.webContents.send()
. The renderer process starts out at the very beginning of the main process in Listing 1. It loads the index.html
file (Listing 2) into the browser in lines 12 to 16, which in turn executes the renderer's JavaScript (Listing 3) in line 8 of Listing 2 via require(./renderer.js)
.
Listing 3
renderer.js
01 loadImage = require('blueimp-load-image'); 02 fs = require( 'fs' ); 03 ipc = require('electron').ipcRenderer; 04 05 images = []; 06 images_idx = -1; 07 08 function displayImage(file) { 09 loaded = loadImage(file, function(img) { 10 scaled_img = loadImage.scale( 11 img, {maxWidth: 600}); 12 scaled_img.id = "image"; 13 node = window.document.getElementById( 14 'image'); 15 node.replaceWith(scaled_img); 16 } ); 17 } 18 19 function scroll(direction){ 20 images_idx += direction; 21 if(images_idx > images.length-1){ 22 images_idx = images.length-1; 23 }else if(images_idx<0) { 24 images_idx = 0; 25 } 26 displayImage( images[ images_idx ] ); 27 } 28 29 function deleteImage() { 30 fs.unlink(images[ images_idx ]); 31 images.splice(images_idx, 1); 32 if(images.length == 0) { 33 console.log("That's it. Good-bye!"); 34 require('electron').remote.app.quit(); 35 } 36 scroll(-1); 37 } 38 39 dir = "images"; // change to process.cwd() 40 fs.readdir(dir, function(err, files) { 41 if( err ) { 42 console.error("readdir:", err); 43 require('electron').remote.app.quit(); 44 } 45 files.forEach(function(file, index) { 46 images.push( dir + "/" + file ); 47 }); 48 scroll(0); 49 } ); 50 51 ipc.on('nextImage', () => { scroll(1); }); 52 ipc.on('prevImage', () => { scroll(-1); }); 53 ipc.on('deleteImage', deleteImage);
« Previous 1 2 3 4 Next »
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Direct Download
Read full article as PDF:
Price $2.95
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
News
-
Mageia 9 Beta 2 is Ready for Testing
The latest beta of the popular Mageia distribution now includes the latest kernel and plenty of updated applications.
-
KDE Plasma 6 Looks to Bring Basic HDR Support
The KWin piece of KDE Plasma now has HDR support and color management geared for the 6.0 release.
-
Bodhi Linux 7.0 Beta Ready for Testing
The latest iteration of the Bohdi Linux distribution is now available for those who want to experience what's in store and for testing purposes.
-
Changes Coming to Ubuntu PPA Usage
The way you manage Personal Package Archives will be changing with the release of Ubuntu 23.10.
-
AlmaLinux 9.2 Now Available for Download
AlmaLinux has been released and provides a free alternative to upstream Red Hat Enterprise Linux.
-
An Immutable Version of Fedora Is Under Consideration
For anyone who's a fan of using immutable versions of Linux, the Fedora team is currently considering adding a new spin called Fedora Onyx.
-
New Release of Br OS Includes ChatGPT Integration
Br OS 23.04 is now available and is geared specifically toward web content creation.
-
Command-Line Only Peropesis 2.1 Available Now
The latest iteration of Peropesis has been released with plenty of updates and introduces new software development tools.
-
TUXEDO Computers Announces InfinityBook Pro 14
With the new generation of their popular InfinityBook Pro 14, TUXEDO upgrades its ultra-mobile, powerful business laptop with some impressive specs.
-
Linux Kernel 6.3 Release Includes Interesting Features
Although it's not a Long Term Release candidate, Linux 6.3 includes features that will benefit end users.