【问题标题】:Powershell: Updating GUI from background job resultsPowershell:从后台作业结果更新 GUI
【发布时间】:2019-05-11 20:52:20
【问题描述】:

编辑:我能够让它工作,请参阅下面的解决方案。下面的评论是正确的,Powershell 并不是真正的 GUI 和线程的理想选择,但它可以做到。


我在 Powershell 中有一个表单,它使用 Start-Job 在后台运行功能而不会冻结 GUI。我的目标是不断检查这些作业的状态以了解它们的输出。我设法使用 Windows Forms Timer 来检查作业的结果并相应地更新 GUI。

一切正常,但看起来很草率。这是完成 GUI 刷新的最佳方式吗?我对 Powershell 比较陌生,我想改进我的编码。

我在做什么的例子:

$jobScript = {
    Start-Sleep 5
    Write-Output "The job has finished running"
}

$timerScript = {
    $timer.Stop()
    $jobResult = Get-Job | Receive-Job -Keep
    if ($jobResult) {
        $btn.text = $jobResult
    } else {
        $timer.Start()
    }
}

Add-Type -AssemblyName System.Windows.Forms
$form             = New-Object System.Windows.Forms.Form
$form.ClientSize  = '300,300'
$form.topmost     = $true

$btn              = New-Object System.Windows.Forms.Button
$btn.Text         = "The job is still running"
$btn.Width        = 300
$btn.Height       = 300
$form.Controls.Add($btn)

$timer            = New-Object System.Windows.Forms.Timer
$timer.Interval   = 100
$timer.add_Tick($timerScript)
$timer.Start()

Start-Job -ScriptBlock $jobScript

$form.ShowDialog()

更新:我的解决方案

使用Register-ObjectEvent 不起作用,似乎它正在与线程的GUI 发生冲突。相反,我能够使用[System.Windows.Forms.Application]::DoEvents()。这允许 GUI 移动,一旦移动完成,线程将恢复。这里需要注意的是,只要移动 GUI,执行就会暂停,因此如果您的代码需要在时间限制内对后台作业做出反应,这可能会导致错误。

示例代码块:

$jobScript =
{
    Start-Sleep 5
    Write-Output "The job is completed"
}

Add-Type -AssemblyName System.Windows.Forms
$form             = New-Object System.Windows.Forms.Form
$form.ClientSize  = '300,300'
$form.topmost     = $true

$btn              = New-Object System.Windows.Forms.Button
$btn.Text         = "..."
$btn.Width        = 300
$btn.Height       = 300
$form.Controls.Add($btn)

$btn.add_click({
    $btn.Text = "Starting job"
    $jobby = Start-Job -ScriptBlock $jobScript
    Do {[System.Windows.Forms.Application]::DoEvents()} Until ($jobby.State -eq "Completed")
    $btn.Text = Get-Job | Receive-Job
})

$form.ShowDialog()

【问题讨论】:

  • 显示进度的 PowerShell 方法是使用 Write-Progress。见Get-Help Write-Progress -examples
  • @KoryGill 对于 winform 应用程序则不然。
  • @KoryGill 感谢您的回复。我真正的问题是如何在后台作业返回结果后让表单等待和更新,无论是Write-Progress还是Write-Output
  • 在 Powershell 中制作一个响应式的 winforms GUI 真的很痛苦。我会考虑与用户互动的其他方式。

标签: forms powershell user-interface timer jobs


【解决方案1】:

您可能想在CodeReview.StackExchange.com 上发布此内容。

我有点讨厌人们在 Powershell 中构建 UI。如果你想要一个合适的 Windows 窗体应用程序,只需用 C# 编写它。所以我不同意这个前提的设计。

我喜欢你放弃投票设计的冲动;您启动作业,然后轮询以查看它是否已完成。我认为事件处理程序可能是更好的选择。查看文章PowerShell and Events: Object Events 中的“监控后台作业”部分。这是一个老歌,但很好。

【讨论】:

  • 非常感谢您的回复和链接。
  • 能否请您详细说明为什么您不喜欢 Powershell GUI?我认为这只是一种解决方法而不是预期用途?
  • 这是个好问题。我可以用我的自行车发电。这种方法可以让我保持健康,而且更环保。带有 Powershell 的 UI 有点像这样。我能做到。它使 UI 开发更易于访问,尤其是对于 Powershell 开发人员。但是,Powershell 并不是为此目的而设计的,并且在 GUI 领域没有获得大量的上场时间。出于这个原因,你越深入那个兔子洞,你就会发现小问题。如果您坚持使用专为 UI 开发人员设计的语言,您会遇到更少的障碍 - 恕我直言。对不起,我不得不保持简短;字符限制。
  • 很好的类比,再次感谢您的时间。我最终为我的目的找到了一个更好的解决方法(我将编辑我的 OP),但我肯定会开始花时间使用其他语言为未来做准备。
  • PowerShell 是 .NET。您可以在 C# 中做所有可以做的事情。你可以在 PoSh 中构建非常好的 UI(例如:bytecookie.wordpress.com/powershell-code-manager)。它比使用 c# 更简单,工作量也更少。
【解决方案2】:

你可以使用事件:

 $job = Start-Job {sleep 3; Write-Output "Dummy job completed"}

$callback = {

Write-Host "Event Fired"
Unregister-Event -SourceIdentifier "DummyJob"
Write-Host ($job | Receive-Job)

}

$evt = Register-ObjectEvent -InputObject $job -EventName StateChanged -SourceIdentifier "DummyJob" -Action $callback 


# to remove all 
Get-Job | Remove-Job -Force # jobs and events
Get-EventSubscriber | Unregister-Event  # events

【讨论】:

  • 谢谢!我将把它作为一种解决方法来研究,直到我开始用 C# 来做这件事,因为我一直听到我应该这样做。
  • 用这个做一些测试,Register-ObjectEvent 在使用 winforms 时会产生奇怪的结果。我做了一些谷歌搜索,似乎它与线程的形式相冲突。我将用我最终找到的解决方案更新我的 OP。
猜你喜欢
  • 2018-10-17
  • 1970-01-01
  • 2014-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多