Exécuter des scripts Python à partir d'applications C # GUI

introduction

Python possède de nombreuses bibliothèques géniales, notamment l'apprentissage automatique. D'autre part, C # est un langage largement utilisé pour développer des applications GUI. Par conséquent, il serait pratique pour les développeurs d'applications C # de pouvoir appeler des scripts Python à partir d'applications C #, et surtout, la gamme d'applications GUI devrait être élargie. Donc, cette fois, j'ai étudié comment appeler un script Python à partir d'une application C # GUI et créé un prototype.

environnement

Prototype que vous souhaitez développer

J'ai identifié les exigences du prototype que je souhaite développer ci-dessous.

Ce qui a été fait

écran

Comme ça. image.png

Description de l'écran

--Pour Python Path, spécifiez l'emplacement de python.exe avec le chemin complet. Pour Anaconda, vérifiez l'emplacement de python.exe dans l'environnement virtuel d'Anaconda et spécifiez-le. --Spécifiez le répertoire d'exécution du script Python dans le répertoire de travail. Lorsqu'un chemin de fichier est spécifié comme argument, il peut être décrit comme un chemin relatif à partir d'ici.

La source

La source est la suivante. C'est très long, mais c'est difficile à éditer, donc je le colle simplement (découpé). Le code côté conception est omis. Veuillez lire le nom de variable de l'objet de la partie GUI à partir du code. La source sera expliquée dans la section suivante.

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace PythonCommandExecutor
{

    public partial class Form1 : Form
    {
        private Process currentProcess;
        private StringBuilder outStringBuilder = new StringBuilder();
        private int readCount = 0;
        private Boolean isCanceled = false;

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        ///Ajouter une chaîne à la zone de texte
        /// </summary>
        public void AppendText(String data, Boolean console)
        {
            textBox1.AppendText(data);
            if (console)
            {
                textBox1.AppendText("\r\n");
                Console.WriteLine(data);
            }
        }

        /// <summary>
        ///Comportement lorsque le bouton d'exécution est cliqué
        /// </summary>
        private void button1_Click(object sender, EventArgs e)
        {
            //Prétraitement
            button1.Enabled = false;
            button2.Enabled = true;
            isCanceled = false;
            readCount = 0;
            outStringBuilder.Clear();
            this.Invoke((MethodInvoker)(() => this.textBox1.Clear()));

            //Courir
            RunCommandLineAsync();
        }

        /// <summary>
        ///Corps du processus d'exécution de la commande
        /// </summary>
        public void RunCommandLineAsync()
        {

            ProcessStartInfo psInfo = new ProcessStartInfo();
            psInfo.FileName = this.textBox2.Text.Trim();
            psInfo.WorkingDirectory = this.textBox3.Text.Trim();
            psInfo.Arguments = this.textBox4.Text.Trim();

            psInfo.CreateNoWindow = true;
            psInfo.UseShellExecute = false;
            psInfo.RedirectStandardInput = true;
            psInfo.RedirectStandardOutput = true;
            psInfo.RedirectStandardError = true;

            // Process p = Process.Start(psInfo);
            Process p = new System.Diagnostics.Process();
            p.StartInfo = psInfo;

            p.EnableRaisingEvents = true;
            p.Exited += onExited;
            p.OutputDataReceived += p_OutputDataReceived;
            p.ErrorDataReceived += p_ErrorDataReceived;

            p.Start();

            //Écrire dans l'entrée standard
            using (StreamWriter sw = p.StandardInput)
            {
                sw.Write(this.textBox5.Text.Trim()); 
            }

            //Commencer à lire la sortie et l'erreur de manière asynchrone
            p.BeginOutputReadLine();
            p.BeginErrorReadLine();

            currentProcess = p;
        }

        void onExited(object sender, EventArgs e)
        {
            int exitCode;

            if (currentProcess != null)
            {
                currentProcess.WaitForExit();

                //Expirer des données qui restent sans être expirées
                this.Invoke((MethodInvoker)(() => AppendText(outStringBuilder.ToString(), false)));
                outStringBuilder.Clear();

                exitCode = currentProcess.ExitCode;
                currentProcess.CancelOutputRead();
                currentProcess.CancelErrorRead();
                currentProcess.Close();
                currentProcess.Dispose();
                currentProcess = null;

                this.Invoke((MethodInvoker)(() => this.button1.Enabled = true));
                this.Invoke((MethodInvoker)(() => this.button2.Enabled=false));


                if (isCanceled)
                {
                    //Message de fin
                    this.Invoke((MethodInvoker)(() => MessageBox.Show("Traitement annulé")));
                }
                else
                {
                    if (exitCode == 0)
                    {
                        //Message de fin
                        this.Invoke((MethodInvoker)(() => MessageBox.Show("Le traitement est terminé")));
                    }
                    else
                    {
                        //Message de fin
                        this.Invoke((MethodInvoker)(() => MessageBox.Show("Une erreur est survenue")));
                    }
                }
            }
        }

        /// <summary>
        ///Traitement lorsque des données de sortie standard sont reçues
        /// </summary>
        void p_OutputDataReceived(object sender,
            System.Diagnostics.DataReceivedEventArgs e)
        {
            processMessage(sender, e);
        }

        /// <summary>
        ///Que faire lorsqu'une erreur standard est reçue
        /// </summary>
        void p_ErrorDataReceived(object sender,
            System.Diagnostics.DataReceivedEventArgs e)
        {
            processMessage(sender, e);
        }

        /// <summary>
        ///Reçoit les données du programme CommandLine et les recrache dans TextBox
        /// </summary>
        void processMessage(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (e != null && e.Data != null && e.Data.Length > 0)
            {
                outStringBuilder.Append(e.Data + "\r\n");
            }
            readCount++;
            //Expirez à un moment cohérent
            if (readCount % 5 == 0)
            {
                this.Invoke((MethodInvoker)(() => AppendText(outStringBuilder.ToString(), false)));
                outStringBuilder.Clear();
                //Mettre en veille pour qu'il n'occupe pas le fil
                if (readCount % 1000 == 0)
                {
                    Thread.Sleep(100);
                }
            }
        }

        /// <summary>
        ///Comportement lorsque le bouton d'annulation est cliqué
        /// </summary>
        private void button2_Click(object sender, EventArgs e)
        {
            if (currentProcess != null)
            {
                try
                {
                    currentProcess.Kill();
                    isCanceled = true;
                }
                catch (Exception e2)
                {
                    Console.WriteLine(e2);
                }
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //Effacer la zone de saisie standard
            this.textBox5.Clear();
            //Effacer la zone de sortie standard
            this.textBox1.Clear();
        }
    }
}

Commentaire de la source

C'est essentiellement une collection de références, mais l'explication est donnée ci-dessous.

p.BeginOutputReadLine();,p.BeginErrorReadLine();Étant donné que chaque gestionnaire d'événements est exécuté à chaque fois qu'il y a une sortie de ligne, la sortie vers TextBox est effectuée dans cela. Si vous écrivez chaque ligne dans TextBox, le traitement de l'interface graphique peut prendre beaucoup de temps dans le cas d'une application qui a une grande quantité de sortie, nous sommes donc en train de concevoir des moyens de les afficher tous ensemble dans une certaine mesure.




# Exemple d'exécution (1) Exécuter un script Python avec beaucoup de sortie (une erreur au milieu)
 Voici un exemple d'exécution d'un script Python créé en tant qu'exemple, qui contient une certaine quantité de sortie standard et de sortie d'erreur standard. On peut voir que la sortie du message d'erreur vers l'erreur standard est également sortie vers la zone de texte et l'erreur MessageBox est affichée par le code de fin.

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/f2c9e895-314f-f7e9-795a-2588ca113c6d.png)


