英文:
Create a progress bar in a GUI window in Powershell
问题
我使用下面显示在我的PowerShell脚本中显示进度条的代码。它能够工作,但进度条是显示在PowerShell 控制台中。
然而,我想要创建一个在单独的窗口中以GUI方式显示的进度条,就像这个视频中展示的一样:链接。
有人可以提供一个示例吗?
这是我基于这个答案调整的基于控制台的进度条的代码:
Write-Progress -Activity $activity -PercentComplete 0
$totalMinutes = 0.15 # How long to display the progress bar for?
$timerIntervalSecs = 0.1 # The timer event triggering interval
$global:continueWhile = $true # Global variable to continue or stop the loading bar
$timer = [System.Timers.Timer]::new($timerIntervalSecs * 1000)
$eventJob = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
$endTime, $totalMinutes, $activity = $Event.MessageData.EndTime, $Event.MessageData.TotalMinutes, $Event.MessageData.Activity
$timeLeft = $endTime - (Get-Date)
$percent = (1 - $timeLeft.TotalSeconds / ($totalMinutes * 60)) * 100
if ($timeLeft -lt 0) { $percent = 100; $timeLeft = [timespan] 0; $global:continueWhile = $false } # Once fully elapsed, set $continueWhile = $false
Write-Progress -Activity $activity -Status "$([math]::Floor($percent))% complete, $([math]::Ceiling($timeLeft.TotalSeconds)) seconds remaining..." -PercentComplete $percent
} -MessageData (@{ EndTime = (Get-Date).AddMinutes($totalMinutes); TotalMinutes = $totalMinutes; Activity = $activity })
$timer.Start() # Start the timer.
try {
Write-Output "test output"
while ($continueWhile) {
Start-Sleep -Seconds 0.5
}
} finally {
$timer.Stop()
Remove-Job $eventJob -Force
Write-Progress -Activity $activity -Completed
}
英文:
I use the code shown below to display a progress bar in my PowerShell script. It works, but it progress bar is displayed it in the PowerShell console.
However, I would like to create a GUI progress bar in a separate window when I click somewhere, as shown in this video: link.
Can someone help please with an example?
Here is the code of my console-based progress bar, adapted from this answer:
Write-Progress -Activity $activity -PercentComplete 0
$totalMinutes = 0.15 # Combien de temps faut-il afficher la barre de progression ?
$timerIntervalSecs = 0.1 # L'intervalle de déclenchement des événements de la minuterie
$global:continueWhile = $true # Variable globale qui permet de continuer ou d'arrêter la barre de chargement
$timer = [System.Timers.Timer]::new($timerIntervalSecs * 1000)
$eventJob = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
$endTime, $totalMinutes, $activity = $Event.MessageData.EndTime, $Event.MessageData.TotalMinutes, $Event.MessageData.Activity
$timeLeft = $endTime - (Get-Date)
$percent = (1 - $timeLeft.TotalSeconds / ($totalMinutes * 60)) * 100
if ($timeLeft -lt 0) { $percent = 100; $timeLeft = [timespan] 0; $global:continueWhile = $false } # une fois entièrement écoulée, passe $continueWhile = $false
Write-Progress -Activity $activity -Status "$([math]::Floor($percent))% complete, $([math]::Ceiling($timeLeft.TotalSeconds)) seconds remaining..." -PercentComplete $percent
} -MessageData (@{ EndTime = (Get-Date).AddMinutes($totalMinutes); TotalMinutes = $totalMinutes; Activity = $activity })
$timer.Start() # Démarre la minuterie.
try {
Write-Output "test output"
while ($continueWhile) {
Start-Sleep -Seconds 0.5
}
} finally {
$timer.Stop()
Remove-Job $eventJob -Force
Write-Progress -Activity $activity -Completed
}
答案1
得分: 2
这段代码描述了如何创建一个Windows窗体应用程序,其中包含一个进度条,用于显示操作的进度。以下是其中文翻译:
这是您基于控制台的进度条的GUI等效物,使用WinForms ProgressBar控件。
注意:
* 不再使用全局变量将值从事件脚本块传递给调用方,而是将条目添加到传递给-MessageData参数的哈希表中,该参数现在充当双向数据传输对象(DTO)。
* 窗体可以手动关闭,此时将向调用方传达已达到的百分比。
* 具有进度条的窗体将显示在最上层,即使切换到其他应用程序也会保持可见。如果不需要这样的行为,请删除TopMost = $true的哈希表条目。
* 您的方法有点不同寻常,因为您允许固定的时间驱动进度显示,而不是在主线程中执行实际任务。但是,您可以通过DTO报告实际进度,但请注意基本限制(同样适用于您的基于控制台的进度条):
* 您可以在脚本的主要部分的while循环中执行的操作必须具有短的运行时间,因为定时器事件仅在命令之间触发。例如,如果循环中的命令运行2分钟,那么在此时间内将阻止更新进度条。
这部分是代码,无需翻译。
英文:
<!-- language-all: sh -->
Here's the GUI equivalent of your console-based progress bar, using the WinForms ProgressBar
control.
Note:
-
Instead of using a global variable to communicate a value to the caller from the event script block, I've added entries to the hashtable that is passed to the
-MessageData
parameter, which now acts as a bidirectional DTO (data-transfer object). -
The form can be closed manually, in which case the percentage reached is communicated to the caller.
-
The form with the progress bar will appear topmost, i.e. it will stay visible even if you switch to other applications. Remove the
TopMost = $true
hashtable entry, if that is undesired. -
Your approach is somewhat unusual in that you're letting a fixed amount of time drive the progress display rather than an actual task being performed in the main thread. However, you could adapt it to report actual progress via the DTO instead, but note the fundamental limitation (which equally applies to your console-based progress bar):
- The operation(s) you can perform in the
while
loop in the main part of your script - whereStart-Sleep -Seconds 0.5
is currently used as a stand-in for such operations - must have a short runtime, because the timer event only fires between commands. For instance, if a command in that loop runs for 2 minutes, updates to the progress bar are blocked during that time.
- The operation(s) you can perform in the
Add-Type -AssemblyName System.Windows.Forms
# Parameters
$activity = 'Running...'
$totalMinutes = 0.15 # How many minutes to run in total
$timerIntervalSecs = 0.1 # The timer-event firing interval
$timer = [System.Timers.Timer]::new($timerIntervalSecs * 1000) # create the timer
# A data-transfer object used for communicating with
# the -Action script block below.
$dto = @{
Activity = $activity
EndTime = (Get-Date).AddMinutes($totalMinutes)
TotalMinutes = $totalMinutes
Done = $false # will be set to $true when the progress bar has reached 100% or the form was closed manually.
AbortedAt = $null # if the form was closed manually, will be set to the percent-complete value at that time.
Form = $null # will be filled in the -Action script block to store the form object between invocations.
}
# Register a script block as the delegate for the timer's "Elapsed" event
# (-Action parameter).
# The data-transfer object is passed via the -MessageData parameter.
$eventJob = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
$endTime, $totalMinutes, $activity, $form = $Event.MessageData.EndTime, $Event.MessageData.TotalMinutes, $Event.MessageData.Activity, $Event.MessageData.Form
# Create the form on demand.
if (-not $form) {
$form = $Event.MessageData.Form = [System.Windows.Forms.Form] @{ TopMost = $true; Text = $activity; MinimizeBox = $false; MaximizeBox = $false; Width = 290; Height = 100; StartPosition = 'CenterScreen' }
$form.Controls.AddRange(@(
[System.Windows.Forms.Label] @{ Name = 'lbl'; Left = 10; Width = 250 }
[System.Windows.Forms.ProgressBar] @{ Name = 'pb'; Minimum = 0; Maximum = 100; Top = 30; Left = 10; Width = 250 }
))
$form.Show()
}
# Calculate the progress...
$timeLeft = $endTime - (Get-Date)
if ($timeLeft -lt 0) { $timeLeft = [timespan] 0 }
$completed = $timeLeft -eq 0
$percent = (1 - $timeLeft.TotalSeconds / ($totalMinutes * 60)) * 100
# ... and update the status label and progress bar
# Write-Progress -Activity $activity -Status "$([math]::Floor($percent))% complete, $([math]::Ceiling($timeLeft.TotalSeconds)) seconds remaining..." -PercentComplete $percent
$form.Controls['lbl'].Text = "$([math]::Floor($percent))% complete, $([math]::Ceiling($timeLeft.TotalSeconds)) seconds remaining..."
$form.Controls['pb'].Value = $percent
# NOTE: This is crucial to make the form paint and to to keep it responsive.
# If the timer interval is too long, the form will be sluggish to respond to attempts to move or close it.
[System.Windows.Forms.Application]::DoEvents()
# If the time has elapsed or the form has been closed, close the form (if it isn't already closed),
# and signal that fact to the calling thread.
if ($completed -or -not $form.Visible) { $Event.MessageData.AbortedAt = if (-not $form.Visible) { $percent }; $form.Dispose(); $Event.MessageData.Done = $true }
} -MessageData $dto
# Start the timer, which displays the form with the progress bar.
$timer.Start()
try {
"Test output"
# Wait until the timer has elapsed or the form has been closed.
while (-not $dto.Done) {
Start-Sleep -Seconds 0.5
}
if ($dto.AbortedAt) {
Write-Warning "Progress-bar window as manually closed at $($dto.AbortedAt.ToString('N2'))% complete."
} else {
"Progress-bar window auto-closed on completion."
}
} finally {
# Clean up.
$timer.Stop()
Remove-Job $eventJob -Force
}
答案2
得分: 0
添加一个基于表单的进度条:
Add-Type -AssemblyName System.Windows.Forms
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "测试"
$objForm.Size = New-Object System.Drawing.Size(400,200)
$objForm.FormBorderStyle = 'Fixed3D'
$objForm.MaximizeBox = $false
$objForm.MinimizeBox = $false
$objForm.StartPosition = "CenterScreen"
$amount = 1500
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Minimum = 0
$ProgressBar.Maximum = $amount
$ProgressBar.Location = New-Object System.Drawing.Size(10,80)
$ProgressBar.Size = New-Object System.Drawing.Size(300,20)
$objForm.Controls.Add($ProgressBar)
$i = 1
# 显示进度
while ($i -le $amount) {
$ProgressBar.Value = $i
$i++
}
for ($i = 1; $i -le $amount; $i++) {}
$objForm.ShowDialog() | Out-Null
$objForm.Dispose()
英文:
Add a forms-based progress bar:
Add-Type -AssemblyName System.Windows.Forms
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Test"
$objForm.Size = New-Object System.Drawing.Size(400,200)
$objForm.FormBorderStyle = 'Fixed3D'
$objForm.MaximizeBox = $false
$objForm.MinimizeBox = $false
$objForm.StartPosition = "CenterScreen"
$amount = 1500
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Minimum = 0
$ProgressBar.Maximum = $amount
$ProgressBar.Location = new-object System.Drawing.Size(10,80)
$ProgressBar.size = new-object System.Drawing.Size(300,20)
$objForm.Controls.Add($ProgressBar)
$i=1
#Display progress
while($i -le $amount){
$ProgressBar.Value = $i
$i++
}
for($i = 1; $i -le $amount;$i++){}
$objForm.ShowDialog() | Out-Null
$objForm.Dispose()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论