Search code examples
pythonc#.netironpythonpython.net

Executing Python Script from Windows Forms .NET


I am fairly new to Python and .NET in general, but decided to ask more competent people, since I have been struggling with the issue of executing python script from Windows Forms.

The basic idea of my project is a desktop applicaton and the overall logic would be to read from a couple of selected check boxes, pass the values of those selections to my python script, from there I generate an excell table based on those results, and display this table back into the Windows Forms application.

Creating the table and managing to display it in the Desktop App is already done, but I am having serious issues with the communication between the two platforms, when it came to executing the script itself.

I have tried using IronPython and it worked perfectly, untill the fact that I found that Iron Python does not support CPython packages, like Pandas, which is build on numpy, and numpy apparantly is one of those packages. I looked over a lot of articles about this issue and the answers did not seem promising and most of the suggestions were to use pythonnet.

I tried to implement pythonnet, following numerous articles and all I managed to do, besides creating a bigger mess, is nothing as a result.

Finally, I decided to use C# Process class, but did not succeed also.

Would appreciate if there are any comments and suggestions on how to remedy this issue.

Python version: 3.7

Windows 10 (64 bit)

.NET Framework 4.7.2

Here is some of my code attempts in Windows Forms:

Implementation with the usage of the Process Class

Issue here is that I am not able to run this script due to the error messages that it cannot find the packages for the python script

var processStartInfo = new ProcessStartInfo
{
    Arguments = "C:\\Users\\Dobromir\\PycharmProjects\\pythonProject\\main.py",
    FileName = "C:\\Python27\\python.exe",
    UseShellExecute = false,
    RedirectStandardOutput = true,
    CreateNoWindow = true
};

Process.Start(processStartInfo);

Implementation using IronPython (which was working before the usage of pandas package)

Issue here is the CPython packages limitation and errors.

For IronPython I had to downgrade to Python 2.7 in order to work with it. For the rest of the examples I am using Python 3.7

ScriptEngine pythonEngine = Python.CreateEngine();
var searchPaths = pythonEngine.GetSearchPaths();

searchPaths.Add(@"C:\Python27\Lib");
searchPaths.Add(@"C:\Users\Dobromir\PycharmProjects\pythonProject\venv\Lib\site-packages");

pythonEngine.SetSearchPaths(searchPaths);

List<String> argv = new List<String>();

argv.Add("Some Value1");
argv.Add("Some Value2");

ScriptSource pythonScript = pythonEngine.CreateScriptSourceFromFile("C:\\Users\\Dobromir\\PycharmProjects\\pythonProject\\main.py");

pythonEngine.GetSysModule().SetVariable("argv", argv);
pythonEngine.SetSearchPaths(searchPaths);

ScriptScope scope = pythonEngine.CreateScope();

pythonScript.Execute(scope);

Implementation of pythonnet The issue that I got here is on the line using Py.GIL(). I believe it is having trouble finding the python files, and also tried giving the python37.dll in the variable pathToPython.

I received the error that Python.Runtime, Version=2.5.2.0, Culture=neutral....missmatch"

string pathToPython = @"C:\Users\Dobromir\AppData\Local\Programs\Python\Python37";
string path = pathToPython + "; " + Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);

Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONHOME", pathToPython, EnvironmentVariableTarget.Process);

Console.WriteLine(path);

var lib = new[]
{
  @"C:\\Users\\Dobromir\\PycharmProjects\\App37\\main.py",
  Path.Combine(pathToPython, "Lib"),
  Path.Combine(pathToPython, "DLLs")
};
   
   
string paths = string.Join("; ", lib);
Environment.SetEnvironmentVariable("PYTHONPATH", paths, EnvironmentVariableTarget.Process);

   
using (Py.GIL()) //Initialize the Python engine and acquire the interpreter lock
{
  try
  {
     Console.WriteLine("I am working");
  }
  catch (PythonException error)
  {
   Console.WriteLine("Error occured: ", error.Message);
  }
}

I Also tried creating a bash script to execute the python script and got the no module found error as well

I know that these are not the best implementations out there, but do the job.

My question is if someone has any idea on how to make this simple operation work I would be very grateful, thank you for your time and understanding

P.S - Apologies for the long post, wanted to write what I have tried before asking for help, but if someone is more interested I will provide additional information.


Solution

  • After some struggle, I found a solution to fit my needs.

    Firstly, I completely removed python 2.7 and installed back 3.10.

    I tried running the script file inside the shell command line and got the same error that the modules could not be found. What I did is try to import these modules and it gave an error, specifically for bs4 that I am using packages for python 2.x instead of 3.x packages.

    After futher investigation I discovered that the packages that I have for my script are treated as "local" packages, meaning I installed them from the IDE (PyCharm) and they work for that project only I guess.

    I also found that to "globally" access these packages I had to install them through the command line using the pip3 install <package_name>. After doing this the problem was gone and was left with running the script from the Windows Forms.

    NOTE: I did not manage to start the script using python.exe, so I used bash script for the job.

    Here is my code and I hope it helps someone down the line...

    Code in C#

    string myApp = string.Format("{0} {1}", @"C:\testing1.sh", "Hello");
    
    var processStartInfo = new ProcessStartInfo
    {
      Arguments = myApp,
      FileName = "C:\\Program Files\\Git\\git-bash.exe",
      UseShellExecute = false,
      RedirectStandardOutput = true,
      CreateNoWindow = false
    };
    
    Process.Start(processStartInfo)
    

    Code in Bash Script File

    #!/bin/bash
    
    arg1="$1"
    
    python C:/Users/Dobromir/PycharmProjects/testing/main.py "$arg1"
    

    Inside the Python file I am using sys.argv[] and accessing the arguments.

    NOTE: Passing arguments from the bash script to the python script, in this case, you will receive 2 arguments - first one is the path to the python file and the second is the variable arg1.

    Another important thing to mention is you need to have comas around the $1 - this is the property that is being send from the C# file, else it will show as empty.

    Articles that were useful:

    Installed BeautifulSoup but still get no module named bs4

    Passing arguments to Python from Shell Script

    https://unix.stackexchange.com/questions/31414/how-can-i-pass-a-command-line-argument-into-a-shell-script

    https://gist.github.com/creativcoder/6df4d349447ff1416e68

    Thank you to everyone who contributed and tried to help, I managed to learned new things with your suggestions!