route127の日記: クリボー保存 6
Win機で画像認識の為に特定のウィンドウのスクリーンショットを決まった名前で繰り返し保存する必要があった。
当初Win32::ScreenshotのCaptureHwndRectを使おうかと思っていたがcpanmでのインストール中にImage::Magickのテストでコケた。
--forceスイッチで行けそうな気がしたがなんとなく気が進まずに別の方法を試す気分になってWin32::GuitestでAlt+PRTSCR(^{PRTSCR})押下することにした。
キャプチャしてクリップボード上にある画像を画像ファイルとして保存するのにはpowershellのワンライナを利用した。
powershell -command "Add-Type -AssemblyName System.Windows.Forms;[Windows.Forms.Clipboard]::GetImage().Save('test.png')"
Microsoft.NETクラスをあまりよくわかってないがpowershellでのクリップボード上画像保存記事を参考にして書いた。
その後python+PILでもクリップボード上の画像を保存できることを知って試してみたがちょっと怪しい。
カットシステムの『Python+Pillow/PIL』読みながら書いたワンライナが動かなかった。
python -c "from PIL import Image,ImageGrab;filelist=ImageGrab.grabclipboard();print(filelist);img=Image.open(filelist[0]);img.show();"
田+vでおなじみのクリップボードの履歴機能があるのでImageGrabが一時ファイルのリストを返すとして書いてる旨注釈があったので、その辺が自機との設定差で上手くいってないのだと思い他の例を探してたら
img=ImageGrab.grabclipboard();
とgrabclipboard()の戻りを直接Imageオブジェクトとして受け取っている記事があり、それに倣うようにした。
一応ドキュメントのgrabclipboard()の項目見ると動作にOSによる差なんかもあるらしい。
またImageGrabにもスクショ機能はあるものの「矩形選択してスクリーンショット」はあってもやはり「hWndでウィンドウ指定してスクリーンショット」はない。
そんなわけでPILで書き直そうかと思ったけど結局PSのままで適当に動かしているが、PSもクリップボードの設定如何で動作が変わったりするんじゃないかという心配はありつつも億劫がって調べてない。
今回の件にはあまり関係なかったが、Win8、Win10にはスクショ用のより高速なAPIが用意されているらしい。
一口にスクリーンショットと言っても実際手を付けてみると色々あるな。
環境はpython 3.11であった。最近3.12出てた。
PowerShell 5.1ならもっと楽 (スコア:0)
PowerShell Coreじゃない、Windows標準添付バージョン(5.1等)だとGet-Clipboard [microsoft.com]が高機能なので楽。
powershell -version 5.1 -command "(Get-Clipboard -Format Image).Save('test.png')"
で終わる模様。
クリップボードは読み書きに失敗する事も有るのでエラー処理はさぼらない方が本当は良い。
スクリーンショットをクリップボードに書き込み中に読みだそうとしたりすると稀にコケるかも。
CPU負荷テスト中とかクリップボード監視ツールを動かしてると多分再現しやすくなる。
Re:PowerShell 5.1ならもっと楽 (スコア:1)
(自分の環境で上手く行かなかったのPSのバージョン低かったからか…)
!?
今試したらちゃんと保存できた。
クリップボード周りの遅延とか考えると確かにエラー処理した方がいいんだろうけど自分用ツールだしと面倒くさがるとこういう失敗する。
テキスト用だとautohotkeyにClipWait [ahkwiki.net]みたいなのがあるけどこの画像版を手軽に書けたらな。
PowerShell 7だとFormatオプション使えないらしい。 [qiita.com]
Re: (スコア:0)
バージョンが新しい(6.0以降)のCore系はOS間の互換性が低くなるからFormatオプションは実装しない方針だとか。
Re: (スコア:0)
欠けた後半
ソース:
https://github.com/PowerShell/PowerShell/issues/12290 [github.com]
6.xだとGet-Clipboardが消滅してたので、移植性考えたら大本の通りのほうが良いのは間違いないので。
ClipWaitはContainsText相当かな?
ContainsImage [microsoft.com]で画像が取れるか引っ張れます。
GetImageで取得後、
Re:PowerShell 5.1ならもっと楽 (スコア:1)
>前回のスクリーンショットを持ってきちゃう事故
それが怖くて画素値で差を取ろうとしてたけどスクショ保存時にクリップボードをフラッシュすればそもそも防げるのか。
改造前: クリップボードの内容物が画像ではない時にエラー
改造後: クリップボードの内容物が画像ではない時に文字列"None"を返す。画像だった場合はファイルに保存後クリップボードをクリアする。
なんか名前空間(今回は[Windows.Forms.Clipboard])を繰り返し書かないといけないのが面倒臭くなってきて調べてたら名前を変数にぶっこんだりAdd-Type -MemberDefinitionとかやりようありそげ。
https://outofmem.hatenablog.com/entry/2015/06/07/180422 [hatenablog.com]
Re: (スコア:0)
余談だけど、クリップボードAPIを直接叩く [microsoft.com]のと違って、
Windows.Forms.Clipboardなら10回まで100msのウェイト付きで自動リトライしてくれる [github.com]のでOpen失敗処理を手抜き出来るのはメリット。
逆に自動でリトライしちゃうのでクリップボードの書き込み側に影響を及ぼしかねないけど、今回のケースなら気にしなくて良さそうかな。