spawn python process is Reloading the page in electron - python

[Update]: Before reloading, fora fraction of second a warning [violation] 'click' hander took xx ms is showinf. (found out from screen recording)
I am using Electron with React.
To communicate with main process(Electron) and renderer(React) i am using ipcRederer and ipcMain Modules and using hooks to load an image on web page.
The ipcMain module spawns a python process and its result (a image location) is send back to which then loads the image that was create by a python script by providing the location
Simple dataflow is: React ->Hooks -> ipcRender -> ipcMain -> spawn python process -> ipc Main - > ipcRender -> Hooks(useState)
When i execute it it shows the image but immediateby reloads the webpage.
Going deep i found that the problem was spawning the python process. That is reloading the page.
main.js(electron):
const { ipcMain } = require('electron');
ipcMain.on('synchronous-message', (event, arg) => {
//.................Code to run a Python script...............
var python = require('child_process').spawn('python', ['./public/test.py']);
python.stdout.on('data',function(data){
//send the output of python to React
event.returnValue = (data.toString('utf8'));
});
//.................................
})
app.js(react):
const { ipcRenderer } = window.require('electron');
function Home(){
const [graph, setGraph] = useState("");
function test(){
setGraph(ipcRenderer.sendSync('synchronous-message', 0));
}
return (<div className="Home">
<div onClick={test}>CLick Me</div>
<div id="graph">
<img src={graph} />
</div>
</div>)
}
graph variable contains the returned value from ipcMain message

Related

How to make a progress bar for an Electron app by using child_process on a Python Script?

