PowerShellでGUIアプリケーションを作る際に使えるオブジェクト座標管理ライブラリを作った

こんにちは、PICMANです。タイトルが長いのは許してください。

今回はタイトルの通り、PowerShell(以下、PS)でGUIアプリ~(略)のライブラリを作った話です。

そもそもPSでGUIなんか作れるんかい、という話ですが簡単に言ってしまうとPowerShell 2.0から追加されたAdd-Typeというコマンドレットを使って.NET Frameworkのアセンブリを読み込み、Windows フォームアプリケーションを作れるそうです。このAdd-Typeが優秀(?)でC#で書いたコードをPS上で取り込んだり、DLL読み込んでネイティブWindows APIを叩いたりできるそうです。コマンドプロンプトがただのCLI実行器程度の機能しかない印象だったのですがPSは面白いですね。Windowsに特に何も入れずにスクリプトからGUIが作れるのはソフトウェアのインストールが制限されている環境では重宝しそうです。

GUIの作り方詳しい話はググるといろいろ出てきますが、下記サイトさんが参考になりました。

上記のサイトさんを見てわかる通り、おおむね作り方としてはフォームウィンドウを生成してLabelやらButtonやらTextBoxのオブジェクトをピクセル座標で1つ1つ場所指定して配置していきます。大変ですね。VisualStudioが入っていればお手軽に作成する方法がある([1], [2])そうですが(それならもうC#で書いたほうが早いのではという話は置いておいて)、とりあえずお手軽に試したいということでオブジェクト座標管理ライブラリを作った次第になります。

DrawingPointCalculator

前置きが長くなりましたが、作ったものは「DrawingPointCalculator」でGithubに上げています

できることとしては、配置したオブジェクトのサイズをセットすると、次に配置するべき場所の座標を返してくれます。要は (x,y座標を取得)→(配置したオブジェクトのサイズをセット) を順々にやっていればなんかいい感じにオブジェクトを空いている場所に配置できます。

使い方

ライブラリなんて言っていますがただのクラスです。使い方としてはファイルを.でインポートしてもらい、インスタンスを作るだけです。コンストラクタの引数はx,y方向のマージンです。

PowerShell
.".\DrawingPointCalculator.ps1"

[int] $margin = 10

$dpc = New-Object DrawingPointCalculator($margin, $margin)

メソッド一覧

  • [in] getX()
  • [int] getY()
  • [void] addObjV([int] $objW, [int] $objH)
  • [void] addObjH([int] $objW, [int] $objH)
  • [void] newLine()
  • [void] beginWrapper()
  • [void] endWrapper()

大体名前の通りですが一応説明を。

getX(), getY()は次にオブジェクトを配置するべきx,y座標を返します。

addObjV(), addObjH()は引数にオブジェクトのWidth, Heightを与えることでライブラリ内で次にオブジェクトを配置すべきx,y座標を計算させます。addObjV()を使うと垂直方向にオブジェクトを配置します。つまり、x座標は変わらずy座標がオブジェクトのHeight+マージン分追加された値に更新されます。addObjH()を使うと水平方向にオブジェクトを配置します。つまり、y座標は変わらずx座標がオブジェクトのWidth+マージン分加算された値に更新されます。

newLine()はそのまま改行を意味します。addObjH()で水平方向にオブジェクトを配置して改行したくなった場合に使います。ちなみに、newLine()しないと「\rしないで\nだけしたコンソール画面」のような状態になります(伝われ)。

beginWrapper(), endWrapper()は、垂直方向にオブジェクトを配置した右隣にオブジェクトを置きたいような場合に使います。何をしているかというと、beginWrapper()した際のy座標を記録しておき、垂直方向にオブジェクトを配置のちにendWrapper()すると記録したy座標に戻ります。その際、x座標はこれまで配置したオブジェクトの最大値+マージン分の値となります。結局どういうことやねん、という方は下記サンプルのテキストボックスの部分を見ていただければなんとなく分かってもらえるかもしれません。

サンプル

とりあえずサンプルコードを張っておきます。下記はGithubのtest.ps1と同じ内容です。これをPS上で実行するとした図のようなウィンドウが生成されます(コピペしてもライブラリのファイルがないので動きません、Githubからcloneして実行したほうが早いです)。

PowerShell
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

.".\DrawingPointCalculator.ps1"

$form = New-Object System.Windows.Forms.Form -Property @{
    Size = New-Object System.Drawing.Size(1200, 400)
    Font = New-Object Drawing.Font('Meiryo UI', 12)
    Text = "DrawingPointCalculator test"
}

############################################################
# 1. basic usage(vertically add)
############################################################

# init(x,y margin = 10)
$dpc = New-Object DrawingPointCalculator(10, 10)

$title = New-Object System.Windows.Forms.Label -Property @{
    # get location point
    Location = New-Object System.Drawing.Point($dpc.getX(), $dpc.getY())
    Font = New-Object Drawing.Font('Meiryo UI', 16)
    Text = "This is title label"
    AutoSize = $true
}
$form.Controls.Add($title)
$dpc.addObjV($title.Width, $title.Height)   # add objects vertically

############################################################
# 2. horizontally add
############################################################

$button1 = New-Object System.Windows.Forms.Button -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Text = "button1"
    AutoSize = $true
}
$form.Controls.Add($button1)
$dpc.addObjH($button1.Width, $button1.Height)   # add objects horizontally

