Sikuli - スクリーンショットをとる

Sikuliでスクリーンショットをとるにはcaptureメソッドを使います。
captureメソッドの返値には画像ファイルのフルパスが返ってくるので、必要に応じてリネームします。

(1) フォーカスのあるウィンドウだけスクリーンショットをとる場合

1
2
filename = capture(App.focusedWindow())
print('file1', filename)

(2) 画面全体のスクリーンショットをとる場合

1
2
img = SCREEN.capture()
print('file2', img.filename)

(3) ユーザが選択したエリアのスクリーンショットをとる場合

1
2
filename = capture()
print('file3', filename)

Sikuli - マウス移動の高速化

実際にSikuliでの動作を確認すると気づきますが、マウスポインタの動きは低速です。
(ロボットが頑張って動いている感じがしてやや面白いのですが。。)

これはマウスポインタの移動に対して、ウェイトがかかっているためです。
このウェイトは Settings.MoveMouseDelay で設定できます。

1
Settings.MoveMouseDelay = 0

上記のように0を設定すると、マウスポインタが瞬間移動するようになり処理を高速化させることができます。

Sikuli - ショートカットキーの設定

Sikuliにはショートカットキーを押したときに処理を実行する機能があります。

ショートカットキーでコードを実行するには、あらかじめキーが押されたときに実行したい関数を定義しておきます。引数にはHotkeyEventオブジェクトを設定します。

1
2
3
4
5
6
def hotKey(event):
r1 = App.focusedWindow()
paste(r1, 'test paste')

Env.addHotkey('k', KeyModifier.ALT, hotKey)
popup(u'[alt] + [k]で貼り付け')

HotkeyEventオブジェクトのプロパティは下記の通りです。

プロパティ名 解説
keyCode 入力されたキーのキーコード
modifiers 入力された修飾キー(Shift、Ctrl、Altなど)

ショートカットキーに関数を設定するためには、EnvクラスのaddHotkeyメソッドを使用します。
また、設定したショートカットキーを解除するためには、EnvクラスのremoveHotkeyメソッドを使います。

処理が終了してしまうとショートカットキーが解除されてしまうので、今回はポップアップメッセージを表示して処理を終了しないようにしています。

Sikuli - クリップボードの値を取得する

Sikuliでクリップボードの値を取得するには、App.getClipboardメソッドを使用します。
例えば、コピーした文字列をresult変数に取得するには下記のように実装します。

1
2
result = App.getClipboard()
print(result)

もちろんクリップボードの値を取得する前に type(‘xxxxx.png’, ‘c’, Key.CTRL)などを実行しておいて情報をコピーしておく必要があります。

Sikuli - 例外処理

合致しなかったときの例外処理

Sikuliでは画像が見つからなかった時に例外が発生し、プログラムが停止します。
これを防ぐ方法としてまず考えられるのが、existsメソッドを使う方法です。existsメソッドを使ってあらかじめ画像が存在するかどうかを確認し、存在するならばクリックするようにプログラミングします。

1
2
3
4
if exists('xxxx.png'):
click(getLastMatch())
else:
popup(u'画像が見つかりませんでした。')

しかしこのようにif文をひとつひとつ記述するのは煩雑で、コードも見づらくなりますので、次の処理を検討した方がよいでしょう。

例外をトラップ

Sikuliは、画像が見つからないときなどに、FindFailedという例外を発生します。
次のコードのようにtry catchで囲って処理します。この方法であれば先に実装したようにひとつひとつexistsメソッドで調べなくてもすみます。

1
2
3
4
try:
click("xxxx.png")
catch FindFailed:
popup(u'画像が見つかりませんでした。')

例外をスキップするか例外ハンドラ処理

画像が見つからない場合は、そのまま無視したり、ユーザにどうするかを尋ねたりするほか、自分が設定した独自の例外ハンドラで処理することもできます。

