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
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Canonical Bumps LTS Support to 12 years
If you're worried that your Ubuntu LTS release won't be supported long enough to last, Canonical has a surprise for you in the form of 12 years of security coverage.
-
Fedora 40 Beta Released Soon
With the official release of Fedora 40 coming in April, it's almost time to download the beta and see what's new.
-
New Pentesting Distribution to Compete with Kali Linux
SnoopGod is now available for your testing needs
-
Juno Computers Launches Another Linux Laptop
If you're looking for a powerhouse laptop that runs Ubuntu, the Juno Computers Neptune 17 v6 should be on your radar.
-
ZorinOS 17.1 Released, Includes Improved Windows App Support
If you need or desire to run Windows applications on Linux, there's one distribution intent on making that easier for you and its new release further improves that feature.
-
Linux Market Share Surpasses 4% for the First Time
Look out Windows and macOS, Linux is on the rise and has even topped ChromeOS to become the fourth most widely used OS around the globe.
-
KDE’s Plasma 6 Officially Available
KDE’s Plasma 6.0 "Megarelease" has happened, and it's brimming with new features, polish, and performance.
-
Latest Version of Tails Unleashed
Tails 6.0 is based on Debian 12 and includes GNOME 43.
-
KDE Announces New Slimbook V with Plenty of Power and KDE’s Plasma 6
If you're a fan of KDE Plasma, you'll be thrilled to hear they've announced a new Slimbook with an AMD CPU and the latest version of KDE Plasma desktop.
-
Monthly Sponsorship Includes Early Access to elementary OS 8
If you want to get a glimpse of what's in the pipeline for elementary OS 8, just set up a monthly sponsorship to help fund its continued existence.