はじめに
SikuliXでいろいろやってるうちに、これはいろいろできると思って、サンプルを作っているうちにわかってきたことの覚書です。
コードをUTF-8に固定しよう
Python使いならやる、コードの先頭に書くあれです
# -*- coding: utf-8 -*-
最低限のモジュールを読み込んでおこう
基本的にフルに読み込む場合はimportでライブラリ名を指定しておけばOK、デフォルトの設定を変える場合はreloadする必要があった
sikulixはなぜかsysをreloadしておかないと、デフォルトの文字コードやフォントサイズが変更できなかった
import os
import sys
import time
import datetime
import csv
import subprocess
reload(sys)
sys.setdefaultencoding("utf-8")
グローバル変数をセットしよう
関数内部から呼び出したりする変数として、必要なものを定義します。
ここでは、whileでループを切り替えるための変数を設定しています
#ループ制御用変数
global infinitloop
infinitloop = 0;
コマンドライン実行した場合の停止用ホットキーを定義しよう
IDEから起動する限り、IDEの割り込みキーが効きますが、コマンドラインから実行した場合割り込みが効かなくなるので、ホットキーを別途設定する場合、以下のように設定します
##ホットキーの登録(コマンドラインから実行した場合の停止受付)
def StopOrContinue(event):
ans = popAsk(u"スクリプトを停止しますか?",u"確認")
if ans == True:
sys.exit() # スクリプト自体を強制停止
##スクリプトを正常終了させるための割り込み
def StopLoop(event):
global infinitloop
infinitloop = 1
return infinitloop # Whileループを脱するようにして終了
#StopOrContinueとStopLoopに対してイベントを起こすホットキーを登録
Env.addHotkey(Key.UP,KeyModifier.CTRL,StopOrContinue) # CLI実行時の停止用(CTRL+↑で、StopOrContinueを呼ぶ)
Env.addHotkey(Key.DOWN,KeyModifier.CTRL,StopLoop) # IDE実行時の停止用(CTRL+↓で、StopLoopを呼ぶ)
#Env.removeHotkey("o",KeyModifier.CTRL,keepdown_up) # Env.addHotkeyで追加したホットキーを解除する
システム変数を設定しよう
デフォルト値を変更する場合以下のように設定します
#各種移動ディレイ(デフォルト:1)、0にするとマウスオーバーで何か動作する処理とかが動かなくなるので注意
Settings.MoveMouseDelay = 0.5
Settings.DelayAfterDrag = 0.5
Settings.DelayBeforeDrop = 0.5
#ログの出力制御
Settings.ActionLogs = True
Settings.InfoLogs = True
Settings.DebugLogs = False
#最小マッチング率、デフォルト 0.70 最小 0.01 最大 0.99 具体的には similar(0.01~0.99) のこと
#PatternのSimilarで都度設定しない場合の最小値を変更する
Settings.MinSimilarity = 0.99
#Region.onChange()を使用したときに、変更イベントをトリガーする内容を変更するピクセル単位の最小サイズ。
Settings.ObserveMinChangedPixels = 50
#1秒辺りの走査回数を定義 ※ただし、CPU性能と描画性能がものをいうので、上げ過ぎても効果なし
Settings.WaitScanRate = 10
Settings.ObserveScanRate = 10
#動作前にハイライトマーカーを出すかどうか[True|False]
Settings.setShowActions(False)
#DelayBeforeMouseDownは、ソース位置のマウスがダウンするまでの待ち時間を小数値(秒)で指定します。
Settings.DelayBeforeMouseDown = 0.1
#DelayBeforeDragは、ソース位置でマウスが押された後の待機時間を小数値(秒)で指定します。
Settings.DelayBeforeDrag = 0.1
#DelayBeforeDropは、目標位置にマウスが上がるまでの待ち時間を数値(秒)で指定します。
Settings.DelayBeforeDrop = 0.1
#マウスの上下の間隔を秒単位で指定するには、0.nnnと指定します。
Settings.ClickDelay = 0.1
#キーを押している間の遅延を秒単位で指定します(0.nnn)。
Settings.TypeDelay = 0.1
#その他初期変数
Settings.InputFontMono = True
Settings.InputFontSize = 20
スクリプトの開始・終了時間をログに残すには
timeでログを表示すればok、
#ログ
print u"開始時刻:",datetime.datetime.today()
#ログ
print u"終了時刻:",datetime.datetime.today()
ファイル読み込み/書き込み
単純に読みだして、書き込むだけ
f = open('text.txt')
data1 = f.read() # ファイル終端まで全て読んだデータを返す
f.close()
print type(data1) # 文字列データ
lines1 = data1.split('\n') # 改行で区切る(改行文字そのものは戻り値のデータには含まれない)
print type(lines1)
for line in lines1:
print line
print
f = open('text.txt', 'w') # 書き込みモードで開く
f.write(data1) # 引数の文字列をファイルに書き込む
f.close() # ファイルを閉じる
アプリケーション名(ウインドウ名)で起動の有無を確認
openAppだと起動させるところからやらないといけないが、AppやswitchAppはすでに起動中のアプリのタイトルバーを完全一致/部分一致で探してフォーカスを切り替えることができる
また、App()は返り値にオブジェクトハンドルが戻るので、それを利用して、isRunning()やhasWindow()、getWindow()を使って画面上にそのウインドウが出ているか調べられる
以下は「名前を付けて保存」が出現しているかどうかの判定
chkApp = App(u"名前を付けて保存")
if chkApp.isRunning():
switchApp(u"名前を付けて保存")
wait(1)
click(Pattern("filename.png").targetOffset(74,-2))
type("l", KEY_SHIFT)
type("s", KEY_SHIFT)
type(Key.TAB)
else:
popup(u"名前を付けて保存がなかった",title = u"警告")
画像認識範囲と画像倍率
画像認識の範囲が広ければ広いほど、時間もスペックも必要になるのでRegionを使って探索範囲を狭めるのもいいと思う、あと、描画後の待機時間もデフォルト3秒だと長すぎて遅かったりするので変更推奨、
また、用意した判定用の画像の縮尺、対象の表示が自動で変わるような環境で(Androidシミュレーターなど)、再度サンプル画像を用意するのが面倒な場合は、AlwaysResizeを使えば判定用の画像をリサイズしてくれる
#認識範囲
area = Region(0,0,1920,1080)
#描画待機時間の指定、早くし過ぎると反応しなくなるので注意[default=3]
area.setAutoWaitTimeout(0.4)
#画像を取得したときよりサイズが小さい環境で実施した場合、マッチする画像を自動で縮小できる[範囲:1~0.01]
#1920*1080 -> 1280*720 のウインドウにリサイズした場合、0.66~0.67倍
#1920*1080 -> 1024*768 のウインドウにリサイズした場合、0.53~0.54倍
Settings.AlwaysResize = 1
#ファイル名の配列化と取り出し
連続で判定する場合や、同じ条件で識別する場合、画像名を配列に入れておけば楽になる
InputBoxImg = "InBox.png"
fnames = ["0.png","1.png","2.png","3.png","4.png"]
for ImgName in fnames :
click(ImgName) # 配列から1つずつ取り出してクリック
time.sleep(1) # 待ち
click(InputBoxImg) #フォームクリック
paste(ImgName) # ファイル名をペースト
print u"File Name : %s" % Imgname
判定座標を取る場合
画像判定したあと、座標情報が値としてほしい場合には以下のようにする
res = find("find.bmp") # 画像探索
Pos = resPos.getCenter() # getCenter() 中央座標 getTopLeft() 左上 getTopRight() 右上 getBottomLeft() 左下 getBottomRight() 右下
MousePos = Location(Pos.getX(),Pos.getY()) # 直前で実行したget???()からX,Y座標を取り出す
print Pos.getW() + "/" + Pos.getH() # 取得範囲の幅と高さ取得
マウスの直接移動
マウスの移動は、Locationを使って配列データにしてからじゃないと使えないので注意。
PX=1
PY=1
MousePos = Location(PX,PY) #直接座標指定
mouseMove(MousePos) # Locationで変換してから入れないと使えない
mouseDown(Button.LEFT) # Button.LEFT, Button.MIDDLE, Button.RIGHT
mouseUp(Button.LEFT) # Button.LEFT, Button.MIDDLE, Button.RIGHT
wheel(MousePos, WHEEL_DOWN , 1 ) # WHEEL_DOWN | WHEEL_UP
wheel(MousePos, WHEEL_UP , 1 ) # WHEEL_DOWN | WHEEL_UP
Drag&Dropをする場合
dragDropは、画像to画像で使うdragDrop("Drag.bmp","Drop.bmp")のような使い方のほかに、Drag座標とDrop座標を指定して実行する方法がある
その場合、座標はLocationで変換してから渡す必要がある。
#ドラック&ドロップはロケーションを指定して使う、アプリのタップ移動もこれ
StLocation = Location(1,1) #直接座標指定
EdLocation = Location(5,5) #直接座標指定
dragDrop(StLocation,EdLocation)
発見したらハイライト表示
画面上のどこを識別したのかデバックするのに便利、Sikulix 1.1.2から利用できるようになった機能、1.1.1はマニュアルにあったけどバグで動かなかった
#発見物ハイライト
find(Pattern(targetimage).similar(samefee).targetOffset(xx,yy)).highlight(1)
click(getLastMatch()) # findの情報をそのまま継承してクリックする場合
click(Pattern(targetimage).similar(samefee).targetOffset(xx,yy)) #再指定する場合
無限ループで判定を繰り返す
無限ループをやる場合は安全策として、ホットキーなんかで、whileを止めれるようにするか、何かしら割り込みをできるようにしておかないと悲惨な目に合う。
while infinitloop == 0:
try:
if area.exists("ok_btn.png",0.1):
click(Pattern("ok_btn.png").similar(0.90).targetOffset(82,27))
wait("check_bar.bmp")
click(getLastMatch())
click("push_btn.png")
time.sleep(2)
except Exception as e2:
raise e2
print 'System Error : ' + e2
さいごに
私がよく使っているSikuliXの命令と使い方を紹介してみました。怪しい書き方をしている箇所が何か所かありますが、見逃してください(苦笑
誰かの役に立つことを祈って、公開しておきます