Python hat viele großartige Bibliotheken, einschließlich maschinelles Lernen. Andererseits ist C # eine weit verbreitete Sprache für die Entwicklung von GUI-Anwendungen. Daher wäre es für C # -Anwendungsentwickler praktisch, Python-Skripte aus C # -Anwendungen aufrufen zu können, und vor allem sollte der Bereich der GUI-Anwendungen erweitert werden. Dieses Mal habe ich untersucht, wie ein Python-Skript aus einer C # -GUI-Anwendung aufgerufen werden kann, und einen Prototyp erstellt.
Ich habe die Anforderungen für den Prototyp identifiziert, den ich unten entwickeln möchte.
So was.
Die Quelle ist wie folgt. Es ist sehr lang, aber es ist umständlich zu bearbeiten, also füge ich es einfach ein (ausschneiden). Der Code auf der Design-Seite wird weggelassen. Bitte lesen Sie den Variablennamen des Objekts des GUI-Teils aus dem Code. Die Quelle wird im nächsten Abschnitt erläutert.
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>
///Fügen Sie der Textbox eine Zeichenfolge hinzu
/// </summary>
public void AppendText(String data, Boolean console)
{
textBox1.AppendText(data);
if (console)
{
textBox1.AppendText("\r\n");
Console.WriteLine(data);
}
}
/// <summary>
///Verhalten beim Klicken auf die Schaltfläche Ausführen
/// </summary>
private void button1_Click(object sender, EventArgs e)
{
//Vorverarbeitung
button1.Enabled = false;
button2.Enabled = true;
isCanceled = false;
readCount = 0;
outStringBuilder.Clear();
this.Invoke((MethodInvoker)(() => this.textBox1.Clear()));
//Lauf
RunCommandLineAsync();
}
/// <summary>
///Hauptteil des Befehlsausführungsprozesses
/// </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();
//Schreiben Sie in die Standardeingabe
using (StreamWriter sw = p.StandardInput)
{
sw.Write(this.textBox5.Text.Trim());
}
//Starten Sie das asynchrone Lesen von Ausgabe und Fehler
p.BeginOutputReadLine();
p.BeginErrorReadLine();
currentProcess = p;
}
void onExited(object sender, EventArgs e)
{
int exitCode;
if (currentProcess != null)
{
currentProcess.WaitForExit();
//Ausatmen von Daten, die ohne Ausatmen verbleiben
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)
{
//Abschlussmeldung
this.Invoke((MethodInvoker)(() => MessageBox.Show("Bearbeitung abgebrochen")));
}
else
{
if (exitCode == 0)
{
//Abschlussmeldung
this.Invoke((MethodInvoker)(() => MessageBox.Show("Die Verarbeitung ist abgeschlossen")));
}
else
{
//Abschlussmeldung
this.Invoke((MethodInvoker)(() => MessageBox.Show("Ein Fehler ist aufgetreten")));
}
}
}
}
/// <summary>
///Verarbeitung beim Empfang von Standardausgabedaten
/// </summary>
void p_OutputDataReceived(object sender,
System.Diagnostics.DataReceivedEventArgs e)
{
processMessage(sender, e);
}
/// <summary>
///Was tun, wenn ein Standardfehler empfangen wird?
/// </summary>
void p_ErrorDataReceived(object sender,
System.Diagnostics.DataReceivedEventArgs e)
{
processMessage(sender, e);
}
/// <summary>
///Empfängt CommandLine-Programmdaten und spuckt sie an TextBox aus
/// </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++;
//Atme zu einem zusammenhängenden Zeitpunkt aus
if (readCount % 5 == 0)
{
this.Invoke((MethodInvoker)(() => AppendText(outStringBuilder.ToString(), false)));
outStringBuilder.Clear();
//Stellen Sie den Schlaf so ein, dass er den Faden nicht einnimmt
if (readCount % 1000 == 0)
{
Thread.Sleep(100);
}
}
}
/// <summary>
///Verhalten beim Klicken auf die Schaltfläche Abbrechen
/// </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)
{
//Standardeingabebereich löschen
this.textBox5.Clear();
//Standardausgabebereich löschen
this.textBox1.Clear();
}
}
}
Es ist im Grunde eine Sammlung von Referenzen, aber die Erklärung ist unten angegeben.
`p.Start ()`
asynchron wird, werden Sie wütend, wenn Sie die Benutzeroberfläche danach bedienen, es sei denn, Sie führen sie über den UI-Thread aus. `this.Invoke ((MethodInvoker) (() => AppendText (outStringBuilder.ToString (), false)));` `Aus diesem Grund gibt es einige Aufrufe wie`. --``` p.EnableRaisingEvents = true;
, p.Exited + = onExited;
`führt den onExit-Ereignishandler am Ende des Prozesses aus, also Bereinigungsverarbeitung und Bereinigungsverarbeitung hier Es beschreibt die Beurteilung des Endcodes, die Anzeige des Abschlussdialogs usw.`using (StreamWriter sw = p.StandardInput)`
.
--Für das Abrufen der Standardausgabe und der Standardfehlerausgabe `p.OutputDataReceived + = p_OutputDataReceived;` `,` `ErrorDataReceived + = p_ErrorDataReceived;`
, um die Standardausgabe und den Standardfehler in jedem Ereignishandler zu empfangen. Ich versuche die Ausgabe zu verarbeiten.p.BeginOutputReadLine();,p.BeginErrorReadLine();Da jeder Ereignishandler jedes Mal ausgeführt wird, wenn eine Zeile ausgegeben wird, wird die Ausgabe an TextBox darin ausgeführt. Wenn Sie jede Zeile in TextBox schreiben, kann die Verarbeitung der GUI bei einer Anwendung mit einer hohen Ausgabemenge sehr lange dauern. Daher entwickeln wir Möglichkeiten, die gesamte Zeile bis zu einem gewissen Grad gemeinsam auszugeben.
# Ausführungsbeispiel (1) Führen Sie ein Python-Skript mit viel Ausgabe aus (ein Fehler in der Mitte).
Das folgende Beispiel zeigt die Ausführung eines als Beispiel erstellten Python-Skripts mit einer bestimmten Menge an Standardausgabe und Standardfehlerausgabe. Es ist ersichtlich, dass die an den Standardfehler ausgegebene Fehlermeldung auch an die TextBox ausgegeben wird und die Fehlernachrichtenbox durch den Endcode angezeigt wird.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/f2c9e895-314f-f7e9-795a-2588ca113c6d.png)
# Ausführungsbeispiel (2) Führen Sie das Python-Skript über die Standardeingabe aus
Das folgende Diagramm zeigt die Ausführung des Skripts "[MOL-Datei über Standardeingabe / -ausgabe in SMILES konvertieren](https://qiita.com/kimisyo/items/c7eb3a6a10298590438e)" über diese GUI. Sie werden feststellen, dass Sie die Verarbeitungsergebnisse von C # und Python nur durch Schreiben eines einfachen Python-Skripts übergeben können.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/f46c8aee-2950-836c-7a00-5d10fad6021d.png)
# abschließend
- Ich habe anfangs mit der Methode async / await fortgefahren, aber ich habe aufgegeben, weil ein Phänomen aufgetreten ist, das ein Deadlock in der Benutzeroberfläche zu sein schien, und ich konnte es auch nach einem ganzen Tag nicht lösen.
#
// Process p = Process.Start(psInfo);
Process p = new System.Diagnostics.Process();
p.StartInfo = psInfo;
#
――Da die Operation einigermaßen stabil ist, möchte ich eine attraktive App basierend auf diesem Prototyp erstellen, indem ich die praktischen Funktionen von Python aus C # verwende. 2020/2/24 Es wurde wie folgt behoben, da es einen schwerwiegenden Fehler gab, dass der Korrekturprozess zweimal gestartet wurde. Wir entschuldigen uns für die Unannehmlichkeiten. Referenzen- [Prozessklasse](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process?view=netframework-4.8)-[Wenn Sie eine Standardausgabe erhalten, indem Sie einen asynchronen externen Prozess starten Anmerkungen](https://qiita.com/skitoy4321/items/10c47eea93e5c6145d48)
- [Starten Sie eine externe Anwendung und warten Sie, bis sie abgeschlossen ist](https://dobon.net/vb/dotnet/process/openfile.html)
- [Was ist, wenn der Prozess einfriert, wenn das externe Programm ausgeführt wird? ](Https://www.atmarkit.co.jp/fdotnet/dotnettips/805pipeasync/pipeasync.html)
Recommended Posts