Powershellで画像ビューワを作る

PoweShell コンピュータ
PowerShell

Powershellからフォームを扱えるようなので画像ファイルを表示するビューワを作ってみました。

エクスプローラから画像ファイルをフォームにドラック&ドロップすると画像が表示されます。表示された画像を左クリックすると次の画像が表示され、右クリックするとひとつ前の画像が表示されます。ドラック&ドロップした画像ファイルと同一ディレクトリ内の画像が対象となります。

使い方

スクリプトを起動するとフォームが表示されます。

エクスプローラから表示したいファイルをフォームへドラック&ドロップします。

画像が表示されます。

表示された画像を左クリックすると次の画像が表示され、右クリックするとひとつ前の画像が表示されます。
終了する場合は「×」で閉じてください。

スクリプト

<#
.SYNOPSIS
 ImageViewer

.DESCRIPTION
 画像ファイルを表示する。

#>

Set-StrictMode -Version Latest
$ErrorActionPreference = "STOP"
$DebugPreference = "Continue"


# アセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing


# ディレクトリ
$dir = $null

# ファイルの一覧
$files = $null

# 位置
$pos = $null

# 初期化
function init($path) {
    
    $file = $null

    # 引数のパスがディレクトリか確認
    if ((Get-Item -LiteralPath $path).PSIsContainer) {
        # ディレクトリ
        $global:dir = $path
    } else {
        # ファイル
        $global:dir = Split-Path $path -Parent
        $file = $path
    }

    # ファイルの一覧を取得
    $global:files = Get-ChildItem -LiteralPath $global:dir -File | ? {
        $_.Extension -match "\.(jp(e)?g|png|bmp|gif)$"
    } | % {
        $_.FullName
    }
    if ($global:files -eq $null) {
        $global:files = @()
    } elseif ($global:files -Is [System.String] ) {
        $global:files = @($global:files)
    }


    # 初期位置をセット
    $global:pos = $global:files | % -Begin {
        $i = 0
        $result = -1
    } -Process {
        if($_ -eq $file) {
            $result = $i
        }
        $i = $i + 1
    } -End {
        $result
    }

    # ファイル指定が無い場合の初期位置
    if (($global:pos -eq -1) -And ($global:files.Count -gt 0)) {
         $global:pos = 0
    }

}

# 次のファイルへ移動
function next() {
    if ($global:pos -lt 0) { return } # -1
    if ($global:pos -eq ($global:files.Count-1)) { return } # 末尾

    $global:pos = $global:pos + 1
}

# 前のファイルへ移動
function prev() {
    if ($global:pos -lt 0) { return } # -1
    if ($global:pos -eq 0) { return } # 先頭

    $global:pos = $global:pos - 1
}

# イメージオブジェクトを取得
function get_image() {
    if ($global:pos -lt 0) { return $null } # -1

    $result = $null

    # 配列の要素を取得
    $global:files | Select-Object -Index $global:pos | % {
        $file = $_    

        $fs = New-Object System.IO.FileStream(
            $file,
            [System.IO.FileMode]::Open,
            [System.IO.FileAccess]::Read
        )

        if ($fs) {
            try {
                $result = [System.Drawing.Image]::FromStream($fs)
            } catch {
                $fs.Close()
                throw $_
            }
            $fs.Close()    
        }
    }
    
    return $result
}


# 画像ビューワ
function simple_imageview() {
    

    $form = New-Object System.Windows.Forms.Form

    $pic = New-Object System.Windows.Forms.PictureBox
    $pic.Name = "PICBOX"
    $pic.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
    
    # D&D
    $pic.AllowDrop = $true
    $pic.Add_DragEnter({$_.Effect = "ALL"})
    $pic.Add_DragDrop({
        $file = @($_.Data.GetData("FileDrop"))[0]
        init $file
        if ($this.Image) { $this.Image.Dispose() }
        $this.Image = get_image
    })


    $pic.Add_MouseDown({
        switch ($_.Button) {
            "Right" {
                prev
                if ($this.Image) { $this.Image.Dispose() }
                $this.Image = get_image
            }
            "Left" {
                next
                if ($this.Image) { $this.Image.Dispose() }
                $this.Image = get_image
            }
            "Middle" {
                throw $_
            }
        }
    })

    $form.Controls.Add($pic)
    $form.Size = New-Object System.Drawing.Size(800,600)
    $pic.Size = $form.ClientSize
    $form.Add_Resize({$this.Controls["PICBOX"].Size = $this.ClientSize})
    $form.ShowDialog()

    if ($pic.Image) { $pic.Image.Dispose() }
    Write-Host "終わり"
}

simple_imageview

学んだこと

マウスクリックのイベントは.Add_MouseDown({})で、{}のスクリプトブロック内で実行するコードを記述する。
スクリプトブロック内からイベントの発生元のオブジェクトを参照する場合は$thisで参照出来る。同じくイベントパラメータは$_で参照可能。

フォーム上にある各種コントロールにアクセスする場合はコントロールオブジェクトのNameプロパティに一意の名前を付けておくと、「フォームオブジェクト」.Controls[名前]でアクセス可能。これでイベントのスクリプトブロック内でフォームオブジェクトが参照出来ればフォーム上にある全てのコントロールにアクセス可能になる。

今後

書籍をスキャンした画像ファイルをアーカイブしたzipファイルに対応させたいところです。
この類のソフトは多数存在しますが性能的には及ばなくともPowershellで似たようなソフトが作れるか試してみたい。

最後に

私は.netの開発経験が無い。これが初めてに成るわけですが最初がPowershellなのは良いのか悪いのか…。

コメント