関数名 解説
setThrowException(f) 例外を発生するかどうかを設定する。
f=Trueで発生させる(既定)
f=Falseで抑制する
setFindFailedResponse(ABORT|SKIP|PROMPT|RETRY) 画像が見つからなかったときにどうするかを設定する。
ABORT=例外を発生し終了する(既定)
SKIP=無視する。戻り値はNone
PROMPT=どのようにするかをユーザに尋ねる
RETRY=見つかるまでリトライする
setFindFailedHandler(handler) 例外が発生したときに実行する独自の例外ハンドラhandlerを設定する。

(1)例外の抑制

setThrowException(False)またはsetFindFailedResponse(SKIP)を実行すると、例外を抑制できます。この場合、戻り値はNoneになります。
clickメソッドやtypeメソッドは、引数がNoneであってもエラーなどは発生せずなにもしないだけです。
特にエラーメッセージを表示する必要がなく、単純に見つからなかったときは何もしないということであれば次のように記述できます。

1
2
setThrowException(False)
click('xxxx.png')

(2)どうするかをユーザに尋ねる

setFindFailedResponse(PROMPT)を実行しておくと、画像が見つからなかったときに確認ダイアログ①が表示されます。

1
2
setFindFailedResponse(PROMPT)
click('xxxx.png')

確認ダイアログ①


[Retry]をクリックすればリトライし、[Abort]をクリックすればプログラムは終了します。
[Capture/Skip]ボタンをクリックするとさらに次の確認ダイアログ②が表示されます。

確認ダイアログ②


[Capture]をクリックすると、この画像を別の画像に差し替えることができます。
Sikuliでは画像の解像度などの違いによって、別の環境に持っていくと画像がマッチしなくなってしまうことがあります。
setFindFailedResponse(PROMPT)としておけば、そのような場合ユーザがマッチする画像を差し替えることができるため大変便利です。

(3)独自の例外ハンドラ設定

setFindFailedHandlerメソッドを使うと、独自の例外ハンドラを設定できます。
ハンドラの書式は以下の通りです。

1
2
3
def handler(event):
#画像が見つからないときの処理をここに書く
event.setResponse(PROMPT) # どうするかをユーザに尋ねる

引数のeventオブジェクトはイベントが発生したときに渡されるObserveEventオブジェクトです。
例外ハンドラではevent.setResponseメソッドを使って、ABORT,SKIP,PROMPT,RETRYのいずれかの値を返さなければなりません。

この方法をとると画像が見つからなかったときに、エラーログを出力したり、メールを送信したりし、その後の処理はユーザに尋ねるといったことができるので個人的には一番お勧めです。

Sikuli - 使いどころ

Sikuliはアイデア次第でいろいろな場面で使えます。代表的な用途としては次の通りです。

  • 業務アプリ操作の自動化
    事務作業・経理作業を自動化することにより作業を大幅に軽減できます。特に、あるソフトから別のソフトへの転記作業は得意で、Sikuliを導入すれば大変効率が向上するはずです。
  • システム開発のテスト自動化
    システム開発をする際の各種テストの自動化にSikuliは貢献します。
    開発したシステムのテストでは操作するシナリオを決め、その通りに実装できているかを確認する場面があるかと思います。そのような場面において一旦Sikuliを使ったテストプログラムを作っておけば何度でも再テストできます。
    さらに他のシステムを開発する際にも、それを流用したテストができるため人間が一からテストを手作業で実施するのに比べてはるかに効率化できます。
    Sikuliにはスクリーンショットをとって保存する機能もあるのでテストのエビデンスを残すのも簡単です。
  • ゲームのBot作成
    ゲームの自動プレイの作成ができます。PCに接続したAndroid端末の操作もできます。(ただまだテスト版で不安定だという噂です。)

Sikuli - デメリット

Sikuliには次のようなデメリットがあります。

  • 画面の解像度やテーマが変わると動かなくなる可能性がある
    Sikuliでは、画像マッチングでボタンなどの操作対象を探しているので、デザインが変わった場合やディスプレイの解像度が変わった場合などに、動かなくなる可能性があります。
    そのような問題が発生した場合は、プログラムの画像が入っている部分を正しく差し替える必要があります。
  • 自動実行中にはユーザ操作ができない
    RPAツールはマウス操作やキーボード操作を自動化するツールです。サンプルを作って実行するとすぐに気づきますが、実行中は自動的にマウスポインタが動いたりキー入力したりするので、その間ユーザは操作することができません。
    操作が禁止されるわけではありませんが、マウスが勝手に動く中それを遮るように人間がマウス操作やキー操作するのは現実的ではありません。
    完全にSikuliに操作を任せたいのであれば、Sikuli専用PCを用意するか仮想マシンを用意する必要があります。