# Exemple d'exécution (2) Exécuter un script Python via une entrée standard
 Ce qui suit est un schéma d'exécution du script de "[Convertir le fichier MOL en SMILES via l'entrée / sortie standard](https://qiita.com/kimisyo/items/c7eb3a6a10298590438e)" via cette interface graphique. Vous vous rendrez compte que vous pouvez transmettre les résultats du traitement de C # et Python simplement en écrivant un simple script Python.
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/f46c8aee-2950-836c-7a00-5d10fad6021d.png)


# en conclusion
 --J'étais initialement en train d'utiliser la méthode async / await, mais j'ai abandonné car un phénomène comme une impasse dans l'interface utilisateur s'est produit et je ne pouvais pas le résoudre même après une journée entière.
 --En produisant les informations de progression sur la sortie standard du script Python, je pense que vous pouvez facilement afficher la progression avec la barre de progression du côté C #.
 ――Le fonctionnement étant raisonnablement stable, je voudrais créer une application attrayante basée sur ce prototype en utilisant les fonctions pratiques de Python à partir de C #.

# 2020/2/24 révisé

 Corrigé comme suit car il y avait un bug fatal qui a démarré le processus deux fois. Nous nous excusons pour le dérangement.
        // Process p = Process.Start(psInfo);
        Process p = new System.Diagnostics.Process();
        p.StartInfo = psInfo;