$button2 = New-Object System.Windows.Forms.Button -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Text = "button2"
    AutoSize = $true
}
$form.Controls.Add($button2)
$dpc.addObjH($button2.Width, $button2.Height)

$button3 = New-Object System.Windows.Forms.Button -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Text = "button3"
    AutoSize = $true
}
$form.Controls.Add($button3)
$dpc.addObjH($button3.Width, $button3.Height)

$dpc.newLine()  # new line

############################################################
# 3. Wrapper
############################################################

$dpc.beginWrapper() # set wrapper

$textbox1 = New-Object System.Windows.Forms.TextBox -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Width = 500
}
$form.Controls.Add($textbox1)
$dpc.addObjV($textbox1.Width, $textbox1.Height)

$textbox2 = New-Object System.Windows.Forms.TextBox -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Width = 500
}
$form.Controls.Add($textbox2)
$dpc.addObjV($textbox2.Width, $textbox2.Height)

$textbox3 = New-Object System.Windows.Forms.TextBox -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Width = 500
}
$form.Controls.Add($textbox3)
$dpc.addObjV($textbox3.Width, $textbox3.Height)

$dpc.endWrapper() # end wrapper

$textbox4 = New-Object System.Windows.Forms.TextBox -Property @{
    Location = New-Object Drawing.Point($dpc.getX(), $dpc.getY())
    Multiline = $true
    Width = 500
    Height = 150
}
$form.Controls.Add($textbox4)
$dpc.addObjV($textbox4.Width, $textbox4.Height)

$dpc.newLine()

$label1 = New-Object System.Windows.Forms.Label -Property @{
    # get location point
    Location = New-Object System.Drawing.Point($dpc.getX(), $dpc.getY())
    Text = "text text text text text text text text text text text text text text text text"
    AutoSize = $true
}
$form.Controls.Add($label1)
$dpc.addObjV($label1.Width, $label1.Height)   # add objects vertically

# show
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

利用時のポイントとしては、

  • 各オブジェクトのLocationプロパティに$dpc.getX(), $dpc.getY()で座標を指定している
  • オブジェクト生成後、$dpc.addObjV() or $dpc.addObjH()でオブジェクトのWidth, Heightを引数取って呼び出している

でしょうか。いちいち数値で座標を入れなくてよくなっただけで、あとからオブジェクトを追加したい場合やメンテナンス的にも使い勝手がよくなったと思います。

誰かのお役に立てれば幸いです。

PIC MAN

ソフトとハードの両方の目線を持てるようになりたいです.

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です