Sikuli - メリット

Sikuliには次のようなメリットがあります。

  • プログラムが直感的
    ソースコードの中に対象の画像が表示されるので、何を操作しているのかが直感的に分かります。
  • メニューやボタン以外も操作可能
    画像マッチングで位置を特定するだけなので、そこに何があるのかを気にしません。ほとんどの操作対象はボタンやメニューだと思いますが、それ以外のものも操作できます。例えば、Adobe Flashを使ったアプリケーションやPC上で動作するスマホのシミュレータなど、OSからはボタンやメニューとして認識されないものでも操作することができます。
  • 画像マッチングを利用して監視もできる
    画像上の変化を監視することができます。例えば何か業務アプリで障害が発生したときに赤いエラーメッセージが表示されるようなシステムがあったとします。Sikuliを使うと画面の変化を監視し赤いエラーメッセージが表示されたときに、警告メールを送信するようなプログラムを作ることができます。
    PCにカメラを接続していれば、Sikuliでそのカメラ画像の変化を監視することもできます。
  • ユーザインターフェースを作れる
    Python、Ruby、Javaなどに対応していて、それぞれのプログラミング言語に対応した各種ライブラリを使うことができ、単純なRPAツールとしてだけでなく本格的なカスタムRPAを作る際のフレームワークとしても活用できます。
  • OCR処理ができる
    画像をOCR処理することができ、帳票をスキャナで読み込んでOCR処理を行い、帳票入力を支援するツールなども作れます。

Sikuli - OCR日本語対応

Sikuliで日本語のOCRを実行してみました。

環境

動作確認環境は次の通りです。

  • OS
    Windows 10 Home
  • Sikuliバージョン
    2.0.1
  • javaバージョン
    1.8.0_101(64bit)

前準備

OCRを日本語に対応させるためには、日本語OCR用のデータファイルをダウンロードする必要があります。

ダウンロードデータ

https://github.com/tesseract-ocr/tessdata/blob/master/jpn.traineddata

ダウンロードデータを格納するフォルダ

C:\Users(ユーザ名)\AppData\Roaming\Sikulix\SikulixTesseract\tessdata

※SikulixTesseractフォルダが見つからない場合は、一度SikuliでOCRに関する処理を実行してみると、自動で作成されるようです。

実装

以下のコードを実行すると、領域選択画面が現れるので、そこでOCRを実行したいエリアを選択するとOCR処理されたデータがout.txtに出力されます。

1
2
3
4
5
6
7
8
9
10
11
import sys
reload(sys)
sys.setdefaultencoding("SHIFT_JISX0213")

tr = TextOCR.start()
tr.setLanguage("jpn")

r = selectRegion()
#print(r.text())
with open('out.txt', 'w') as f:
f.write(r.text())

※標準出力だと文字化けするのでとりあえずファイル出力にして回避しました。

Sikuli - バッチ起動時に引数を渡す

Sikuliプロジェクトをバッチ(コマンド)で実行する際に引数を渡すには下記のようにします。

1
java -jar sikulix.jar -r xxxxxxx.sikuli -- 引数1 引数2

-jarのあとにsikuli.jarを指定し、-rのあとにSikuliプロジェクトを指定するところまでは引数なしのコマンド実行と同じです。
そこからハイフンを2つ入力し、引数をスペース区切りで指定すると、起動引数として処理に渡すことができます。

Sikuliプロジェクトの中で起動引数を確認するときは、Pythonでの文法そのままです。

1
2
3
import sys

print(sys.argv)

Python使いの方にとっては実行時のハイフン2つを覚えるだけで簡単に実装できます。