パスワードを忘れた? アカウント作成
17408029 journal
Python

route127の日記: クリボー保存 6

日記 by route127

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出てた。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by Anonymous Coward on 2023年10月10日 23時17分 (#4543379)

    PowerShell Coreじゃない、Windows標準添付バージョン(5.1等)だとGet-Clipboard [microsoft.com]が高機能なので楽。

    powershell -version 5.1 -command "(Get-Clipboard -Format Image).Save('test.png')"

    で終わる模様。

    クリップボードは読み書きに失敗する事も有るのでエラー処理はさぼらない方が本当は良い。
    スクリーンショットをクリップボードに書き込み中に読みだそうとしたりすると稀にコケるかも。
    CPU負荷テスト中とかクリップボード監視ツールを動かしてると多分再現しやすくなる。

    • (自分の環境で上手く行かなかったのPSのバージョン低かったからか…)

      powershell -command "$PSVersionTable"

      PSVersion 5.1.19041.3031

      !?
      今試したらちゃんと保存できた。
      クリップボード周りの遅延とか考えると確かにエラー処理した方がいいんだろうけど自分用ツールだしと面倒くさがるとこういう失敗する。
      テキスト用だとautohotkeyにClipWait [ahkwiki.net]みたいなのがあるけどこの画像版を手軽に書けたらな。

      PowerShell 7だとFormatオプション使えないらしい。 [qiita.com]

      親コメント
      • by Anonymous Coward

        バージョンが新しい(6.0以降)のCore系はOS間の互換性が低くなるからFormatオプションは実装しない方針だとか。

        • by Anonymous Coward

          欠けた後半

          ソース:
          https://github.com/PowerShell/PowerShell/issues/12290 [github.com]

          6.xだとGet-Clipboardが消滅してたので、移植性考えたら大本の通りのほうが良いのは間違いないので。

          ClipWaitはContainsText相当かな?
          ContainsImage [microsoft.com]で画像が取れるか引っ張れます。

          GetImageで取得後、

          • >前回のスクリーンショットを持ってきちゃう事故
            それが怖くて画素値で差を取ろうとしてたけどスクショ保存時にクリップボードをフラッシュすればそもそも防げるのか。

            改造前: クリップボードの内容物が画像ではない時にエラー

            powershell -command "Add-Type -AssemblyName System.Windows.Forms;[Windows.Forms.Clipboard]::GetImage().Save('test.png')"

            改造後: クリップボードの内容物が画像ではない時に文字列"None"を返す。画像だった場合はファイルに保存後クリップボードをクリアする。

            powershell -command "Add-Type -AssemblyName System.Windows.Forms;if([Windows.Forms.Clipboard]::ContainsImage()){[Windows.Forms.Clipboard]::GetImage().Save('test.png');[Windows.Forms.Clipboard]::Clear();}else{'None'}"

            なんか名前空間(今回は[Windows.Forms.Clipboard])を繰り返し書かないといけないのが面倒臭くなってきて調べてたら名前を変数にぶっこんだりAdd-Type -MemberDefinitionとかやりようありそげ。
            https://outofmem.hatenablog.com/entry/2015/06/07/180422 [hatenablog.com]

            親コメント
typodupeerror

あつくて寝られない時はhackしろ! 386BSD(98)はそうやってつくられましたよ? -- あるハッカー

読み込み中...