Как WinForm может разместить долгосрочный сценарий IronPython и обновить пользовательский интерфейс для изменения прогресса

Мне нужно, чтобы WinForm выступал в качестве хоста IronPython, и во время выполнения IronPython скрипт может обновлять пользовательский интерфейс, чтобы сообщать о своем прогрессе. У меня есть следующий код, и он обновляет интерфейс, но проблема в том, что окно не реагирует. Я ценю любую помощь.

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

}

1 ответ

Решение

Ты должен Invoke в поток пользовательского интерфейса:

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

и вам не нужно await за Task.Run,

Кроме того, учтите, что не следует сообщать о всех изменениях прогресса, но давайте говорить, что каждые 1000 изменений

Другим решением является использование наблюдателя опроса (ваш скрипт меняет значение volatile переменная, это значение проверяется в таймере)

Другие вопросы по тегам