【问题标题】:C# WPF application capture python script console outputC# WPF 应用程序捕获 python 脚本控制台输出
【发布时间】:2018-03-13 03:55:24
【问题描述】:

我已经尝试了几个小时来弄清楚如何做到这一点,但我有一个 python 脚本,它被制作成一个 exe,所以它充当一个控制台应用程序我正在尝试使用 WPF 为它编写一个 GUI 包装器我有它设置到它使用命令参数执行 exe 的位置,但我想从控制台捕获输出并将其显示在文本框中,但我无法弄清楚。我尝试了多个代码 sn-ps 但它要么什么都不做,在 python exe 完成后输出,或者锁定 GUI,直到 python exe 完成然后将完成的输出转储到文本框。

有人可以看一下,看看他们是否可以帮助我解决这个问题?

public partial class MainWindow : Window


{

    //string output = string.Empty;
    private static StringBuilder output = new StringBuilder();
    private object syncGate = new object();
    private Process process;
    private bool outputChanged;


    public MainWindow()
    {
        InitializeComponent();
    }

    private void RB_Mii_Checked(object sender, RoutedEventArgs e)
    {   
    }

    //If we click the button we copy the bin file to the work directory
    private void btn_SelMiiQR_Click(object sender, RoutedEventArgs e)
    {
        //Copy the encrypted.bin file to the working directory
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Filter = "Input.bin (*.bin)|*.bin|All files (*.*)|*.*";
        openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        if (openFileDialog.ShowDialog() == true)
        {
            var fileName = openFileDialog.FileName;
            String exePath = System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName;
            //If the file exists delete the existing file and copy the newone.
            if (System.IO.File.Exists(System.IO.Path.GetDirectoryName(exePath) + "\\App\\" + System.IO.Path.GetFileName(fileName)))
            {
                System.IO.File.Delete(System.IO.Path.GetDirectoryName(exePath) + "\\App\\" + System.IO.Path.GetFileName(fileName));
            }
                System.IO.File.Copy(fileName, System.IO.Path.GetDirectoryName(exePath) + "\\App\\" + System.IO.Path.GetFileName(fileName));
        }

    }

    //If the button was clicked use the input.bin file and attempt to brute force the movable_sedpart1.bin
    private void BTN_MIIBF_Click(object sender, RoutedEventArgs e)
    {
        //If the mfg has input year or no input use it
        if (TB_MFGYR.Text.Length == 0 || TB_MFGYR.Text.Length == 4)
        {
            string DStype = null;
            string MFGYR = null;
            //Grab the Year if it has value
            if (TB_MFGYR.Text.Length == 4)
            {
                 MFGYR = TB_MFGYR.Text;
            }
            else
            {
                MFGYR = null;
            }

            if (RB_N3ds.IsChecked == true)
            {
                DStype = "new";
            }

            else if (RB_O3DS.IsChecked == true)
            {
                DStype = "old";
            }


            //Execute Command with Arguments
            String exePath = System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName;
            string dir = System.IO.Path.GetDirectoryName(exePath)+"\\App\\";

            //Start the process and export thr console output to the textbox
            CreateProcess(dir + "seedminer_launcher.exe", "Mii " + DStype + " " + MFGYR, dir);


        }
        //Else display Error Message WIP
        else
        {
            tb_outputtext.Text = null;
            tb_outputtext.Text = "MFG Year must have 4 characters or none";
        }
    }

    //Execute a new process
    private void CreateProcess(string fileName, string arguments, string workdir)
    {
        // Process process = new Process();
        process = new Process();
        process.StartInfo.FileName = fileName;
        process.StartInfo.Arguments = arguments;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.WorkingDirectory = workdir;
        process.OutputDataReceived += proc_OutputDataReceived;


        process.Start();
        process.BeginOutputReadLine();

    }

    void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        this.Dispatcher.Invoke((Action)(() =>
        {
            tb_outputtext.Text = tb_outputtext.Text + "\n" + e.Data;
            tb_outputtext.ScrollToEnd();
        }));

    }


    private void ReadData()
    {
        var input = process.StandardOutput;
        int nextChar;
        while ((nextChar = input.Read()) >= 0)
        {
            lock (syncGate)
            {
                output.Append((char)nextChar);
                if (!outputChanged)
                {
                    outputChanged = true;
                    var dispatcher = Application.Current.MainWindow.Dispatcher;
                    Dispatcher.BeginInvoke(new Action(OnOutputChanged));
                }
            }
        }
        lock (syncGate)
        {
            process.Dispose();
            process = null;
        }
    }

    private void OnOutputChanged()
    {
        lock (syncGate)
        {
            tb_outputtext.AppendText(output.ToString());
            outputChanged = false;
        }
    }


}

【问题讨论】:

    标签: c# python wpf


    【解决方案1】:

    如果我对您的理解正确,那么您希望您的 WPF 应用程序在您的 python 可执行文件运行时不断更新TextBox 的内容?

    我已经剥离了您的代码并使用了 Windows 命令ping -t 127.0.0.1 -w 10000,它每秒生成一个新行来测试您的代码。在我的机器上,您的代码按预期工作:WPF 文本框中的输出每秒更新一次。

    如果您在下面的代码中将ping 命令替换为您的python 可执行文件会发生什么?您的 python 脚本是否在每行之后输出换行符(如 Process.OutputDataReceived Event 中所述)?

    MainWindow.xaml.cs

    using System;
    using System.Diagnostics;
    using System.Windows;
    
    namespace SO_Continous_Process_Output
    {
        public partial class MainWindow : Window
        {
            private Process process;
    
            public MainWindow()
            {
                InitializeComponent();
    
                CreateProcess("ping", "-t 127.0.0.1 -w 1000", "");
            }
    
            //Execute a new process
            private void CreateProcess(string fileName, string arguments, string workdir)
            {
                // Process process = new Process();
                process = new Process();
                process.StartInfo.FileName = fileName;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.WorkingDirectory = workdir;
                process.OutputDataReceived += proc_OutputDataReceived;
    
                process.Start();
                process.BeginOutputReadLine();
            }
    
            void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    tb_outputtext.Text = tb_outputtext.Text + "\n" + e.Data;
                    tb_outputtext.ScrollToEnd();
                }));
            }
        }
    }
    

    MainWindow.xaml

    <Window x:Class="SO_Continous_Process_Output.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <TextBox Name="tb_outputtext" Text="{Binding ProcessOutput}"></TextBox>
        </Grid>
    </Window>
    

    更新

    Python 脚本

    我编写了一个 python 脚本来测试输出是否有效,我必须设置 flush=True 才能使其正常工作。

    import time
    
    while True:
        print('hi!', flush=True)
        time.sleep(1)
    

    【讨论】:

    • 我会在几个小时后尝试一下,看看它是否会将输出收集到文本框中。在某些行上它确实导出了一个新行,但在进度更新时它只更新最后一行
    • 这确实抓取了输出,但它似乎只是在 python 脚本完成执行后再次将其放入文本框中
    • 在脚本中使用print 时设置flush=True 是否有效?查看更新。
    猜你喜欢
    • 2010-09-16
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多