Here is the Code, The Tutorial Said I should use this syntax in order to use 'menubar', I've also tried including Menubar as a dependency from 'electron', but that responded with Menubar is not a constructor.
Here is the Error Message:
Error: Cannot find module 'menubar'
and here is the full code from the main process
const path = require('path');
const {
app,
clipboard,
Menu,
BrowserWindow,
globalShortcut,
Tray,
systemPreferences,
} = require('electron');
// Declare an empty Array
const clippings = [];
// Declare a variable in the global scope that eventually stores a reference to
// the tray instance
let tray = null;
// Instead of requiring the app module from Electron, we create an instance of menubar
const Menubar = require('menubar');
const menubar = new Menubar();
// menubar wraps up several common Electron modules. It fires its ready event
// when the app module fires its ready event
menubar.on('ready', () => {
console.log('Application is ready.');
})
// The getIcon() function checks the platform the application is running on and
// returns the appropriate filename
// Initialize a BrowserWindow instance
let browserWindow = null;
const getIcon = () => {
if (process.platform === 'win32') return 'icon-light#2x.ico';
// Use the sysemPreferences.isDarkMode() to detect if macOS is in dark mode
if (systemPreferences.isDarkMode()) return 'icon-light.png';
return 'icon-dark.png';
};
app.on('ready', () => {
// Hide the dock icon if running on macOS
if (app.dock) app.dock.hide();
// When creating a new tray instance, use getIcon() to get the correct filename
tray = new Tray(path.join(__dirname, getIcon()));
// Creates a tray instance by calling the constructor with a path to an image
// tray = new Tray(path.join(__dirname, '/Icon.png'));
// On Windows, we register a click event listener to open the menu
if (process.platform === 'win32') {
tray.on('click', tray.popUpContextMenu);
}
// Initialize browserWindow
browserWindow = new BrowserWindow({
show: false,
});
// Load alternative Icon
browserWindow.loadURL(path.join(__dirname, 'index.html'));
// Setting an alternate icon for when icon is pressed
tray.setPressedImage(path.join(__dirname, 'icon-light.png'));
// Passes a string defining the accelerator and an anonymous function that
// should be invoked whenever the accelerator is passed
const activationShortcut = globalShortcut.register(
'CommandOrControl+Option+C',
// We register a second shortcut to add a clipping to the array
() => { tray.popUpContextMenu(); }
);
// If registration fails, Electron does not throw an error. Instead, it
// returns undefined. In this line, we check if the activationShortcut is defined
if (!activationShortcut) {
// If either shortcut fails, we log the issue with console.error. In a
// more robust application, you might show the user that there was an issue
// or implement a fallback
console.error('Global activation shortcut failed to register');
}
const newClippingShortcut = globalShortcut.register(
'CommandOrControl+Shift+Option+C',
() => {
// addClipping() returns the string of the clipping that was added
// to the array
const clipping = addClipping();
if (clipping) {
browserWindow.webContents.send(
'show-notification',
// If there was a clipping saved, we send a notification to
// the renderer process, which triggers the notification
'Clipping Added',
clipping,
);
}
},
);
if (!newClippingShortcut) {
console.error('Global new clipping shortcut failed to register');
}
updateMenu();
// Define a tooltip to be shown when the ser hovers ove the tray icon
tray.setToolTip('Clipmaster');
});
const updateMenu = () => {
// Build a menu in the same fashion that we built the application and context menus
const menu = Menu.buildFromTemplate([
{
label: 'Create New Clipping',
// When a user clicks the Create New Clipping menu item, we call addClipping() function
click() { addClipping(); },
accelerator: 'CommandOrControl+Shift+C'
},
{ type: 'separator' },
...clippings.slice(0, 10).map(createClippingMenuItem),
{ type: 'separator'},
{
label: 'Quit',
click() { app.quit(); },
// accelerator for the Quit menu itme
accelerator: 'CommandOrControl+Q'
}
]);
// Take the menu created and set it as the menu that appears when the user
// clicks the icon in the menu or system tray in macOS and Windows, respectively.
tray.setContextMenu(menu);
};
const addClipping = () => {
// Uses Electron's clipboard module to read text from the system clipboard
const clipping = clipboard.readText();
// Checks if the clippings array already contains the current clippings. If so,
// returns early from the function
if (clippings.includes(clipping)) return;
// Unshift the text read from teh clipboard into the array of clippings
clippings.unshift(clipping);
// Regenerates the menu to display the new clipping as a menu item
updateMenu();
return clipping;
}
// Creates a function called createClippingMenuItem()
const createClippingMenuItem = (clipping, index) => {
return {
// if the length of the clipping is longer than 20 characters
label: clipping.length > 20
? clipping.slice(0, 20) + '_'
: clipping,
// When a user clicks on a given clipping, writes it to the clipboad
click() { clipboard.writeText(clipping); },
// Assign the menu item an accelerator based on its index
accelerator: `CommandOrControl+${index}`
};
};
First check if you actually installed the menubar repo.
Or just npm i -S menubar
Then try:
const { menubar } = require('menubar');
It seems that it is not a default export.
I just assume that they refer to the npm module: https://github.com/maxogden/menubar
Related
Been trying to get Electron.js to talk to my Arduino.
char x = 'v';
void setup() {
// initialize serial:
Serial.begin(9600);
}
void loop() {
// print the string when a newline arrives:
Serial.print(x);
delay(1600);
}
I checked the IDE and it is reacting to the rendering code below:
var path = "/dev/ttyACM0";
const { SerialPort } = require('serialport')
const {DelimiterParser} = require('#serialport/parser-delimiter')
var ports = new SerialPort({path, baudRate: 9600});
const parser = ports.pipe(new DelimiterParser({ delimiter: '\n' }))
//ports.pipe(parser);
//parser.on('ready', () => console.log('the ready byte sequence has been received'))
data=parser.on('data', console.log) // all data after READY is received
// Read the data from the serial port
//parser.on("data", (ports) => console.log(ports));
// console.log('ports', ports);
document.getElementById('ports').innerHTML = data;
The output too the application is this: SerialRead [object Object] . It should be SerialRead v. Below is the index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Read from USB port</title>
<style> </style>
</head>
<body>
<h1>Read</h1>
<center>
SerialRead <span id="ports"></span>
<div id="error"></div>
<div id="ports"></div>
</center>
</body>
<script src="./__renderer.js"></script>
</html>
main.js
const { app, BrowserWindow } = require('electron')
app.commandLine.appendSwitch("disable-gpu");
const path = require('path')
const url = require('url')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
backgroundColor: "#ccc",
webPreferences: {
nodeIntegration: true, // to allow require
contextIsolation: false, // allow use with Electron 12+
preload: path.join(__dirname, 'preload.js')
}
})
// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'tindex.html'),
protocol: 'file:',
slashes: true
}))
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// 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.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
app.quit()
})
app.on('activate', function() {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Electron and the Aruduino are reacting to each other, the the rendering is not working correctly. I would appreciate any ideas. Thank you!
serialport is a Node.js module. To be able to use a main process module in the renderer process, you need to know about the Process Model.
TL;DR; use contextIsolation and ipc to send messages between main and renderer. Keep in mind: just because you use contextIsolation, does not mean the code is safe. You need to use it properly. Don't expose powerful APIs without filtering.
I'm trying to build a Flask app where I upload pdf's and I'm working on previewing them before submitting to the back-end.
The script I'm using is as follows:
const imageUploadValidation = (function () {
"use strict";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://mozilla.github.io/pdf.js/build/pdf.js";
const onFilePicked = function (event) {
// Select file Nodelist containing 1 file
const files = event.target.files;
const filename = files[0].name;
if (filename.lastIndexOf(".") <= 0) {
return alert("Please add a valid file!");
}
const fileReader = new FileReader();
fileReader.onload = function (e) {
const pdfData = e.target.result;
let loadingTask = pdfjsLib.getDocument({ data: pdfData })
loadingTask.promise.then(function (pdf) {
console.log("PDF loaded", pdf);
pdf.getPage(1).then((page) => {
console.log("page loaded", page);
// var scale = 1.5;
// var viewport = page.getViewport({ scale: scale });
var iframe = document.getElementById("image-preview");
iframe.src = page
// var context = canvas.getContext("2d");
// canvas.height = viewport.height;
// canvas.width = viewport.width;
// var renderContext = {
// canvasContext: context,
// viewport: viewport,
// };
// var renderTask = page.render(renderContext);
// renderTask.promise.then(function () {
// console.log("Page rendered");
// });
});
})
.catch((error) => {
console.log(error);
});
};
const pdf = fileReader.readAsArrayBuffer(files[0]);
console.log("read as Data URL", pdf);
};
const Constructor = function (selector) {
const publicAPI = {};
const changeHandler = (e) => {
// console.log(e)
onFilePicked(e);
};
publicAPI.init = function (selector) {
// Check for errors.
const fileInput = document.querySelector(selector);
if (!selector || typeof selector !== "string") {
throw new Error("Please provide a valid selector");
}
fileInput.addEventListener("change", changeHandler);
};
publicAPI.init(selector);
return publicAPI;
};
return Constructor;
})();
imageUploadValidation("form input[type=file]");
The loading task promise never seems to run. Everything seems to work up until that point. I'm not familiar with this Promise syntax, so I can't be sure if the problem is there or how I'm passing in the pdf file.
P.S. The commented out code is the original way I had this setup, what
s uncommented was just me testing a different way.
Check Datatype
First you might want to check what your getting back from your FileReader, specifically what is the datatype for pdfData. If you have a look at the documentation (direct link) getDocument is expecting a Unit8Array or a binary string.
Add Missing Parameters
The next problem you have is your missing required parameters in your call to getDocument. Here is the minimum required arguments:
var args = {
url: 'https://example.com/the-pdf-to-load.pdf',
cMapUrl: "./cmaps/",
cMapPacked: true,
}
I have never used the data argument in place of the url but as long as you supply the correct datatype you should be fine. Notice that cMapUrl should be a relative or absolute path to the cmap folder. PDFJS often needs these files to actually interpret a PDF file. Here are all the files from the demo repository (GitHub pages): cmaps You'll need to add these to your project.
Instead of using data I would recommend uploading your files as blobs and then all you have to do is supply the blob URL as url. I am not familiar with how to do that, I just know its possible in modern browsers.
Where Is Your Viewer / You Don't Need iFrame or Canvas
PDFJS just needs a div to place the PDF inside of. It's picky about some of the CSS rules, for exmaple it MUST be positioned absolute, otherwise PDFJS generates the pages as 0px height.
I don't see PDFViewer or PDFLinkService in your code. It looks like you are trying to build the entire viewer from scratch yourself. This is no small endeavor. When you get loadingTask working correctly the response should be handled something like this:
loadingTask.promise.then(
// Success function.
function( doc ) {
// viewer is holding: new pdfjsViewer.PDFViewer()
// linkService is: new pdfjsViewer.PDFLinkService()
viewer.setDocument( doc );
linkService.setDocument( doc );
},
// Error function.
function( exception ) {
// What type of error occurred?
if ( exception.name == 'PasswordException' ) {
// Password missing, prompt the user and try again.
elem.appendChild( getPdfPasswordBox() );
} else {
// Some other error, stop trying to load this PDF.
console.error( exception );
}
/**
* Additional exceptions can be reversed engineered from here:
* https://github.com/mozilla/pdf.js/blob/master/examples/mobile-viewer/viewer.js
*/
}
);
Notice that PDFViewer does all the hard work for you. PDFLinkService is needed if you want links in the PDF to work. You really should checkout the live demo and the example files.
Its a lot of work but these example files specifically can teach you all you need to know about PDFJS.
Example / Sample Code
Here is some sample code from a project I did with PDFJS. The code is a bit advanced but it should help you reverse engineer how PDFJS is working under the hood a bit better.
pdfObj = An object to store all the info and objects for this PDF file. I load multiple PDFs on a single page so I need this to keep them separate from each other.
updatePageInfo = My custome function that is called by PDFJS's eventBus when the user changes pages in the PDF; this happens as they scroll from page to page.
pdfjsViewer.DownloadManager = I allow users to download the PDFs so I need to use this.
pdfjsViewer.EventBus = Handles events like loading, page changing, and so on for the PDF. I am not 100% certain but I think the PDFViewer requires this.
pdfjsViewer.PDFViewer = What handles actually showing your PDF to users. container is the element on the page to render in, remember it must be positioned absolute.
// Create a new PDF object for this PDF.
var pdfObj = {
'container': elem.querySelector('.pdf-view-wrapper'),
'document': null,
'download': new pdfjsViewer.DownloadManager(),
'eventBus': new pdfjsViewer.EventBus(),
'history': null,
'id': id,
'linkService': null,
'loaded': 0,
'loader': null,
'pageTotal': 0,
'src': elem.dataset.pdf,
'timeoutCount': 0,
'viewer': null
};
// Update the eventBus to dispatch page change events to our own function.
pdfObj.eventBus.on( 'pagechanging', function pagechange(evt) {
updatePageInfo( evt );
} );
// Create and attach the PDFLinkService that handles links and navigation in the viewer.
var linkService = new pdfjsViewer.PDFLinkService( {
'eventBus': pdfObj.eventBus,
'externalLinkEnabled': true,
'externalLinkRel': 'noopener noreferrer nofollow',
'externalLinkTarget': 2 // Blank
} );
pdfObj.linkService = linkService;
// Create the actual PDFViewer that shows the PDF to the user.
var pdfViewer = new pdfjsViewer.PDFViewer(
{
'container': pdfObj.container,
'enableScripting': false, // Block embeded scripts for security
'enableWebGL': true,
'eventBus': pdfObj.eventBus,
'linkService': pdfObj.linkService,
'renderInteractiveForms': true, // Allow form fields to be editable
'textLayerMode': 2
}
);
pdfObj.viewer = pdfViewer;
pdfObj.linkService.setViewer( pdfObj.viewer );
About 30% of the time my electron app does not execute the fs.readdir callback after I've reloaded the window that contains that script. When I open the application with electron ., the issue never occurs, it only ever occurs after I've reloaded the window.
I've tried adding a setTimeout of 5 seconds before executing fs.readdir, however this didn't change anything. Additionally, after the first time fs.readdir is run, all proceeding fs.readdir callbacks are never executed unless done immediately afterwards or if the window has never been reloaded before.
Anyone know a why this occurs and a solution?
mainWindow.js:
const fs = require('fs')
// read all files in "images" directory
function readDirectory(){
fs.readdir('images', (e, files) => {
// On error, show and return error
if(e) return console.error(e);
console.log(files)
});
}
readDirectory()
main.js:
const electron = require('electron')
const path = require('path')
const {app, BrowserWindow, Menu} = electron
process.env.NODE_ENV = 'development'
let mainWindow
let mainMenu
function createMainWindow(){
// Create mainWindow
mainWindow = new BrowserWindow({
icon:'icon.png',
webPreferences:{
nodeIntegration:true,
fullscreen: true
}
})
// Load html file
mainWindow.loadFile('mainWindow.html')
// Main menu
mainMenu = Menu.buildFromTemplate(mainMenuTemplate)
// Set main menu
Menu.setApplicationMenu(mainMenu)
// Garbage handling
mainWindow.on('close',()=>{
mainWindow = null
mainMenu = null // idk if necessary
})
if (process.env.NODE_ENV !== 'production'){
mainWindow.webContents.openDevTools()
}
}
app.on('ready',e=>{
createMainWindow()
})
const mainMenuTemplate = [
{
role:'reload',
accelerator:'cmdorctrl+r',
}
]
Also, sometimes the content is read but then it seems like it disappears. Vid: https://imgur.com/a/qZjwSBi
If you're using the fs module set:
app.allowRendererProcessReuse = false;
https://github.com/electron/electron/issues/22119
Im having issues in bypassing proxy. I’m new to both JavaScript and Electron.
I'm either facing this issue (pic below) or a blank white screen will be loaded.
My pacfile.pac :
function findProxyForURL(url, host) {
// If the hostname matches, send direct.
if (dnsDomainIs(host, “http://000.00.0.0:0000”) ||
shExpMatch(host, “(*.domain.com)”))
return “DIRECT”;
}
my index.js:
const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')
var fs = require('fs');
var pac = require('pac-resolver');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
let ProxyFunc
let pacVariable
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
//proxy: "http://000.00.0.0:0000"
// /* src="https://domain.name.com/#/random/login" */
/*mainWindow.webContents.session.setProxy({proxyRules:"http://000.00.0.0:0000"}, function () {
mainWindow.loadURL('https://domain.name.com/#/random/login');
});*/
app.commandLine.appendSwitch('proxy-bypass-list', '*.google.com;*domain.name.com;');
pacVariable = new pacfile();
pacVariable.findProxyForURL("https://domain.name.com/#/random/login", "*domain.name.com");
//ProxyFunc = new FindProxyForURL("https://domain.name.com/#/random/login","domain.name.com");
mainWindow.webContents.session.setProxy({proxyRules:"http://000.00.0.0:0000",proxyBypassRules:"domain.name.com"}, function () {
mainWindow.loadURL(path.join('file://', __dirname, 'index.html'));
});
/*mainWindow.webContents.session.setProxy({proxyRules:"http://000.00.0.0:0000"}, function () {
mainWindow.loadURL('https://domain.name.com/#/random/login');
});*/
mainWindow.openDevTools({ mode: 'bottom' });
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// 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.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Replace pacVariable = new pacfile() with
var FindProxyForURL = pac(fs.readFileSync('pacfile.pac'));
FindProxyForURL('https://domain.name.com/#/random/login').then((res) => {
console.log(res);
});
refer the docs for API
FindProxyForURL is a promise, so you'll have to write the code that requires the response inside the .then.
I am working on an application using Node.JS, Electron. This application will run its own instance of MongoDB. The start up of Mongo is working using the following code:
child = childProcess.exec(`mongod --dbpath ${appConfig.dbConfigPath}`);
However, when the user exits the program, I want to stop mongo. I have tried the following, all taken from MongoDB Documentation
child = childProcess.exec('mongod --shutdown');
and
child = childProcess.exec(`kill -2 ${child.pid}`);
yet neither of these are shutting down the process.
This application is being developed to run on the windows platform.
For clarity, here is my app configuration file. The init() function is executed from within my main.js. The shutdown() is executed in the windowMain.on('close').
calibration.js
'use strict';
const childProcess = require('child_process');
const fileUtils = require('./lib/utils/fileUtils');
const appConfig = require('./config/appConfig');
let child;
class Calibration {
constructor() {}
init() {
createAppConfigDir();
createAppDataDir();
startMongo();
}
shutdown() {
shutdownMongo();
}
}
function createAppConfigDir() {
fileUtils.createDirSync(appConfig.appConfigDir);
}
function createAppDataDir() {
fileUtils.createDirSync(appConfig.dbConfigPath);
}
function startMongo() {
child = childProcess.exec(`mongod --dbpath ${appConfig.dbConfigPath}`);
console.log(child.pid);
}
function shutdownMongo() {
console.log('inside shutdownMongo');
//This is where I want to shutdown Mongo
}
module.exports = new Calibration();
main.js
'use strict'
const { app, BrowserWindow, crashReporter, ipcMain: ipc } = require('electron');
const path = require('path');
const appCalibration = require('../calibration');
appCalibration.init();
const appConfig = require('../config/appConfig');
let mainWindow = null;
ipc.on('set-title', (event, title) => {
mainWindow.setTitle(title || appconfig.name);
})
ipc.on('quit', () => {
app.quit();
})
// Quit when all windows are closed.
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
app.quit();
}
});
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
// Create the browser window.
mainWindow = new BrowserWindow({ center: true });
mainWindow.maximize();
mainWindow.setMinimumSize(770, 400);
mainWindow.loadURL(path.join(`file://${__dirname}`, '../ui/index.html'));
mainWindow.on('close', () => {
console.log('Inside quit')
appCalibration.shutdown();
app.quit();
});
mainWindow.on('closed', function() {
mainWindow = null;
});
});
Any assistance is greatly appreciated.
You can use Ipc to send orders through your js files.
In your main.js where you defined your electron, you can put this:
ipcMain.on("shutDownDatabase", function (event, content) {
// shutdown operations.
});
Then in some part of your application code, you can put a function like this:
function sendShutdownOrder (content){
var ipcRenderer = require("electron").ipcRenderer;
// the content can be a parameter or whatever you want that should be required for the operation.
ipcRenderer.send("shutDownDatabase", content);
}
Also I think you can use the events of Electron to shut down your db, this listens to the events of your mainWindow created when you start electron
mainWindow.on('closed', function () {
// here you command to shutdowm your data base.
mainWindow = null;
});
For more information about IPC you can see here and information about the events of your window here.
With Paulo Galdo Sandoval's suggestion, I was able to get this to work. However, I needed to get the PID for mongod from Windows Task manager. To do that I added the following function to the application configuration js file
function getTaskList() {
let pgm = 'mongod';
exec('tasklist', function(err, stdout, stderr) {
var lines = stdout.toString().split('\n');
var results = new Array();
lines.forEach(function(line) {
var parts = line.split('=');
parts.forEach(function(items) {
if (items.toString().indexOf(pgm) > -1) {
taskList.push(items.toString().replace(/\s+/g, '|').split('|')[1])
}
});
});
});
}
I also declared an array variable to place the located PID in. Then I updated my shutdown function
function shutdownMongo() {
var pgm = 'mongod';
console.log('inside shutdownMongo');
taskList.forEach(function(item) {
console.log('Killing process ' + item);
process.kill(item);
});
}
With this I am now able to start and stop Mongo as my application starts up and closes.
Thanks all