snovaのブログ

主にプログラミングやデジタルコンテンツについて書きます。最近はPython, Flutter, VRに興味があります。

PyAutoGUIでGUI操作

イントロダクション

PCで繰り返し作業をすることがあって、単純作業なのでプログラムでしたいなと思いました。 (GUI操作をプログラムで実現させるとは矛盾したような話ですが) そこで、今回はPyAutoGuiを使ってGUI操作をしました。

インストール

公式のドキュメントを参照。

windows

Anaconda Promptを開いて、pip install pyautogui

macOS

pip install pyobjc-core
pip install pyobjc
pip install pyautogui

ターミナルで実行したら、セキュリティ関連の許可が必要です。

Linux OS

pip3 install python3-xlib
sudo apt-get install scrot
sudo apt-get install python3-tk
sudo apt-get install python3-dev
pip3 install pyautogui

もし、E: Unable to locate packageというエラーが出たら、cat /etc/apt/sources.listで、

deb http://archive.ubuntu.com/ubuntu bionic main
deb http://archive.ubuntu.com/ubuntu bionic-security main
deb http://archive.ubuntu.com/ubuntu bionic-updates main

になってることを確認して、vimで以下のように編集します。

deb http://archive.ubuntu.com/ubuntu bionic main universe
deb http://archive.ubuntu.com/ubuntu bionic-security main universe
deb http://archive.ubuntu.com/ubuntu bionic-updates main universe

あとは、sudo apt update

参考サイト : Ubuntu 18.04のapt installで”Unable to locate package”のエラーはsources.listを編集して解決できる

マウスの制御に使う座標

画面の座標は以下のようになっています。

 X increases ->
(0, 0)   ---------------- 
|                        | Y increases
|                        |     |
|   1920 x 1080 screen   |     V
|                        |    
|                        |
|                        |
--------------  (1919, 1079)

マウス操作におけるチートシート

コマンド 内容
pyautogui.moveTo(x, y, t) 絶対座標(x, y)にt秒かけて移動
pyautogui.moveRel(x, y) 現在位置から(x, y)へ相対移動
pyautogui.size() 画面サイズを取得
pyautogui.position() カーソルの現在位置を取得
pyautogui.onScreen(x, y) 座標(x, y)がスクリーン上にあるかをT or Fで出力
pyautogui.dragTo(x, y, t, button='left') 左ボタンを押しながら(x, y)へt秒かけて移動
pyautogui.dragRel(x, y, t, button='right') 右ボタンを押しながら現在位置から(x, y)へ相対移動
pyautogui.moveTo(x, y, t, pyautogui.easeInQuad) 始めはゆっくりで最後は高速に移動
pyautogui.click() 左クリック
pyautogui.click(x=100, y=200) (100, 200)に移動し、クリック
pyautogui.click(button='right') 右クリック
pyautogui.click(clicks=2, interval=0.25) 間隔0.25秒のダブルクリック
pyautogui.scroll(y) yだけマウススクロール。y>0で上へ、y<0で下へ。

テキスト入力に関するチートシート

コマンド 内容
pyautogui.typewrite('Hello world!') 'Hello world!' と入力
pyautogui.typewrite('Hello world!', interval=t) 1文字をt秒かけて、'Hello world!' と入力
pyautogui.press('enter') Enterキーを押す
pyautogui.keyDown('shift') shiftキーを押したままにする
pyautogui.keyUp('shift') shiftキーを離す
pyautogui.press(['left', 'left', 'left']) 連続でキーを押すときはリスト形式で
pyautogui.hotkey('ctrl', 'v') ホットキー。例はctrl + v
  • presskeyDownなどで使用できるキーは以下の通り
['\t', '\n', '\r', ' ', '!', '"', '#', '$', '%', '&', "'", '(',
')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', 
'a', 'b', 'c', 'd', 'e','f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
'accept', 'add', 'alt', 'altleft', 'altright', 'apps', 'backspace',
'browserback', 'browserfavorites', 'browserforward', 'browserhome',
'browserrefresh', 'browsersearch', 'browserstop', 'capslock', 'clear',
'convert', 'ctrl', 'ctrlleft', 'ctrlright', 'decimal', 'del', 'delete',
'divide', 'down', 'end', 'enter', 'esc', 'escape', 'execute', 
'f1', 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f2', 'f20', 'f21', 'f22', 'f23', 'f24', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
'final', 'fn', 'hanguel', 'hangul', 'hanja', 'help', 'home', 'insert', 'junja', 'kana', 'kanji', 'launchapp1', 'launchapp2', 'launchmail',
'launchmediaselect', 'left', 'modechange', 'multiply', 'nexttrack',
'nonconvert', 'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6',
'num7', 'num8', 'num9', 'numlock', 'pagedown', 'pageup', 'pause', 'pgdn',
'pgup', 'playpause', 'prevtrack', 'print', 'printscreen', 'prntscrn',
'prtsc', 'prtscr', 'return', 'right', 'scrolllock', 'select', 'separator', 'shift', 'shiftleft', 'shiftright', 'sleep', 'space', 'stop', 'subtract', 'tab', 'up', 'volumedown', 'volumemute', 'volumeup', 'win', 'winleft', 'winright', 'yen', 'command', 'option', 'optionleft', 'optionright']