# Les références
 - [Classe de processus](https://docs.microsoft.com/ja-jp/dotnet/api/system.diagnostics.process?view=netframework-4.8)
 - [Remarques sur la réception de la sortie standard lors du démarrage d'un processus externe asynchrone](https://qiita.com/skitoy4321/items/10c47eea93e5c6145d48)
 - [Démarrez une application externe et attendez qu'elle se termine](https://dobon.net/vb/dotnet/process/openfile.html)
 - [Que faire si le processus se fige lorsque le programme externe est exécuté? ](Https://www.atmarkit.co.jp/fdotnet/dotnettips/805pipeasync/pipeasync.html)



Recommended Posts

Exécuter des scripts Python à partir d'applications C # GUI
Appel de scripts Python à partir de Python intégré en C ++ / C ++
Exécutez des scripts Python à partir d'Excel (en utilisant xlwings)
Exécutez le script illustrator à partir de python
Exécuter du code Python à partir de l'interface graphique C #
Exécutez des scripts Python de manière synchrone à partir de C #
Comment exécuter un programme Python à partir d'un script shell
Exécuter l'interpréteur Python dans le script
Utiliser Django à partir d'un script Python local
Exécutez une application Web Python avec Docker
Comment exécuter des scripts Maya Python
J'ai créé une application graphique avec Python + PyQt5
Créer un tableau C à partir d'une feuille Python> Excel
Créer une nouvelle tâche Todoist à partir d'un script Python
Exécutez Python à partir d'Excel
"Kit Python" qui appelle des scripts Python depuis Swift
Exécutez des fichiers Python à partir de HTML en utilisant Django
Un mémorandum pour exécuter un script python dans un fichier bat
Points Python du point de vue d'un programmeur en langage C
Script Python qui crée un fichier JSON à partir d'un fichier CSV
[Python] Comment appeler une fonction de c depuis python (édition ctypes)
Exécuter un script multiligne dans un PDB
Exécuter le script Python à partir du fichier de commandes
Résoudre ABC163 A ~ C avec Python
Appeler C depuis Python avec DragonFFI
Toucher les objets Python d'Elixir
Explication ABC127 A, B, C (python)
Créer une interface graphique python à l'aide de tkinter
Faisons une interface graphique avec python.
ABC166 en Python A ~ C problème
Appeler popcount depuis Ruby / Python / C #
python / Créer un dict à partir d'une liste.
Résoudre ABC168 A ~ C avec Python
Exécutez Aprili depuis Python sur Orange
Résoudre ABC036 A ~ C avec Python
Conseils pour appeler Python à partir de C
Détection d'erreur Python exécutée à partir de Powershell
Résoudre ABC162 A ~ C avec Python
Résoudre ABC167 A ~ C avec Python
ABC128 Commentaire A, B, C (python)
Résoudre ABC158 A ~ C avec Python
Explication ABC126 A, B, C (python)
Résoudre ABC037 A ~ C avec Python
Exécutez Ansible à partir de Python à l'aide de l'API
Lancer le script Python en service
Exécutez un algorithme simple en Python
Ecrire un script batch avec Python3.5 ~
Appeler C / C ++ depuis Python sur Mac
Exécutez le script Python à partir de Cisco Memorandum_EEM
Appeler le langage C depuis Python (python.h)
Créez un script shell pour exécuter le fichier python plusieurs fois
Appelez dlm depuis python pour exécuter un modèle de régression à coefficient variable dans le temps
Passer la liste de Python vers C ++ par référence dans pybind11
[Python] Application Web à partir de 0! Pratique (3) - Mise en œuvre de l'API
Exécutez Cloud Dataflow (Python) depuis AppEngine
Résoudre ABC175 A, B, C avec Python
Exécuter un script depuis Jupyter pour traiter
Exécuter un script local sur un hôte distant
Créer une application GUI simple en Python