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.
J'ai identifié les exigences du prototype que je souhaite développer ci-dessous.
Comme ça.
--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 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();
}
}
}
C'est essentiellement une collection de références, mais l'explication est donnée ci-dessous.
p.Start () '', si vous utilisez l'interface utilisateur après cela, vous vous fâcherez si vous ne l'exécutez pas à partir du thread de l'interface utilisateur.
this.Invoke ((MethodInvoker) (() => AppendText (outStringBuilder.ToString (), false)));
C'est pourquoi il y a des appels comme`.
--``` p.EnableRaisingEvents = true; ,
p.Exited + = onExited;
exécute le gestionnaire d'événements onExit à la fin du processus, donc le traitement de nettoyage et le traitement de nettoyage ici Il décrit le jugement du code de fin, l'affichage de la boîte de dialogue de fin, etc.`. --Pour la récupération de la sortie standard et de la sortie d'erreur standard,
p.OutputDataReceived + = p_OutputDataReceived;
, ʻErrorDataReceived + = p_ErrorDataReceived;
`` pour recevoir la sortie standard et l'erreur standard dans chaque gestionnaire d'événements. J'essaye de traiter la sortie.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)