スクリーンショット

コマンド 内容
pyautogui.screenshot('filename.png') 'filename.png' で保存
im = pyautogui.screenshot(region=(x, y, w, h)) 座標(x, y)から幅w、高さhの領域のスクリーンショットを取得
im.save(filename.png) imスクリーンショットを保存
loc_sc = pyautogui.locateOnScreen('filename.png') 'filename.png' の画像と同じ場所を探して(x, y, w, h)を出力。なければ、None。オプションは grayscale=True
pyautogui.locateCenterOnScreen('filename.png') locateOnScreen と同様だが、中央の座標を出力するのはこちら。
pyautogui.center(loc_sc) locateCenterOnScreenと同じ動作
locateAllOnScreen('filename.png') 画面に同じ画像が複数ある場合に使用。listやnumpy配列の形式に変換して使用。

サンプルプログラム

  • マウスを絶対座標(680, 240)に移動させて、左クリックするプログラム。
import pyautogui

pyautogui.moveTo(680, 240, 1)
pyautogui.click()
import pyautogui
import sys

def main():
    print('Press Ctrl-C to quit.')
    try:
        while True:
            x, y = pyautogui.position()
            positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4)
            print(positionStr, end='')
            print('\b' * len(positionStr), end='', flush=True)
    except KeyboardInterrupt:
        print('\n')

if __name__ == '__main__':
    main()
import pyautogui
import numpy as np

def main():
    # スクリーンショットを撮影
    pyautogui.screenshot(region=(100, 700, 300, 300)).save('filename.png')
    
    # 'filename.png'と同じ画像をスクリーン上から探し左上の座標と幅、高さを返す
    loc_sc = pyautogui.locateOnScreen('filename.png')
    print(loc_sc)

    # 'filename.png'と同じ画像をスクリーン上から探し、中央の座標を返す
    loc_sc_cen = pyautogui.locateCenterOnScreen('filename.png')
    print(loc_sc_cen)

    # 'filename.png'と同じ画像をスクリーン上から複数個探し、リストで返す
    loc_all = pyautogui.locateAllOnScreen('filename.png')
    list_loc = [list(i) for i in list(loc_all)] # タプル形式なのでリストに変換
    print(list_loc)

    # numpy配列に変換
    np_loc = np.array(list_loc)
    print(np_loc)

    # locateAllOnScreen は中央の座標を返す関数がないので自作
    np_loc_cen_x = np_loc[:, 0] + np_loc[:, 2]/2
    np_loc_cen_y = np_loc[:, 1] + np_loc[:, 3]/2
    np_loc_cen = np.array([np_loc_cen_x, np_loc_cen_y]).T
    print(np_loc_cen)

if __name__ == '__main__':
    main()

上記コードはgithubにあげておきます。

github.com

バグについて

内容

typewrite()を使ったとき、コロンを入力するとアスタリスクになってしまうバグが報告されている。
これはURLを入力するときに、致命的なバグとなり得る。

サンプル

import pyautogui

url_name = r'https://snova301.hatenablog.com/'
pyautogui.typewrite(url_name)

結果

https*//snova301.hatenablog.com/

解決方法

以下のファイルをエディタで開きます。

/anaconda3/lib/python3.7/site-packages/pyautogui/_pyautogui_x11.py
/home/[username]/anaconda3/lib/python3.7/site-packages/pyautogui/_pyautogui_x11.py

_keyDown関数を書き変えます。

def _keyDown(key):

    # 中略

    needsShift = pyautogui.isShiftCharacter(key)

    # これを追加 
    if key == '@': needsShift = False
    if key == '^': needsShift = False
    if key == ':': needsShift = False
    
    # 以下略

まとめ

PyAutoGuiを使うと数行でGUI操作ができます。 単純作業をするときは活用したいです。

参考文献

Google Play and the Google Play logo are trademarks of Google LLC.