0

I built this simple form to illustrate my issue:

enter image description here

What I need:

  1. Execute a script that I have in python (built an .exe from pyinstaller) clicking on "button1"
  2. Update the progress bar and the label with the percentage the python script prints out as soon as it prints.

What I've got:

  • The code works fine, does what I need, but not as soon as the script print the percentage.

Here's my code:

private static double value= 0;

private void button1_Click(object sender, EventArgs e)
{
    Process process = new Process();
    process.StartInfo.FileName = @"C:\Users\User\Desktop\testeProgresso.exe";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.OutputDataReceived += new DataReceivedEventHandler((_sender, _e) =>
    {
        // Prepend line numbers to each line of the output.
        if (!String.IsNullOrEmpty(_e.Data))
        {
            value= double.Parse(_e.Data);
        }
    });

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

    label1.Text += value.ToString();
    progressBar1.Value = (int)value;

    process.WaitForExit();
    process.Close();
}

I need it does so when the process is finished, I'll close this form and open another.

EDIT:

private void button6_Click(object sender, EventArgs e)
        {
            Process process = new Process();
            process.StartInfo.FileName = @"C:\Users\User\Desktop\testeProgresso.exe";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.SynchronizingObject = this;
            process.EnableRaisingEvents = true;
            process.Exited += (s, evt) => { process?.Dispose();};
            process.OutputDataReceived += new DataReceivedEventHandler((_sender, _e) =>
            {
                
                if (!String.IsNullOrEmpty(_e.Data))
                {
                    value = double.Parse(_e.Data);
                    label1.Text += value.ToString();
                    progressBar1.Value = (int)value;
                    
                }
            });

            process.Start();
            process.BeginOutputReadLine();
            //process.Close();
        }


as Jimi suggested, I made some changes. But it still just updates the whole thing after the process is done. Plus, I couldnt implement the Exited event, because I dont know how.

Community
  • 1
  • 1
  • Move `label1.Text = value.ToString();` and `progressBar1.Value = (int)value;` in the `OutputDataReceived` handler. Set `process.SynchronizingObject = this;`. Note that `WaitForExit()` is duplicated. I'd suggest to remove it completely and subscribe to the `Exited` event (requires `process.EnableRaisingEvents = true;`) instead, where you call `process?.Dispose();` – Jimi Apr 02 '20 at 21:39
  • Hi, @Jimi!! Well, actually this is the code I came up with after hours of research and studying. I'm relatively new to programming. I did not understand the last part of your comment. "where you call...". Well, I do not call `process?.Dispose`anywhere... Should I? Thank you for your comment!! – Leandro Manes Apr 03 '20 at 00:05
  • I mean, subscribe to the `Exited` event and, in the event handler, call `process?.Dispose();` (the `Process` object is disposable, so you need to call its `Dispose()` method). – Jimi Apr 03 '20 at 00:10
  • It didnt work, @Jimi... I eliminated the `WaitForExit()`, set`SynchronizingObject = this` and evertything else you said, but I think I got it wrong. When I created the event `Exited`, the event dont know what "process" is. Sorry. Can you do a little extra help here? – Leandro Manes Apr 03 '20 at 12:32
  • The code still updating after the script is done. I'm in windowsform... ```Console.WriteLine()``` works? I edited the code again – Leandro Manes Apr 03 '20 at 13:54
  • You're running that Process: it *depends* on your Process now, it may not be able to quit until you release it (or you release the Streams). --- In WinForms (or WPF), `Console.WriteLine()` prints to Visual Studio's `Output` panel. Similar to `Debug.WriteLine()`. In Debug mode; In Release mode, it does nothing. – Jimi Apr 03 '20 at 14:00
  • Oh, ok, then. Thanks, Jimi. My python scripts "print" a message on the console with the number of the percentage complete. I'll figure something out and when I do, I'll come back and edit the question here. Thank you very much!! – Leandro Manes Apr 03 '20 at 14:04
  • Hey @Jimi!! I solved. I took what you last said and made a unique little change to my python script. I just set the ```flush=True``` on the ```print``` statement and all worked. Thanks a lot! – Leandro Manes Apr 03 '20 at 15:09
  • `ProgressBar` cannot update when the UI thread is blocked. You've blocked the UI thread by calling `WaitForExit()` after starting the process. See marked duplicates for examples of how to approach this in a GUI-friendly way. – Peter Duniho Apr 03 '20 at 19:13

1 Answers1

2

So, after all the information exchange with Jimi, down in the comments, I solved the problem. I used all his suggestions and adjusted my c# code to this:

 private static double value = 0;
 private void button1_Click(object sender, EventArgs e)
        {
            string path = @"your path here";
            Process process = new Process();
            process.StartInfo.FileName = path;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.SynchronizingObject = this;
            process.EnableRaisingEvents = true;

            process.Exited += (s, evt) => {

                process?.Dispose();
            };
            process.OutputDataReceived += new DataReceivedEventHandler((_sender, _e) =>
            {

                if (!String.IsNullOrEmpty(_e.Data))
                {
                    value = double.Parse(_e.Data);
                    label1.Text = value.ToString() + "%";
                    progressBar1.Value = (int)value;

                }
            });

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

The code was doing what it was supposed to do, but I was not getting the results I wanted because my Python script was not giving any StandardOutput. So, I went back to python and made this adjustment to the printing lines:

print(percentage, flush=True)

And now life is good. Thank you, Jimi!

  • 1
    All right, well done :) You could add a little improvement, adding `RedirectStandardError = true` and `process.BeginErrorReadLine()`, to subscribe to the `ErrorDataReceived` event, so your script can inform the other app if/when something goes wrong and the c# app can take action, eventually, (or dismiss the Process or whatever else is deemed necessary). – Jimi Apr 03 '20 at 17:09