The problem
I am creating an Electron-React frontend for my Python script.
Electron and Python communicate via Node.js child_process module, using the function spawn to call my Python script.
The script does the following task:
Gets path to a folder, which contains 15 pdf files.
Loads 1 pdf file at a time.
Skims through it to find the first word of the 5th page.
Saves the result in a list.
Continues until all pdf files have been read.
As you can see, this takes some time to process this script.
What I want to do: Once the process is called from Electron, I want the progress bar to appear, indicating how many pdf files are processed out of the 15 given. For example: 1/15 done, 2/15 done, and etc.
I tried googling the answer but after a week I'm at my wits end. I would very much appreciate any help I could get.
Disclaimer: I know of other good packages like Python Eel which does the job (sort of, in a hackish way). But I really want it to work with CLI.
The setup
I used Electron React Boilerplate for my purposes.
For simplicity I used a dummy function for python:
assets/python/main.py
import sys
import time
# Track the progress
progress = 0
def doStuff():
# Access the global variable
global progress
# Simulate time taken to do stuff. Set at 30 seconds per iteration.
for i in range(15):
progress += 1
time.sleep(30)
# The result
return "It is done!"
# Print is used to get the result for stdout.
print(doStuff())
src/main/main.ts
import { spawn } from 'child_process';
import path from 'path';
import { app, BrowserWindow, ipcMain, webContents } from 'electron';
...
let mainWindow: BrowserWindow | null = null;
...
app
.whenReady()
.then(() => {
ipcMain.handle('readPdfs', async (event, arg) => {
// Spawn a python instance
const python = spawn('python', [
path.join(__dirname, '..', '..', 'assets', 'python', 'main.py')
]);
// Get the result and pass it on to the frontend
python.stdout.on('data', (data) => {
const result = data.toString('utf8');
mainWindow.webContents.send('output', result);
});
});
...
src/main/preload.ts
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
...
let mainWindow: BrowserWindow | null = null;
...
contextBridge.exposeInMainWorld('electron', {
...
// Just sending a command forward and getting a response.
readPdfs: async () => ipcRenderer.invoke('readPdfs'),
readPdfsOutput: (callback: any) => ipcRenderer.on('output', callback),
});
src/renderer/App.tsx
const Hello = () => {
const [text, setText] = useState('');
const handleReadPdfs = async (): Promise<void> => {
await window.electron.readPdfs();
await window.electron.output((event: any, response: string) => {
setText(response);
});
};
The question
What I have set up works. I manage to get the "It is done!" message all right. The problem is I want to get the value progress from main.py every time it increments, while the process is busy reading the pdfs.
Is there a way to get that value from the python script using child_process without interrupting a time consuming process?

How do I load an image from HTML to Flask?

I have an HTML page that lets you upload a picture and displays it (very simple). But how do I send that particular loaded image into my flask app so that I could, say, perform some image processing on it using python's openCV or PIL packages?
Disclaimer
I've done similar thing recently, It may not be a 100% solution for you, but using parts of this code will propably solve all of your problems.
Flask Code
This part gets all files uploaded from browser and saves them with their respected extensions.
if request.method=="PUT":
for f in request.files.getlist("Files"): #<---- note 1
f.save(os.path.join(Path_To_Folder, f.filename))
return jsonify({"result": "res"}), 200 #status code is only thing important here
HTML
This just allows you to select file(s) from your device
<input type="file" id="file_loader" multiple/>
JavaScript
This code
const file_loader = document.getElementById("file_loader");
file_loader.onchange = async function(e){
alert("Upload started")
let sending = new FormData(); //creates form data object
//this for loop adds all files you selected to a form object
for (let i = 0; i < file_loader.files.length; i++) {
sending.append("Files", file_loader.files[i]); //<---- note 1
}
//this part just sends all the files to server
const podaci = await fetch(url_filesistem, {
method: "PUT",
body: sending,
})
.then((response) => response.json())
.then((pod) => {return pod;});
//removing all selected files from an input, so every time you want to select files
//its just those files, not ones previously selected
while (file_loader.length > 0) {
file_loader.pop();
}
alert("Finnished uploading")
}
Note 1
String "Files" mentioned in both lines needs to be the same in order for this method to work.
Advice
First save all the files on the server and then do the processing. I don't know what "f" object from for loop in python contains, therefore I don't know whether you can process them immediately.
Feel free to ask me anything regarding my code!
Best regards!

How to trigger a Python or C# script to run when .Net/C# Windows service stops?

We have a c#/.net Windows service that parses big log files for us and updates a meta table when it does so. The problem is whenever you need to stop the service or (services, we have multiple of them running), one must manually delete the files that are in the process of being parsed in the local folder and also update the queue DB table where it tracks files to process.
I want to automate this. I am much more familiar with python so ideally, it would be a python script as opposed to .net. Is it possible to have a script that will trigger when the service is stopped? How would one do this?
I have tried doing this internally in the .net service but since it's multithreaded, files don't get cleaned up neatly. There's always a "can't stop service because another process is using it". It is like the service gets stuck trying to delete files when the Onstop() method is called. This was how I had tried to do it internally within the service:
protected override void OnStop()
{
ProducerConsumerQueue.Dispose();
Logger.Info($"{ProducerConsumerQueue.Count()} logs will be canceled");
CancellationTokenSource.Cancel();
FileUtil.DeleteFilesInProgress(Constants.ODFS_STAGING);
MetadataDbContext.UpdateServiceEntriesOnServiceReset();
//look into some staging directory, delete all files.
Logger.Info($"{ProducerConsumerQueue.Count()} logs canceled");
}
public static void DeleteFilesInProgress(string directory)
{
var constantsutil = new ConstantsUtil();
constantsutil.InitializeConfiguration();
try
{
System.IO.DirectoryInfo di = new DirectoryInfo(directory);
foreach (FileInfo file in di.GetFiles())
{
file.Delete();
}
}
catch(Exception ex)
{
Logger.Error(ex.Message);
string subject = Constants.GENERAL_EMAIL_SUBJECT;
string body = "The following error occured in Client.Util.ConstantsUtil:";
string error = ex.ToString(); //ex.ToString makes it more verbose so you can trace it.
var result = EmailUtil.Emailalert(subject, body, error);
}
}
public static int UpdateServiceEntriesOnServiceReset()
{
int rowsAffected = 0;
try
{
string connectionString = GetConnectionString();
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = connectionString;
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = $"UPDATE {Constants.SERVICE_LOG_TBL} SET STATUS = '0'";
cmd.Connection = connection;
connection.Open();
rowsAffected = cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
Logger.Error($"{ex.Message.ToString()}");
string subject = Constants.GENERAL_EMAIL_SUBJECT;
string body = "The following error occured in Client.MetadatDbContext while Parser was processing:";
string error = ex.ToString(); //ex.ToString makes it more verbose so you can trace it.
var result = EmailUtil.Emailalert(subject, body, error);
}
return rowsAffected;
}
You can run your script from OnStop:
using System.Diagnostics;
Process.Start("python yourscript.py");
// or whatever the command for executing your python script is on your system.
And then use something like pywin32's win32service to find out the status of the service that launched the script, and then wait for it to die and release its hold on the files.
Then wipe them.

Angular python-shell fails on first line

Set Up
I am having an odd problem.
I have a website and I am hoping to run a python script when someone presses a button.
So, I have followed this tutorial: https://www.npmjs.com/package/python-shell
Question:
My console prints this: ReferenceError: require is not defined
on this line: var PythonShell = require('python-shell');
This is the first line in my .js function. I added this line after the npm install step.
How do I overcome this error?
Potentially Helpful Notes
My script looks like this:
angular.module('MYApp')
.controller('MyTypeController', function ($scope, $http, $stateParams, $state) {
$scope.runPythonRoutine = function () {
console.log("Error 1");
var PythonShell = require('python-shell');
var myshell = new PythonShell.run('hello.py', function (err, results) {
console.log("Error 2");
// script finished
});
};
...
My package.json contains:
...
"node-uuid": "1.4.7",
"python-shell": "^0.5.0",
...
require() does not exist in the browser/client-side JavaScript. You need require.js to use require() in the browser. You must also add this to package.json:
"require.js": "version"
and specify require.js in the <script> tag of the html code at the bottom, like so:
<script src="/location/of/require.js/"></script>

How to stream a constantly changing image

The Setup:
I have an NGINX web server. On a web page I am running the javascript to refresh the file tmp_image.jpg every half a second:
var img = new Image();
img.onload = function() {
var canvas = document.getElementById("x");
var context = canvas.getContext("2d");
context.drawImage(img, 0, 0);
setTimeout(timedRefresh, 500);
};
function timedRefresh() {
// just change src attribute, will always trigger the onload callback
try {
img.src = 'tmp_image.jpg?d=' + Date.now();
}catch(e){
console.log(e);
}
}
setTimeout(timedRefresh, 100);
With the HTML:
<canvas id="x" width="700px" height="500px"></canvas>
On another process using a raspberry pi camera I am running a script to write a image to a file tmp_image.jpg with the library picamera:
for frame in cam.camera.capture_continuous(raw, format="rgb", use_video_port=True):
img = frame.array
cv2.imwrite("tmp_image.jpg", img)
The problem:
I am receiving the error:
GET http://192.168.1.234/images/tmp_img.jpg?d=1513855080653 net::ERR_CONTENT_LENGTH_MISMATCH
Which then leads the refresh to crash (even though I have put it in a try catch)
I believe this is because NGINX is trying to read the file while it is being written to. So what should I do? And is this awfully inefficient?

Categories