1

I need a WinForm act as the IronPython host, and during the IronPython run-time, the script can update UI to report its progress. I have the following code, and it does update the UI, but the problem is that the Window is not responsive. I appreciate any help.

namespace TryAsyncReport
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //use python
        private async void button1_Click(object sender, EventArgs e)
        {
            IProgress<ProgressReportInfo> progress = new Progress<ProgressReportInfo>(ReportProgress);
            await Task.Run(() =>
            {
                ScriptEngine engine = Python.CreateEngine();
                ScriptScope scope = engine.CreateScope();
                string script = "for i in range(1000000):\r\n";
                script += "   progress.Report(ProgressReportInfo(i / 10000, str(i / 10000)));\r\n";
                scope.SetVariable("progress", progress);
                scope.SetVariable("ProgressReportInfo", typeof(ProgressReportInfo));
                var code = engine.CreateScriptSourceFromString(script);
                code.Execute(scope);

            });

        }

        private void ReportProgress(ProgressReportInfo info)
        {
            progressBar1.Value = info.Percentage; 
            label1.Text = info.Status; 
        }

    }

    public class ProgressReportInfo
    {
        public ProgressReportInfo(int percentage, string status)
        {
            Percentage = percentage;
            Status = status;
        }
        public int Percentage { set; get; }
        public string Status { set; get; }
    }

}
ohlmar
  • 958
  • 8
  • 17
AndyDing
  • 23
  • 4

1 Answers1

0

You have to Invoke to UI thread:

    private void ReportProgress(ProgressReportInfo info)
    {
        // or better BeginInvoke
        Invoke(() =>
        {
            progressBar1.Value = info.Percentage;
            label1.Text = info.Status;
        });
    }

and you don't need to await for Task.Run.

Also, consider do not report every bit of progress change, but let say every 1000 changes.

Other solution is to use polling watcher (your script change value of volatile variable, this value is checked in the timer)

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Thank you Sinatr. However the GUI is still not responsive when Python is running. – AndyDing Feb 19 '14 at 15:11
  • Try running long python script in separate thread without passing/calling anything from/into. This shouldn't block UI. If it does, then something is wrong with python `Execute`. Perhaps there is a property to (guess) run with blocking/not blocking of calling process? – Sinatr Feb 19 '14 at 15:35
  • The problem is induced since Python updates the UI. If the the "progress.Report(...)" is replaced by "i++", the issue is gone. As your suggested, I looked around for possible property for "Execute", but no result. Actually I also tried a BackgroundWork, it shows the same behavior. – AndyDing Feb 20 '14 at 13:45
  • How do you update UI from within python code? I see now (lol), you are not calling `ReportProgress` there, but calling `Report` method. Show please `Report` method code, the problem is there. – Sinatr Feb 20 '14 at 13:54
  • progress.Report actually calls this function "private void ReportProgress(ProgressReportInfo info)" – AndyDing Feb 20 '14 at 14:52
  • Then I correctly suggested you to use `Invoke` in `ReportProgress()`. Though I am not sure now, but simply use `BeginInvoke` (maybe you call `Invoke` way too often, which should not completely block UI, but may feels this way). – Sinatr Feb 20 '14 at 15:00
  • I retried your advice "Also, consider do not report every bit of progress change, but let say every 1000 changes.". And it's true, if I reduce the frequency, the UI is not blocked. So I should ask my users, Python script writers, not to report back the progress too often. And actually doesn't need Invoke or BeginInvoke. Thank you so much! – AndyDing Feb 20 '14 at 15:26