Sikuli - 複数個所を連続して選択する

Regionオブジェクトのfindメソッドを使うと、引数に指定した画像をRegionオブジェクトの内部から探し最も合致した一か所を返します。

findAllメソッドを使うと合致した全ての箇所をリストとして返します。
このfindAllメソッドを使うとチェックボックスをすべてクリックするといった操作を行うことができます。

1
2
3
4
5
r = Region(1622,365,220,125)
list = r.findAll('checkbox.png')

for chk in list:
click(chk)

Sikuli - マウスホイールでスクロールする

Sikuliではマウスホイールでスクロールするための関数 wheel が用意されています。

wheel関数の引数は3つあります、

  • Regionオブジェクト
  • マウスホイールのアップ・ダウン
  • 移動数

以下の例ではフォーカスのあるウィンドウでマウスホイールを5回分ダウンさせたあとに、5回分アップさせます。

1
2
3
4
5
r = App.focusedWindow()

wheel(r, WHEEL_DOWN, 5)

wheel(r, WHEEL_UP, 5)

実際の動作としてはRegionオブジェクトの中心位置にマウスが移動し(クリックはされない)、マウスホイールを回転させる動作が実行されます。

Sikuli - 幅や高さが等間隔なものに入力する

Sikuliで幅と高さが等間隔の場合のデータ入力ではsetRasterメソッドを使うと便利です。

まずはfindメソッドで入力エリアのRegionを指定します。
そのRegionに対してsetRasterメソッドを使うと等間隔に分割することができます。

分割したエリアにはgetCell関数に行番号と列番号を指定しそれぞれのセルにアクセスすることができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
# coding:utf-8
data = [[1, 2, 3, 4],
[10, 20, 30, 40]]

cells =find("1579840049307.png")
#4行3列に分割
cells.setRaster(3, 4)

for y, row in enumerate(data):
for x, col in enumerate(row):
# getCellメソッドで各セルに値を貼り付ける
paste(cells.getCell(y + 1, x), str(col))
wait(0.3)

5行目に指定した画像は下記です。

1579840049307.png

実行してみるとカーソルが1セルずつ選択を行い、データが入力されていきます。

Sikuli - ダイアログの表示位置を設定する

ダイアログはデフォルトで画面中央に表示されますが、popatメソッドを使用するとダイアログの表示位置を変更することができます。

popatメソッドの引数にx,y座標を指定することができるとのことですが私の環境ではエラーになってしまいました。
動作確認ができたLocation指定のコードは下記の通りです。

1
2
popat(Location(1511, 255))
popup('test')

LocationはSikuli IDEのツールバーで指定できますのでこの方法がおすすめです。

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端末の操作もできます。(ただまだテスト版で不安定だという噂です。)