0

I have been trying for hours to figure out how to do this but I have a python script that was made into an exe so it acts a console application I'm trying to write a GUI wrapper for it using WPF I have it set up to where it does execute the exe with the command arguments but I want to capture the output from the console and display it in a text box and i can not figure it out. I have tried multiple code snippets but it either does nothing, Outputs after the python exe has finished, or locks up the GUI until the python exe finishes then dumps the completed output to the textbox.

Would someone be able to take a look and see if they can help me with this?

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;
        }
    }


}

1 Answer 1

1

If I understand you correctly then you want your WPF app to continously update the content ot the TextBox while your python executable is running?

I have stripped down your code and used the Windows command ping -t 127.0.0.1 -w 10000 which generates a new line every second to test your code. On my machine your code works as expected: the output in the WPF textbox is updated every second.

What happens if you replace the ping command with your python executable in the code below? Does your python script output a newline character after each line (as mentioned in 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>

Update

Python script

I wrote a python script to test if the output works and I had to set flush=True in order to make it work.

import time

while True:
    print('hi!', flush=True)
    time.sleep(1)
Sign up to request clarification or add additional context in comments.

3 Comments

I'll give this a try in a few hours and see if it gathers the output into the textbox. On some line it does export a new line but on the progress updates it just updates the last line
This does grab the output but it only seems to put it in the text box again after the python script has finished executing
Does it work if you set flush=True when you use print in your script? See update.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.