我在尝试创建带有可顺利更新的进度条的 powershell gui 时遇到问题

I'm having problems trying to create a powershell gui with a progress bar that updates smoothly

提问人:David FJ 提问时间:11/17/2023 更新时间:11/17/2023 访问量:31

问:

我知道这是一个被广泛讨论的问题,我花了一周的大部分时间在谷歌上搜索和尝试,但无济于事......现在请放轻松!我不是这个powershell云雀的佼佼者,但我一直在努力获得一个简单的GUI,允许用户根据收集到的一些数据进行交互和创建输出。我有点让它工作,但数据收集大约需要 15 分钟,所以我想要一个进度条,这就是一切出错的地方!我创建了 prog 栏,它只会在数据函数完成时更新。我意识到我需要尝试找出跑道:(几天后,我有一个运行runspaces的脚本,但我仍然无法让它:(更新进度条。我敢肯定,你们中的一个聪明人会立即拉出我的代码,并向我展示它做得有多糟糕......正如你们中的一些人可能认识到的那样,最终结果是基于 https://www.foxdeploy.com/blog/part-v-powershell-guis-responsive-apps-with-progress-bars.html。那是在多次迭代尝试其他人的解决方案无济于事之后。我试图大规模简化我的代码,只处理手头的直接问题 - 这就是我将在下面添加的内容。 因此,如果你们中的任何一个善良的人能清楚地向我指出我是多么的白痴,以及我哪里出了问题,我将不胜感激:)请记住,您可能需要慢慢呼吸并大写,因为我显然很难学习......好吧,我感觉就像这周在该死的跑道上撞墙之后!:) 提前感谢您:)的任何帮助 哦,我提前向那些会回复的人道歉 - “只是搜索 - 看这里是你在另一个链接中的答案”我试过了,但我仍然把头撞在墙上!:)

所以这里是 - 我的时髦代码 - 请善待!:)(一些证据仍然在那里徘徊,证明我在变量类型和一些弹出窗口上搞砸了,以确认进度等)

$

`global:NumOfDays = 0
$global:selectcb1 = "Y"
$global:selectcb2 = "Y"
$global:selectcb3 = "Y"
$global:outputArr = @()


#create synchash
$global:syncHash = [hashtable]::Synchronized(@{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"         
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$global:syncHash) 

Add-Type -AssemblyName PresentationCore, PresentationFramework
         
#create main process
$psCmd = [PowerShell]::Create().AddScript({   
    [xml]$xaml = @"
    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Name="Window" Title="Data Tool" WindowStartupLocation = "CenterScreen"
        Width = "900" Height = "600" ShowInTaskbar = "True">
        <Grid Margin="3,0,137,1">
            <CheckBox Name="CB1" IsChecked="True" HorizontalAlignment="Left" VerticalAlignment="Top" Content="CB 1" Margin="40,110,0,0"/>
            <CheckBox Name="CB2" IsChecked="True" HorizontalAlignment="Left" VerticalAlignment="Top" Content="CB 2" Margin="40,150,0,0"/>
            <CheckBox Name="CB3" IsChecked="True" HorizontalAlignment="Left" VerticalAlignment="Top" Content="CB 3" Margin="40,190,0,0"/>
            <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" TextWrapping="Wrap" Text="Explain stuff" Margin="47,30,0,0" Width="271" Height="67" FontWeight="SemiBold"/>

            <Button Name="Create" Content="Create Output" HorizontalAlignment="Left" VerticalAlignment="Top" Width="139" Margin="350,400,0,0" Height="22"/>
            <Button Name="Quit" Content="Quit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="600,500,0,0" Height="22"/>
            <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" TextWrapping="Wrap" Text="explain slider" Margin="200,320,0,0" Width="500" Height="67" />

            <DockPanel VerticalAlignment="Center" Margin="60,150,0,0">
                <TextBox Name="slValue" Text="{Binding ElementName=slider, Path=Value, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right" Width="40" />
                <Slider Name="slider" Minimum = "5" Maximum="365" TickPlacement="BottomRight" TickFrequency="5" IsSnapToTickEnabled="True" />
            </DockPanel>

            <ProgressBar Name="ProgBar" Maximum="100" HorizontalAlignment="Left" Height="19" VerticalAlignment="Top" Width="408" Margin="372,41,0,0"/>
            <TextBlock Text="{Binding ElementName=ProgBar, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="572,41,0,0" />
            <TextBlock Name="dataprogress" HorizontalAlignment="Left" VerticalAlignment="Top" Text="Progress - " Margin="498,15,0,0"/>

            </Grid>
        </Window>
"@
  
    $reader=(New-Object System.Xml.XmlNodeReader $xaml)
    $syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
    
    [xml]$XAML = $xaml
    $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
        #Find all of the form types and add them as members to the synchash
        $global:syncHash.Add($_.Name,$global:syncHash.Window.FindName($_.Name) )
    }


    # Create second process handler
    $global:JobCleanup = [hashtable]::Synchronized(@{})
    $global:Jobs = [system.collections.arraylist]::Synchronized((New-Object System.Collections.ArrayList))

    #region Background runspace to clean up jobs
    $jobCleanup.Flag = $True
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"
    $newRunspace.ThreadOptions = "ReuseThread"          
    $newRunspace.Open()        
    $newRunspace.SessionStateProxy.SetVariable("jobCleanup",$jobCleanup)     
    $newRunspace.SessionStateProxy.SetVariable("jobs",$jobs) 
    $jobCleanup.PowerShell = [PowerShell]::Create().AddScript({
        #Routine to handle completed runspaces
        Do {    
            Foreach($runspace in $jobs) {            
                If ($runspace.Runspace.isCompleted) {
                    [void]$runspace.powershell.EndInvoke($runspace.Runspace)
                    $runspace.powershell.dispose()
                    $runspace.Runspace = $null
                    $runspace.powershell = $null               
                } 
            }
            #Clean out unused runspace jobs
            $temphash = $jobs.clone()
            $temphash | Where {
                $_.runspace -eq $Null
            } | ForEach {
                $jobs.remove($_)
            }        
            Start-Sleep -Seconds 1     
        } while ($jobCleanup.Flag)
    })
    $jobCleanup.PowerShell.Runspace = $newRunspace
    $jobCleanup.Thread = $jobCleanup.PowerShell.BeginInvoke()  
    #endregion Background runspace to clean up jobs


    # controls ###################################

    $syncHash.Quit.add_click({
        #[System.Windows.MessageBox]::Show('Quit')
        $global:syncHash.Window.Close()
        $global:syncHash.Clear()
        $global:syncHash.Quit
        $newRunspace.Clear()
        $newRunspace.Dispose()
        #Exit
    })

    
    $global:syncHash.CB1.Add_Checked({
        $global:selectcb1 = "Y" 
        #[System.Windows.MessageBox]::Show($selectcb1)
    })
    $global:syncHash.CB1.Add_Unchecked({
        $global:selectcb1 = "N" 
        #[System.Windows.MessageBox]::Show($selectcb1)
    })
    $global:syncHash.CB2.Add_Checked({
        $global:selectcb2 = "Y" 
        #[System.Windows.MessageBox]::Show($selectcb2)
    })
    $global:syncHash.CB2.Add_Unchecked({
        $global:selectcb2 = "N" 
        #[System.Windows.MessageBox]::Show($selectcb2)
    })
    $global:syncHash.CB3.Add_Checked({
        $global:selectcb3 = "Y" 
        #[System.Windows.MessageBox]::Show($selectcb3)
    })
    $global:syncHash.CB3.Add_Unchecked({
        $global:selectcb3 = "N" 
        #[System.Windows.MessageBox]::Show($selectcb3)
    })

    $global:syncHash.slValue.add_TextChanged({
        $global:NumOfDays = $global:syncHash.slValue.Text
        #[System.Windows.MessageBox]::Show($NumOfDays)  
    })

    $global:syncHash.create.add_click({
        [System.Windows.MessageBox]::Show('create')
    })

    $global:syncHash.Window.Add_ContentRendered({
        [System.Windows.MessageBox]::Show('gather')

        $newRunspace =[runspacefactory]::CreateRunspace()
        $newRunspace.ApartmentState = "STA"
        $newRunspace.ThreadOptions = "ReuseThread"          
        $newRunspace.Open()
        $newRunspace.SessionStateProxy.SetVariable("SyncHash",$SyncHash) 
        $PowerShell = [PowerShell]::Create().AddScript({
        ###############################################
            Function Update-Window {
                Param (
                    $Control,
                    $Property,
                    $Value,
                    [switch]$AppendContent
                )

                # This is kind of a hack, there may be a better way to do this
                If ($Property -eq "Close") {
                    $syncHash.Window.Dispatcher.invoke([action]{$syncHash.Window.Close()},"Normal")
                    Return
                }

                # This updates the control based on the parameters passed to the function
                $syncHash.$Control.Dispatcher.Invoke([action]{
                # This bit is only really meaningful for the TextBox control, which might be useful for logging progress steps
                    If ($PSBoundParameters['AppendContent']) {
                            $syncHash.$Control.AppendText($Value)
                    } Else {
                       $syncHash.$Control.$Property = $Value
                    }
                }, "Normal")
            } 
        ###############################################
            [System.Windows.MessageBox]::Show('gathering')
            for ($i = 1 ; $i -le 100 ; $i++) {
                Start-Sleep -milliseconds 100
                #$global:SyncHash.Window.Dispatcher.Invoke([Action]{$global:SyncHash.ProgBar.Value = $i})
                update-window -Control ProgrBar -Property Value -Value $i                
            }
        })
        ###############################################
        $PowerShell.Runspace = $newRunspace
        [void]$Jobs.Add((
            [pscustomobject]@{
                PowerShell = $PowerShell
                Runspace = $PowerShell.BeginInvoke()
            }
        ))
    })

    #region Window Close 
    $syncHash.Window.Add_Closed({
        Write-Verbose 'Halt runspace cleanup job processing'
        $jobCleanup.Flag = $False

        #Stop all runspaces
        $jobCleanup.PowerShell.Dispose()      
        $PowerShell.Invoke()
        [System.Windows.MessageBox]::Show('done')
    })


    ##############################################

$global:syncHash.Window.ShowDialog() | Out-Null
$global:syncHash.Error = $Error
`
PowerShell XAML 进度条 运行空间

评论

0赞 mklement0 11/17/2023
这是大量的代码和一堵文字墙。如果你将代码缩减到一个最小的可重现示例,或者尽可能接近一个示例,并将你的叙述简化为一个集中的问题描述,那么你获得帮助的机会要大得多。Смотритетакже: 如何提出一个好问题

答: 暂无答案