ProgressBar показывает на ISE, но не на консоли.Отзывчивый графический интерфейс Powershell в Runspace - PullRequest
0 голосов
/ 05 октября 2018

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

Я проверил различные методы для связи пространств выполнения.Сначала я попробовал Control.Invoke, но некоторые мнения на разных форумах и странное поведение на тестах заставили меня использовать основанную на сообщениях внутреннюю коммуникацию на основе синхронизированной HashTable.ProgressBar работает с control.Invoke, но выполнение некоторых других действий, таких как отключение нескольких кнопок в форме, выполняется очень медленно.

1-я проблема: я хотел бы показать индикатор выполнения (выделение), когда задача выполняется, изменяя ее видимое состояние.Однако индикатор выполнения отображается, когда скрипт работает на ISE, а не когда он работает на консоли.Я думаю, это потому, что основное пространство выполнения занято, но я не понимаю, как это может повлиять на пространство выполнения GUI ...

2-й: в скрипте, который я публикую ниже, я использую блоки скриптов через переменную стекапередавать команды между пространствами выполнения.Затем каждое пространство выполнения (основное пространство выполнения делает это с помощью пула, а GUI - через таймер) проверяет, ожидает ли задача выполнения в стеке, и, если это так, выполняет ее.Однако, если бы я захотел вызвать функцию, объявленную в другом пространстве выполнения (в этом примере Test-OutOfMainScopeUiFunction), я бы не смог.Я получил бы ошибку во время выполнения на блоке сценария.Я думал, что решения для этого, как: -Импортировать функции в обоих пространствах выполнения -Сделать функции глобальными или использовать делегаты functon¿?-Передача строки с командами для выполнения, несмотря на блок скрипта -> Использование этой в данный момент, но не очень нравится ... склонность к ошибкам

Любое решение проблемы с индикатором выполнения или сценарияулучшения будут оценены.Спасибо!

$global:x = [Hashtable]::Synchronized(@{})
$global:x.MainDispatcher = new-object system.collections.stack
$global:x.UiDispatcher = new-object system.collections.stack
$global:x.GuiExited = $false

$formSB = { 

  [reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
  [reflection.assembly]::LoadWithPartialName("System.Drawing")

  Function Test-OutOfMainScopeUiFunction 
  {
    $x.Host.Ui.WriteLine("Test-OutOfMainScopeUiFunction Executed")
  }
  Function Execute-OnMainRs
  {
    param(
      [Parameter(Mandatory=$true)]
      [ScriptBlock]$ScriptBlock
    )

    $x.Host.Ui.WriteLine("`r`nAdding task")

    $x.MainDispatcher.Push($ScriptBlock)

    $x.Host.Ui.WriteLine("Task added")
    $x.Host.Ui.WriteLine("Task: $($x.MainDispatcher)")
  }

  $form = New-Object System.Windows.Forms.Form

  $button = New-Object System.Windows.Forms.Button
  $button.Text = 'click'
  $button.Dock = [System.Windows.Forms.DockStyle]::Right

  $progressBar = (New-Object -TypeName System.Windows.Forms.ProgressBar)
  $ProgressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
  $ProgressBar.MarqueeAnimationSpeed = 50
  $ProgressBar.Dock = [System.Windows.Forms.DockStyle]::Bottom
  $ProgressBar.Visible = $false  

  $label = New-Object System.Windows.Forms.Label
  $label.Text = 'ready'
  $label.Dock = [System.Windows.Forms.DockStyle]::Top

  $timer=New-Object System.Windows.Forms.Timer
  $timer.Interval=100

  $timer.add_Tick({
      if($x.UiDispatcher.Count -gt 0)
      {
        & $($x.UiDispatcher.Pop())
      }
  })

  $form.Controls.add($label)
  $form.Controls.add($button)
  $form.Controls.add($progressBar)

  Add-Member -InputObject $form -Name Label -Value $label -MemberType NoteProperty
  Add-Member -InputObject $form -Name ProgressBar -Value $progressBar -MemberType NoteProperty

  $button.add_click({ 
      Execute-OnMainRs -ScriptBlock {
          write-host "MainRS: Long Task pushed from the UI started on: $(Get-Date)"
          start-sleep -s 2
          write-host "MainRS: Long Task pushed from the UI finished on: $(Get-Date)"
        }
  })

  $form.add_closed({ $x.GuiExited = $true })

  $x.Form = $form

  $timer.Start()

  $form.ShowDialog()

}

Function Execute-OnRs
{ 
  param(
    [Parameter(Mandatory=$true)]
    [ScriptBlock]$ScriptBlock
  )

  $x.Host = $Host

  $rs = [RunspaceFactory]::CreateRunspace()
  $rs.ApartmentState,$rs.ThreadOptions = "STA","ReUseThread"
  $rs.Open()
  $rs.SessionStateProxy.SetVariable("x",$x)

  $ps = [PowerShell]::Create().AddScript($ScriptBlock)

  $ps.Runspace = $rs
  $handle = $ps.BeginInvoke()

  #Almacenar variables del RS
  $x.Handle = $handle
  $x.Ps = $ps
}
Function Execute-OnUiRs
{
  param(
    [Parameter(Mandatory=$true)]
    [ScriptBlock]$ScriptBlock
  )

  $x.UiDispatcher.Push($ScriptBlock)
}

Function Dispatch-PendingJobs
{ 
  while($global:x.GuiExited -eq $false) { 
    if($global:x.MainDispatcher.Count -gt 0)
    {
      Execute-OnUiRs -ScriptBlock {
        $msg = "UIRS: MainThread informs: Long Task started on $(Get-Date)."
        $global:x.Form.Label.Text = $msg
        $global:x.Form.ProgressBar.Visible = $true
        $x.host.ui.WriteLine($msg)    
        #Next line throws an error visible on UI runspace error stream
        Test-OutOfMainScopeUiFunction
      }

      & $($global:x.MainDispatcher.Pop())

      Execute-OnUiRs -ScriptBlock {
        $msg = "UIRS: MainThread informs: Long Task finished on $(Get-Date)."
        $global:x.Form.Label.Text = $msg
        $global:x.Form.ProgressBar.Visible = $false
        $x.host.ui.WriteLine($msg)
      }

      write-host "UI Streams: $($global:x.Ps.Streams |out-string)"
    }
    else
    {
      start-sleep -m 100
    }
  }
}

1 Ответ

0 голосов
/ 08 октября 2018

Нашли решение ... http://community.idera.com/powershell/powertips/b/tips/posts/enabling-visual-styles

Сначала необходимо активировать VisualStyles.Проблема не связана с вещами на пространствах выполнения.Это краткий и более понятный пример кода, взятый из Индикатор прогресса Power Shell не работает с исправлением.

Add-Type -AssemblyName System.Windows.Forms

[System.Windows.Forms.Application]::EnableVisualStyles()

$window = New-Object Windows.Forms.Form
$window.Size = New-Object Drawing.Size @(400,75)
$window.StartPosition = "CenterScreen"
$window.Font = New-Object System.Drawing.Font("Calibri",11,[System.Drawing.FontStyle]::Bold)
$window.Text = "STARTING UP"

$ProgressBar1 = New-Object System.Windows.Forms.ProgressBar
$ProgressBar1.Location = New-Object System.Drawing.Point(10, 10)
$ProgressBar1.Size = New-Object System.Drawing.Size(365, 20)
$ProgressBar1.Style = "Marquee"
$ProgressBar1.MarqueeAnimationSpeed = 20
$window.Controls.Add($ProgressBar1)

$window.ShowDialog()
...