I am using Visual C# as UI and Python in the background.
Enter the details on a visual C# form.
Clicking a button should run a Python program which should embed the details given in the form into an XML file.
Python should process the XML and ingest into a system.
Python should monitor for the success from logs and return back the value to be displayed in C# form.
Is this possible?
You could start your python in a new process in the background and pass the form items as arguments in the process start information as if you were running the python script from the command line, like so:
var start = new ProcessStartInfo
{
FileName = _pathToPythonExecutable,
Arguments = string.Format(" {0} --arg1 {1}",_pathToYourPythonScript, //formItemValue),
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
WorkingDirectory = _currentWorkingDirectory
};
using (Process process = Process.Start(start))
{
// Do stuff
}
You'll see that in my start information, I have told the process to Redirect StandardInput, Standard Output and Standard Error. This is another way that you can pass data between the processes.
You would write to standard input like so:
process.StandardInput.Write(input);
process.StandardInput.Close();
Standard output and Standard Errors are streams, so you can read them like so
// This would be the same for standard error
using (StreamReader reader = process.StandardOutput)
{
result = reader.ReadToEnd();
}
Related
I want to pass an image object from my c# project to my python script however from my understanding whatever there is in the arguments it is considered as string and also when I try type(passedImage) in python it identifies it as a string even if I try to put a number instead of the image variable.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\Python\Python36\python.exe";
start.Arguments = string.Format("{0} {1}", #"C:\OCRonImage2.py", image );
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.CreateNoWindow = true;
using (Process process = Process.Start(start))
{
}
When executing OCRonImage2.py manually, is it an image file location that you would pass as an argument? I would be surprise if you would pass in a stream from the command line. It is no surprise that attempting to put the entire image's bytes into an argument would create a string too long. But with the error you reported, I would also believe that the python script was expecting a file path to the image. However, if you look at that python code, I wouldn't be surprised if you find it using the filepath argument to open the file, probably using Image.open(filepath,mode=r). Mode is optional, r is the default.
You are in luck however, Image.open also takes a stream. If you are willing to modify the python code there are two options:
Try converting the argument to a stream object, since the argument is a string maybe use io.StringIO()
Use input() instead of the argument passed, then you could redirect the input of the process and stream the file into your python.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\Python\Python36\python.exe";
start.Arguments = string.Format("{0}", #"C:\OCRonImage2.py");
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.RedirectStandardInput = true;
start.CreateNoWindow = true;
using (Process process = Process.Start(start))
{
StreamWriter streamWriter = process.StandardInput;
streamWriter.Write({imageString});
// ...
}
Be sure the way you encode imageString the same as the decoding is performed in the python script.
Hopefully one of these solutions will work for you.
As I work with the Anaconda distribution of Python, in my tests on an isolated conda environment, the OCR is successful with pytesseract through a Python script, on a test image.
Prerequisites to test:
install Anaconda and create an env called py3.7.4: conda create --name py3.7.4
activate the env with conda activate py3.7.4
install pytesseract with conda install -c conda-forge pytesseract
create a folder called Test and place a jpg file called ocr.jpg with the following sample image:
in the same Test folder also place a Python script called ocr_test.py with the following code:
import pytesseract
from PIL import Image
import argparse
parser = argparse.ArgumentParser(
description='perform OCR on image')
parser.add_argument("--path", "-p", help="path for image")
args = parser.parse_args()
print(pytesseract.image_to_string(Image.open(args.path)))
print("done")
The above snippet accepts the image path as a command line argument. The --path flag must be specified in order to pass the image path as an arg.
Now, in the C# code snippet below, we will:
launch the cmd shell
navigate to the workingDirectory Test folder by specifying the WorkingDirectory arg for the process.start() method.
activate Anaconda with the anaconda.bat file(replace the file path as per its location on your computer)
activate the above conda environment
call the Python script passing the imageFileName as an arg.
C# snippet:
using System.Diagnostics;
using System.Threading;
namespace PyTest
{
class Program
{
static void Main(string[] args)
{
string workingDirectory = #"C:\Test";
string imageFileName = "ocr.JPG";
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = false,
WorkingDirectory = workingDirectory
}
};
process.Start();
using (var sw = process.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
// Vital to activate Anaconda
sw.WriteLine(#"C:\Users\xxxxxxx\Anaconda3\Scripts\activate.bat");
Thread.Sleep(500);
// Activate your environment
sw.WriteLine("conda activate py3.7.4");
Thread.Sleep(500);
sw.WriteLine($"python ocr_test.py --path {imageFileName}");
Thread.Sleep(50000);
}
}
}
}
}
If you have followed the above steps, you should receive the following output on executing the C# snippet in Visual Studio:
Output:
Microsoft Windows [Version 10.0.18362.535]
(c) 2019 Microsoft Corporation. All rights reserved.
C:\xxxxxxx\Projects\Scripts>C:\Users\xxxxx\Anaconda3\Scripts\activate.bat
(base) C:\xxxxxx\Projects\Scripts>conda activate py3.7.4
(py3.7.4) C:\xxxxxxx\Projects\Scripts>python ocr_test.py --path ocr.JPG
Introduction
This is a test to see accuracy of Tesseract OCR
Test 1
Test 2
done
Note: I am unable to test with a standalone Python distro but I believe it should work just fine with that too. The key is to pass the image file path as an argument to the Python script too. That way, the image file path passed as argument from C# is treated similarly by Python too. Also, using Image.open() does the following(from the docs):
Opens and identifies the given image file. This is a lazy operation;
this function identifies the file, but the file remains open and the
actual image data is not read from the file until you try to process
the data
You can save the image as a file somewhere on your local machine and give the python program the path to read it.
That's the easiest way I think you can do.
Edited: You can use a temporary file to make sure the file can be deleted in the future
http://www.java2s.com/Tutorial/CSharp/0300__File-Directory-Stream/Createatempfileanddeleteit.htm
http://www.vcskicks.com/temporary-file.php
I think this will be bad to pass IMAGE as Argument.
Good options to go with:
Stdin on your python example, and RedirectStandardInput on your c#.
TCP Communication. Using TCP Services (No Internet Needed)
Sharing Memory. (More Info, ReadyLib)
We have our own C++ compiled OCR as .exe file that takes the image location as a parameter and return a string, and we place it in folder within our web-API 2 application folder, now we start the OCR as a process from the web-api, get the output and return it back.
everything works good in a local machine, when we deploy the API in the server, the output cannot be retrieved unless we replace the Application pool identity with the Admin in the application pool. At this stage we need to use the Application pool identity (or any other user but the admin) and still be able to retrieve the output from the process here is our code:
ProcessStartInfo info = new ProcessStartInfo
{
WorkingDirectory = enginepath,
CreateNoWindow = true,
UseShellExecute = false,
FileName = enginepath+"//"+"OCR.exe",
RedirectStandardOutput = true,
Arguments = " "+imageFilepath
};
using (Process process = Process.Start(info))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
return result;
}
}
we tried all the popular ways from giving the right permissions,loading the user profile=true, and still can't be able to retrieve the output.
*we need to be able to get the output within the web server application.
I am trying to run a process, hide its window and then print its output in my program. I am achieving it with this code:
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = path,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
Verb = "runas"
}
};
proc.Start();
while(!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
Console.WriteLine(line);
}
It works with other programs but for this one it's stopping at some point. My guess is that it sends too much messages, because before actually loading it spams a ton of messages that say: "Initializing... n%". It reads it until it reaches around 90-95% and then stops reading. My guess is that it can't read anymore because it sent too much messages. What can I do to read the whole output?
I found out how to make a workaround. It turns out the program actually needed input inbetween the initialization and the actual result. Thanks to #Alexandru Clonțea I decided that I would not run the process, but create a .bat file, which contains the following command:
program.exe > output.txt < input.txt
Where program.exe is the executable, output.txt is the file that is going to contain the output of the program and input.txt in my case is just an empty text file so I can just get some kind of an input and make the program finish.
First of all I do not know if it is a bad practice to call python script from c# so if this is the case please tell me.My current problem is as follows.
MY c# code only runs the python script partially....
means (python script create only 4 files when it is supposed to create 10 files)
But When I run my script from cmd in windows I see complete functionality....
Another thing I saw is when I stop my Visual Studio(2013) I see the complete functionality
I am calling the python script(main.py) from c# like this...
public JsonResult FetchscrapyDataUrl(String website)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\ProgramData\Anaconda3\python.exe";
start.Arguments = #"C:\Users\PycharmProjects\scraping_web\scrape_info\main.py";
//this is path to .py file from scrapy project
start.CreateNoWindow = false; // We don't need new window
start.UseShellExecute = false; // Do not use OS shell
//start.RedirectStandardOutput = true;// Any output, generated by application will be redirected back
start.RedirectStandardError = true; // Any error in standard output will be redirected back (for example exceptions)
Console.WriteLine("Python Starting");
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd(); // Here are the exceptions from our Python script
string result = reader.ReadToEnd(); // Here is the result of StdOut(for example: print "test")
Console.Write(result);
}
}
}
Why I am getting complete script functionality when I stop in Visual Studio(2013)??
I dont understand you motivation behind this. But why dont you use IronPython which is excellent addition to the .NET Framework, providing Python developers with the power of the .NET framework.
I am trying to run a Batch file from .net/c# using System.Diagnostics.Process. Somehow it does not execute xcopy command of the Batch file.
Sample Batch File:
#copy test to test2 including sub directories
xcopy c:\test\ c:\test2
C# code:
public void RunMSIBatchFile(string _workingDirectory, string batchFileName)
{
var process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = _workingDirectory,
FileName = _workingDirectory + batchFileName,
CreateNoWindow = true,
RedirectStandardError = true
}
};
process.OutputDataReceived += ProcessOutputDataReceived;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit(Convert.ToInt32(CommandTimeOut.TotalMilliseconds));
}
If I change UseShellExecute to true then it works but then there seems to be no way to capture standard output.
Has anyone faced such problem?
I've tested your exact code, and appear to be able to receive data just fine. However, since the read occurs asynchronously, it is possible for WaitForExit(...) to return before you have read all of the data. It appears that the end of the data is signalled by the Data property of the DataReceivedEventArgs passed to the OutputDataReceived event handler being null.
It is also worth noting that if xcopy requests input from the user (e.g. in the case of a file with the same name existing in the destination) it appears that no data is returned. You may want to check for this in your batch file, or also handle data from the Standard Error stream.