PowerShellでGUI: 電卓を作ってみました
これまで色々な言語や環境を勉強した際に、練習台として電卓を試作したので、PowerShell+WPFでも電卓を作ってみました。なお、下記は別の言語/環境での電卓の記事です。合わせてお読みいただければ幸いです。
他の言語との比較
GUI表現であるWPF以外は、Javascript版をかなり流用できました。
Javascriptとの違いは下記程度です。
・関数内にスコープが閉じた変数は、変数名の先頭に「$」を付加
・値が設定されるグローバル変数は、変数名の先頭に「$global:」を付加。
・比較演算子の書換( "=="→"-eq"、"!="→"-ne" 等)。
・ライブラリ呼出しの書換("Math.abs"→"[Math]::Abs"等)。
・switch構文をPowershellの構文に書換(ソースコード参照)。
ソースコード
- 実行方法もソースコードに書かれていますので、参照ください。
#■プログラムの説明 #・WPFを使った電卓です。 #■実行方法 #・本ファイルがあるフォルダでコマンドプロンプトを開き下記を実行 # powershell -sta -file WpfPocketCalc.ps1 #■画面基本操作 #・非常に基本的な電卓と同じ操作方法です。 #・ただし、計算結果は全て整数です。 #・また、BSは最後に入力した数字を1文字削除します。 #■バージョン等 #・プログラム名:WpfPocketCalc.ps1 #・バージョン :V1.0.0 #・作成日 :2017/04/05 #・最終更新日 :2017/04/05 #・作成者 :Itsukara (Takayoshi Iitsuka)、iitt21-t@yahoo.co.jp、http://itsukara.hateblo.jp $ErrorActionPreference = "stop" Set-PSDebug -Strict try { Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,System.Windows.Forms } catch { throw "Failed to load Windows Presentation Framework assemblies." } # For each name in <xxx x:Name="name">, create new variable and set value Function setVaviables ($xaml, $form) { $xaml.SelectNodes("//*") | ? {$_.Attributes["x:Name"] -ne $null} | % { New-Variable -Name $_.Name -Value $form.FindName($_.Name) -Force -Scope Global } } # xaml設定 [xml]$xaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WPF Pocket Calc presented by Itsukara" Height="300" Width="300"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="40"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <TextBox x:Name="displayLine" Grid.ColumnSpan="4" TextWrapping="Wrap" Text="0" FontSize="26" TextAlignment="Right" Margin="10,0"/> <Button x:Name="buttonBS" Content="BS" Grid.Column="0" Grid.Row="1" FontSize="30"/> <Button x:Name="buttonC" Content="C" Grid.Column="1" Grid.Row="1" FontSize="30"/> <Button x:Name="buttonAC" Content="AC" Grid.Column="2" Grid.Row="1" FontSize="30"/> <Button x:Name="buttonAdd" Content="+" Grid.Column="3" Grid.Row="1" FontSize="30" FontWeight="Bold"/> <Button x:Name="button7" Content="7" Grid.Column="0" Grid.Row="2" FontSize="30"/> <Button x:Name="button8" Content="8" Grid.Column="1" Grid.Row="2" FontSize="30"/> <Button x:Name="button9" Content="9" Grid.Column="2" Grid.Row="2" FontSize="30"/> <Button x:Name="buttonSub" Content="-" Grid.Column="3" Grid.Row="2" FontSize="30" FontWeight="Bold"/> <Button x:Name="button4" Content="4" Grid.Column="0" Grid.Row="3" FontSize="30"/> <Button x:Name="button5" Content="5" Grid.Column="1" Grid.Row="3" FontSize="30"/> <Button x:Name="button6" Content="6" Grid.Column="2" Grid.Row="3" FontSize="30"/> <Button x:Name="buttonMul" Content="×" Grid.Column="3" Grid.Row="3" FontSize="30" FontWeight="Bold"/> <Button x:Name="button1" Content="1" Grid.Column="0" Grid.Row="4" FontSize="30"/> <Button x:Name="button2" Content="2" Grid.Column="1" Grid.Row="4" FontSize="30"/> <Button x:Name="button3" Content="3" Grid.Column="2" Grid.Row="4" FontSize="30"/> <Button x:Name="buttonDiv" Content="/" Grid.Column="3" Grid.Row="4" FontSize="20" FontWeight="Bold"/> <Button x:Name="button0" Content="0" Grid.Column="0" Grid.Row="5" FontSize="30"/> <Button x:Name="buttonEql" Content="=" Grid.Column="1" Grid.Row="5" FontSize="30" FontWeight="Bold"/> <Button x:Name="buttonSgn" Content="±" Grid.Column="2" Grid.Row="5" FontSize="30" FontWeight="Bold"/> <Button x:Name="buttonMod" Content="%" Grid.Column="3" Grid.Row="5" FontSize="30" FontWeight="Bold"/> </Grid> </Window> '@ # 変数初期化 $maxKeta = 15; $maxDouble = 999999999999999.0; $OVERFLOW = "OVERFLOW (Push AC)"; $ZERODIV = "DIV BY 0 (Push AC)"; $operand1 = ""; $operator = ""; $firstNum = $true; # form作成 $form = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml)) # $displayLineの値として、form中のdisplayLine Nodeを設定 $displayLine = $form.FindName("displayLine") # callback設定 ## Function for button action Function button_action($buttonStr) { $display = $displayLine.Text if ($display -eq $OVERFLOW -or $display -eq $ZERODIV) { if ($buttonStr -ne "AC") { return } } switch -regex ($buttonStr) { "0|1|2|3|4|5|6|7|8|9" { if ($global:firstNum) { if ($global:operator -ne "") { $global:operand1 = $display } $display = "0" $global:firstNum = $false } if ($display.Length -ge $maxKeta) { $displayLine.Text = $OVERFLOW } else { $displayNum = +$display $buttonNum = +$buttonStr $displayNum = $displayNum * 10 + $buttonNum $displayLine.Text = $displayNum } } "+|-|×|/|=|%" { if ($global:operator -ne "" -and $global:operand1 -ne "" -and !$global:firstNum) { $operand1Num = +$global:operand1 $operand2Num = +$display switch ($global:operator) { "+" { if ([Math]::Abs($operand1Num + $operand2Num) -gt $maxDouble) { $displayLine.Text = $OVERFLOW } else { $displayLine.Text = $operand1Num + $operand2Num } } "-" { if ([Math]::Abs($operand1Num - $operand2Num) -gt $maxDouble) { $displayLine.Text = $OVERFLOW } else { $displayLine.Text = $operand1Num - $operand2Num } } "×" { if ([Math]::Abs($operand1Num * $operand2Num) -gt $maxDouble) { $displayLine.Text = $OVERFLOW } else { $displayLine.Text = $operand1Num * $operand2Num } } "/" { if ($operand2Num -eq 0) { $displayLine.Text = $ZERODIV } else { $displayLine.Text = [Math]::Floor($operand1Num / $operand2Num) } } "%" { if ($operand2Num -eq 0) { $displayLine.Text = $ZERODIV } else { $displayLine.Text = $operand1Num % $operand2Num } } } } if ($buttonStr -eq "=") { $global:operator = "" } else { $global:operator = $buttonStr } $global:firstNum = $true } "BS" { $displayNum = +$display $displayNum = [Math]::Floor($displayNum / 10) $displayLine.Text = $displayNum } "±" { $displayNum = +$display $displayNum = -$displayNum $displayLine.Text = $displayNum } "C" { $displayLine.Text = "0" } "AC" { $displayLine.Text = "0" $global:operand1 = "" $global:operator = "" } } } ## Function to add Click callback Function button_add_Click($button) { $btn = $form.FindName($button.Name) if ($btn.add_Click -ne $null) { $btn.add_Click({button_action $this.content}) } } ## Add_Click to all button $xaml.SelectNodes("//*") | ? {$_.Name -clike "button*"} | % {button_add_Click $_} # 全体 $result = $form.ShowDialog()
github
上記ソースコードは、Wpfzooのgithubに加えておきました。
github.com
PowerShellでGUI:WPFの部品をほぼ全て含むデモ公開
PowerShellとWPFでGUIを表示できることが分かり、Visual Studioを使うとWPFのGUIを簡単にプレビュー/設計できることが分かったので、WPFの全ての部品(コントロール)を試してみようと思い、デモプログラムを作成しました。下記がそのスクリーンショットです。
WpfZoo.ps (WPF部品の動物園)
- WPFの色々なコントロールを表示するデモプログラムです。
- 表示だけでなくクリックしたボタンの表示(画面下部に表示)等も行います。
- なお、下記はスクリーンショットなのでクリックできません、プログラムをgithub(最後の方に記載)からダウンロードし、実行して試してください。
TabItem1 (最初の画面、色々な部品を配置)
- ボタン、リスト、テーブル等をクリックしてみてください。
TabItem2 (色々な部品を配置)
- カレンダー、Expanderや、スライダーを試してみてください。
Tab for DocPanel (DocPanelのデモ)
- MenuItemや、画面左のTreeViewで項目を選択してみてください。
Frames (Frameのデモ)
- 画面上のボタンをクリックしてFrameを表示してみてください。
Media (MediaElementのデモ)
- 動画の再生、一時停止、再生ができます。
RichTextBox (RichTextBoxのデモ)
- 表示内容の書換、文字属性や文節属性の変更ができます。
- ただし「下付き」「上付き」は、数字以外は上手く行きません。(原因不明)
画面上の各WPF部品に対応するxamlの確認方法
- Visual StudioでWPFプロジェクトを新規作成(こことここを参照)
- Visual Studioの画面下部に表示されたxaml文字列の<Grid>と</Grid>で囲まれた部分を、プログラム内のxamlの<Grid>と</Grid>で囲まれた部分で置換
- 事前にxaml中の文字列PSScriptRootを、プログラムを置いたパスで置換
- これにより、プログラムと同様の画面がVisual Studioで表示される
- Visual Studioの画面上のWPF部品をクリックして選択すると、画面下部のxaml中で対応する行が選択されるので、WPF部品とxaml文字列の対応が分かる
WpfFrame*.ps (Frameのちゃんとしたデモ)
上記のうち、Frameは、単に表示するだけで、表示したFrameでのアクションは何もないものでした。これでは使えないので、Frameのデモも作成しました。下記がその画面です。あまり複雑な画面に見えませんが、分からないことが多く、結構苦労しました。
最初の画面
- 表示された画面左上の「スタート」をクリック
Frame内にPage1を表示
- 名前等を入力後、「次へ」をクリック
Frame内にPage2を表示
- 好きなフルーツを選択後、「次へ」をクリック
Frame内にPage3を表示
- 「上記内容を確認しました」をチェックし、「登録」をクリック
- 未チェックではエラーが出て、チェックするまで登録できない
Frame内にPage4を表示
- この後、更に「スタート」をクリックすると次の登録画面になる
- 補足情報
- 登録情報の履歴が、画面下に表示される(この欄はスクロール可能)
- 画面左上の「←」と「→」によって、画面を戻ったり、進めたりできる
- この際に、既に入力した情報は保存される
あとがき
参考になる情報もあり、WpfZooは結構簡単に作れたのですが、Frameは適切な例がなく、かなり苦労しました。しかし、苦労の過程で、Visual StudioでのWPF画面の確認方法や設計のコツ、PowerShell ISEでのデバッグ方法なども習得することができました。
PowerShellでGUI: 部品配置はGridとテキスト編集が良い
2つ前の記事「PowerShellでGUI: 可変個数RadioButton実現方法(別法) - Itsukaraの日記」で、PowerShellのGUIをVisual Studioで作成する方法を書きました。ただ、GUIを実現するWPFの勉強が不足していたので、GUI画面上にGUI部品を適当に貼り付けていました。そのため、部品の位置等を表す数字「Margin="68,119,0,0"」が沢山あり、後で調整するのが大変そうと感じました。
その後、WPFを少し調べたところ、GRIDを使うことにより部品をテーブル状に整然と並べることが出来ると分かりました。下記のような感じです。
ただ、GRIDへのGUI部品配置をGUIでやると、微調整が結構面倒なので、1~2個部品を並べたら、あとは、XAMLテキストをコピー・ペーストし適切な修正を行うのが良いです。
また、GUIで部品を配置すると、HeightやMarginなどに、色々な数字が入りますが、これらが部品ごとに入っていると、後で纏めて変えるのが面倒なので、それらの数字はできるだけ削っておいた方が良いです。順序としては、上記のコピー・ペーストの前にやっておく必要がありますね。
なお、Syleを使うと、後でTextBoxのHeightを纏めて30にしたりといったことが、簡単にできます。Styleは、<Window.Resources>というタグ内に記述します。
結果として、次のようなプログラムになりました。表示内容は、依然とあまり変わりませんが、無駄な数字がなくなって、すっきりしています。その分、後で変更するのが楽(なはず)です。
- プログラム例-
$ErrorActionPreference = "stop" Set-PSDebug -Strict Add-Type -AssemblyName PresentationFramework [xml]$xaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <Style TargetType="TextBox"> <Setter Property="Height" Value="30"/> </Style> <Style TargetType="Button"> <Setter Property="Height" Value="30"/> </Style> <Style TargetType="TextBlock"> <Setter Property="Foreground" Value="Blue"/> </Style> </Window.Resources> <Grid Margin="10,10,10,10"> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="10"/> <RowDefinition Height="30"/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="50"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label x:Name="label01" Content="名前" Margin="0" VerticalAlignment="Center" HorizontalAlignment="Right"/> <TextBox x:Name="textName" Margin="0" TextWrapping="Wrap" VerticalAlignment="Center" Grid.Column="1"/> <Label x:Name="label02" Content="ふりがな" Margin="0" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="1"/> <TextBox x:Name="textFurigana" Margin="0" TextWrapping="Wrap" VerticalAlignment="Center" Grid.Column="1" Grid.Row="1" /> <Label x:Name="label03" Content="性別" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Row="2"/> <StackPanel x:Name="stackGender" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Center" Orientation="Horizontal" Grid.Column="1" Grid.Row="2" Width="120"> <RadioButton x:Name="radioButtonMale" Content="Male" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,10,0"/> <RadioButton x:Name="radioButtonFemale" Content="Female" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0"/> </StackPanel> <Label x:Name="label04" Content="国籍" Margin="0" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="3"/> <ComboBox x:Name="comboBoxCountry" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Center" Grid.Row="3" Grid.Column="1" Width="100"/> <Label x:Name="label05" Content="年齢" Margin="0" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="4"/> <TextBox x:Name="textAge" Margin="0" TextWrapping="Wrap" VerticalAlignment="Center" Grid.Column="1" Grid.Row="4" Width="50" HorizontalAlignment="Left" /> <Button x:Name="buttonRegist" Content="登録" Margin="0" VerticalAlignment="Center" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="6" /> <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="0" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="2" Grid.Row="7"/> </Grid> </Window> '@ $reader = New-Object System.Xml.XmlNodeReader $xaml $frm = [System.Windows.Markup.XamlReader]::Load($reader) $countries = "U.S.A China Japan Germany France England Russia".split(" ") $comboBoxCountry = $frm.FindName("comboBoxCountry") foreach ($country in $countries) { [void]$comboBoxCountry.Items.Add($country) } $textName = $frm.FindName("textName") $comboBoxCountry = $frm.FindName("comboBoxCountry") $textFurigana = $frm.FindName("textFurigana") $stackGender = $frm.FindName("stackGender") $textAge = $frm.FindName("textAge") $textBlock = $frm.FindName("textBlock") function getChecked($parent) { $checked = "" foreach ($child in $parent.Children) { if ($child.isChecked) { $checked += $child.Content } } return $checked } function regist { $name = $textName.Text $furigata = $textFurigana.Text $gender = getChecked $stackGender $country = $comboBoxCountry.SelectedValue $age = $textAge.Text $msg = "" $msg += "名前:" + $name + "`n" $msg += "ふりがな:" + $furigata + "`n" $msg += "性別:" + $gender + "`n" $msg += "国籍:" + $country + "`n" $msg += "年齢:" + $age + "`n" $textBlock.Text = $msg } $buttonRegist = $frm.FindName("buttonRegist") $buttonRegist.Add_Click({regist}) $result = $frm.ShowDialog()
- 画面表示例
PowerShellでGUI: 可変個数RadioButton実現方法(別法)
「VisualStudioでPowerShell用GUIを作成する方法 - Itsukaraの日記」という記事で、PowerShellのGUI作成用にVisualStudioを使う方法を紹介し、可変個数のRadioButtonを表示する方法も書きました。しかし、可変個数のRadioButtonは、もっと良い実現方法を考えたので、紹介します。
とても簡単で、GUIの構造を表すXAML文字列を書き換えるだけです。下記にプログラム例を示します。下記の「add_items_to_xaml」という関数が、XAMLを書き換える処理です。汎用性があるので、RadioButton以外でも使えると思います。
- 可変個数のRadioButtonを作る例:
$ErrorActionPreference = "stop" Set-PSDebug -Strict Add-Type -AssemblyName PresentationFramework $xaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="xaml test #2" Width="170pt" Height="170pt"> <StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel x:Name="country"> </StackPanel> <Label Content=" " FontSize="10pt"/> <StackPanel x:Name="fruit"> </StackPanel> </StackPanel> <Label x:Name="msg" Content=" " FontSize="10pt"/> <Button x:Name="btn" Content="Push Me" FontSize="10pt" /> </StackPanel> </Window> '@ # 項目一覧を$xamlに追加 # $head : 置換前文字列 # $add : 置換後文字列のひな型 # $placeholder: $add中のプレスホルダー文字列 # $items: 項目一覧(空白で区切られたワード列) function add_items_to_xaml($head, $add, $placeholder, $items) { $list = $items.split(" ") [array]::Reverse($list) foreach ($item in $list) { $add_item = $add.Replace($placeholder, $item) $global:xaml = $global:xaml.Replace($head, $add_item) } } # countryへの項目追加 $country_head = @' <StackPanel x:Name="country"> '@ $country_add = @' <StackPanel x:Name="country"> <RadioButton Content="PlaceHolder"></RadioButton> '@ $countries = "U.S.A China Japan Germany France England Russia" add_items_to_xaml $country_head $country_add "PlaceHolder" $countries # fruitへの項目追加 $fruit_head = @' <StackPanel x:Name="fruit"> '@ $fruit_add = @' <StackPanel x:Name="fruit"> <RadioButton Content="PlaceHolder"></RadioButton> '@ $fruits = "Apple Orange Grape" add_items_to_xaml $fruit_head $fruit_add "PlaceHolder" $fruits $xaml $xaml = [xml]$xaml $reader = New-Object System.Xml.XmlNodeReader $xaml $frm = [System.Windows.Markup.XamlReader]::Load($reader) $country = $frm.FindName("country") $fruit = $frm.FindName("fruit") $msg = $frm.FindName("msg") # 選択された項目を返す function getChecked($parent) { $checked = "" foreach ($child in $parent.Children) { if ($child.isChecked) { $checked += $child.Content } } return $checked } # ボタンクリック時の処理 function clicked { $selectedCountry = getChecked $country $selectedfruit = getChecked $fruit $msg.content = "Country=" + $selectedCountry + "`n" + "Fruit=" + $selectedfruit } $btn = $frm.FindName("btn") $btn.Add_Click({clicked}) $result = $frm.ShowDialog()
- 書換後のXAML(上記プログラムで出力したもの)
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="xaml test #2" Width="170pt" Height="170pt"> <StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel x:Name="country"> <RadioButton Content="U.S.A"></RadioButton> <RadioButton Content="China"></RadioButton> <RadioButton Content="Japan"></RadioButton> <RadioButton Content="Germany"></RadioButton> <RadioButton Content="France"></RadioButton> <RadioButton Content="England"></RadioButton> <RadioButton Content="Russia"></RadioButton> </StackPanel> <Label Content=" " FontSize="10pt"/> <StackPanel x:Name="fruit"> <RadioButton Content="Apple"></RadioButton> <RadioButton Content="Orange"></RadioButton> <RadioButton Content="Grape"></RadioButton> </StackPanel> </StackPanel> <Label x:Name="msg" Content=" " FontSize="10pt"/> <Button x:Name="btn" Content="Push Me" FontSize="10pt" /> </StackPanel> </Window>
- 画面表示例:
あとがき
今回の件に関して、Webで色々と見つけたのですが、よい方法が見つからず、結局、自分で考えました。でも、後から考えると当たり前の内容であり、Webで色々調べた労力が無駄でした。
VisualStudioでPowerShell用GUIを作成する方法
PowerShellでGUIを使う記事を下記2件書きましたが、PowerShellのプログラムを動かすまでGUIを確認できず不便と思っていました。もしかしたら、VisualStudioを使うと、画面設計ができなかと思ったら、簡単に出来たので報告します。
自動生成されたXAMLを使いPowerShellプログラム作成
- 例えば下記のようにGUI部品を配置。
- 画面下部のXAML(文字列)を抜き出してPowerShellのプログラムを作成。
- ただし、一部の文字列は削除が必要(下記の黄色枠部分)。
- 下記がプログラムの例。「@'」と「'@」で囲まれた部分がXAML。
$ErrorActionPreference = "stop" Set-PSDebug -Strict Add-Type -AssemblyName PresentationFramework [xml]$xaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Label x:Name="label01" Content="名前" HorizontalAlignment="Left" Margin="15,26,0,0" VerticalAlignment="Top"/> <TextBox x:Name="textName" HorizontalAlignment="Left" Height="26" Margin="68,26,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="439"/> <Label x:Name="label02" Content="ふりがな" HorizontalAlignment="Left" Margin="15,57,0,0" VerticalAlignment="Top"/> <TextBox x:Name="textFurigana" HorizontalAlignment="Left" Height="26" Margin="68,57,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="439"/> <Label x:Name="label03" Content="性別" HorizontalAlignment="Left" Margin="15,88,0,0" VerticalAlignment="Top"/> <StackPanel x:Name="stackGender" HorizontalAlignment="Left" Height="26" Margin="68,88,0,0" VerticalAlignment="Top" Width="129" Orientation="Horizontal"> <RadioButton x:Name="radioButtonMale" Content="Male" Height="26" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,5,5,0"/> <RadioButton x:Name="radioButtonFemale" Content="Female" Height="26" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,5,5,0"/> </StackPanel> <Label x:Name="label04" Content="国籍" HorizontalAlignment="Left" Margin="15,119,0,0" VerticalAlignment="Top"/> <Label x:Name="label05" Content="年齢" HorizontalAlignment="Left" Margin="15,150,0,0" VerticalAlignment="Top"/> <TextBox x:Name="textAge" HorizontalAlignment="Left" Height="26" Margin="68,150,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="52"/> <ComboBox x:Name="comboBoxCountry" HorizontalAlignment="Left" Height="26" Margin="68,119,0,0" VerticalAlignment="Top" Width="439"/> <Button x:Name="buttonRegist" Content="登録" HorizontalAlignment="Left" Height="33" Margin="242,276,0,0" VerticalAlignment="Top" Width="48"/> <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Height="90" Margin="15,181,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="492"/> </Grid> </Window> '@ $reader = New-Object System.Xml.XmlNodeReader $xaml $frm = [System.Windows.Markup.XamlReader]::Load($reader) $countries = "U.S.A China Japan Germany France England Russia".split(" ") $comboBoxCountry = $frm.FindName("comboBoxCountry") foreach ($country in $countries) { [void]$comboBoxCountry.Items.Add($country) } $textName = $frm.FindName("textName") $comboBoxCountry = $frm.FindName("comboBoxCountry") $textFurigana = $frm.FindName("textFurigana") $stackGender = $frm.FindName("stackGender") $textAge = $frm.FindName("textAge") $textBlock = $frm.FindName("textBlock") function getChecked($parent) { $checked = "" foreach ($child in $parent.Children) { if ($child.isChecked) { $checked += $child.Content } } return $checked } function regist { $name = $textName.Text $furigata = $textFurigana.Text $gender = getChecked $stackGender $country = $comboBoxCountry.SelectedValue $age = $textAge.Text $msg = "" $msg += "名前:" + $name + "`n" $msg += "ふりがな:" + $furigata + "`n" $msg += "性別:" + $gender + "`n" $msg += "国籍:" + $country + "`n" $msg += "年齢:" + $age + "`n" $textBlock.Text = $msg } $buttonRegist = $frm.FindName("buttonRegist") $buttonRegist.Add_Click({regist}) $result = $frm.ShowDialog()
- プログラム実行例。名前等を入力後、「登録」ボタンを押したところ。
あとがき
VisualStudioを使うと、PowerShellのGUIがとても簡単に設計できるので、ぜひ試してみてください。
おまけ
ListBoxやComboBoxは、PowerShellから簡単に項目を追加できるが、RadioButtonは簡単に追加できない。これはとても不便だが、次のようにプレースホルダを沢山書いておき、必要なものだけ表示するようにPowerShellで制御すれば、ある程度対応できることが分かった。ご参考まで。
- プログラム例:
$ErrorActionPreference = "stop" Set-PSDebug -Strict Add-Type -AssemblyName PresentationFramework [xml]$xaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="xaml test #2" Width="170pt" Height="170pt"> <StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel x:Name="country"> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> </StackPanel> <Label Content=" " FontSize="10pt"/> <StackPanel x:Name="fruits"> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> <RadioButton Content="PlaceHolder"></RadioButton> </StackPanel> </StackPanel> <Label x:Name="msg" Content=" " FontSize="10pt"/> <Button x:Name="btn" Content="Push Me" FontSize="10pt" /> </StackPanel> </Window> '@ $reader = New-Object System.Xml.XmlNodeReader $xaml $frm = [System.Windows.Markup.XamlReader]::Load($reader) $country = $frm.FindName("country") $fruits = $frm.FindName("fruits") $msg = $frm.FindName("msg") # 全てのRadioButtonを非表示にする foreach ($s in $country.Children) { $s.Visibility = "Collapsed" } # 全てのRadioButtonを非表示にする foreach ($b in $fruits.Children) { $b.Visibility = "Collapsed" } # RadioButtonのContentを設定し、表示する $countryList = "U.S.A China Japan Germany France England Russia".split(" ") $n = 0 $children = $country.Children foreach ($c in $countryList) { $radioButton = $children[$n] $radioButton.Content = $c $radioButton.Visibility = "Visible" $n += 1 } # RadioButtonのContentを設定し、表示する $fruitsList = "Apple Orange Grape".split(" ") $n = 0 $children = $fruits.Children foreach ($f in $fruitsList) { $radioButton = $children[$n] $radioButton.Content = $f $radioButton.Visibility = "Visible" $n += 1 } # 選択された項目を返す function getChecked($parent) { $checked = "" foreach ($child in $parent.Children) { if ($child.isChecked) { $checked += $child.Content } } return $checked } function clicked { $selectedCountry = getChecked $country $selectedFruits = getChecked $fruits $msg.content = "Country=" + $selectedCountry + "`n" + "Fruits=" + $selectedFruits } $btn = $frm.FindName("btn") $btn.Add_Click({clicked}) $result = $frm.ShowDialog()
- 実行例:
*1:それぞれの部品の意味は、別のサイト等で調べてください。
PowerShell+WPFが動かない場合の原因と対策
仕事の関係でPowerShellを使ったので、PowerShellでの勉強内容と作成プログラムを記事に書きましたが、本番で動かず、困ってしまいました。同様の問題で困っている方がいるかもしれないので、解決先を書きます。
問題点
- スクリプトがネットワークドライブ上にあるとエラーとなる。
- PowerShell ISEでは動作するが、PowerShellではエラーとなる。
原因と解決方法
- スクリプトがネットワークドライブ上にあるとエラーとなる。
- 原因:スクリプトがネットワークドライブ上にあると、リモートスクリプト(ネットワークから入手したスクリプト)とみなされエラーとなる。
- 対策:powershellにオプション付加「powershell -ExecutionPolicy Bypass」
- 別案:「powershell -file ファイル名」で起動(2/18追記)
- PowerShell ISEでは動作するが、PowerShellではエラーとなる。
- 原因:powershellの古いバージョン(1.0と2.0)のPowerShellのデフォルトはMTAモードになっている。なお、PowerShell ISEは、古いバージョン(1.0と2.0)でもSTAモードで動作している。
- 対策:powershellにオプション付加「powershell -sta」
まとめ
PowerShellは、スクリプトがネットワークドライブにあるか、どのバージョンか、PowerShellかPowerShell ISEか、によって動作が異なるので注意が必要。]
- 対策:「powershell -sta -ExecutionPolicy Bypass ファイル名」で起動。
- 別案:「powershell -sta -file ファイル名」で起動(2/18追記)。
PaintsChainerが爆速になっていた
http://itsukara.hateblo.jp/entry/2017/01/29/064926:embed:先週の記事で書いた線画着色(PaintsChanier)の件、久しぶりに最新版をダウンロードして試してみたら、当方の貧弱なGPU RAM(2GB)でも動くようになっており、サイズが500x500位の画像では3秒ぐらいで処理が完了。爆速になっていた。