Pythonで作るマウス・キーボードの自動操作ツール①

Python
  1. はじめに
  2. ツール(プログラム)一式のダウンロード
    1. Pythonで作るマウス・キーボードの自動操作ツール_プログラム一式_v1.0.0
    2. Pythonで作るマウス・キーボードの自動操作ツール_ツール一式_v1.0.0
  3. ツールの実行方法
    1. Pythonのプログラムから実行する方法
      1. 実行環境に必要なパッケージ
      2. 実行環境の構築
      3. ツールの使い方
    2. EXEファイルから実行する方法
  4. 動作設定ファイルの作成方法
  5. 座標の確認方法
  6. 起動させたいアプリケーションの設定
  7. ログの出力設定
  8. プログラムの説明
    1. Pythonで作るマウス・キーボードの自動操作ツール_プログラム一式_v1.0.0
    2. 作成した関数の一覧と概要
    3. ライブラリ読み込み箇所の説明とコード
      1. 説明
      2. コード
    4. 設定ファイル(config.ini)の読み込む処理箇所の説明とコード
      1. 説明
      2. コード
    5. ダイアログ(tkinter)を使用するための設定箇所の説明とコード
      1. 説明
      2. コード
    6. 動作設定ファイルのパスを保存する変数の定義箇所の説明とコード
      1. 説明
      2. コード
    7. ログ設定(logging.conf)を読み込む処理とロガー設定箇所の説明とコード
      1. 説明
      2. コード
    8. main()の説明とコード
      1. 説明
      2. コード
    9. read_action_setting_file()の説明とコード
      1. 説明
      2. コード
    10. record_pid()の説明とコード
      1. 説明
      2. コード
    11. execute_action(action, option1, option2, option3)の説明とコード
      1. 説明
      2. コード
    12. execute_if_action_setting_file_selected()の説明とコード
      1. 説明
      2. コード
    13. exit_button()の説明とコード
      1. 説明
      2. コード
    14. select_action_setting_file()の説明とコード
      1. 説明
      2. コード
    15. execute_action_list()の説明とコード
      1. 説明
      2. コード
    16. forced_close_window()の説明とコード
      1. 説明
      2. コード
    17. terminate_process()の説明とコード
      1. 説明
      2. コード
    18. execute_and_show_forced_close_window()の説明とコード
      1. 説明
      2. コード
    19. main_window()の説明とコード
      1. 説明
    20. main()の説明とコード
      1. 説明
      2. コード
  9. 次回、試したいこと
  10. プログラムの全コード

はじめに

Google Colaboratoryを使用して機械学習のプログラムを作成しています。機械学習のモデル作成中にGoogle colaboratoryとのセッション切れると、モデル作成を最初からやり直す必要があります。セッションが切れないようにマウスの操作を自動化して定期的にクリックとスクロールを行うプログラムを作成し、セッション切れに対応していました。

ITエンジニアではない友人との会話で、たまたまそのプログラムの話が挙がり、友人が使ってみたい、とのことだったので、友人にそのプログラムを提供しました。友人にマウス・キーボードの自動操作ツールを提供して使ってもらった際、以下のフィードバックをもらいました。

  • マウス・キーボードの動作設定を簡単にしてほしい。
    マウス・キーボードの自動操作ルールを作成するのに、プログラムを編集するのはハードルが少し高い。マウス・キーボードの自動操作ルールの設定はファイル、できればエクセルファイルで扱えるようにしてほしい。
  • 自動操作の途中で止めれるようにしてほしい。
    自動操作の設定を間違えても自動操作が終了するまで待つ必要がある。自動操作を途中で止めれるようにしてほしい。
  • 自動操作の実行方法を簡単にしてほしい。
    コマンドプロンプトからのプログラム起動と自動操作の動作結果の確認は、一般人からするとハードルが高い。ツール化してほしい。

ウインドウアプリを作ったことはなく、いい勉強になると思い、Pythonのtkinterを用いて自動操作ツールのウインドウアプリを作成しました。まだまだ改善したいところはありますが、作成したツール(プログラム)を公開します。

<ツールのメインウインドウ>

ツール(プログラム)一式のダウンロード

  • Pythonのプログラムと設定ファイル、検証用ファイルの一式を下記からダウンロードできます。
  • 自身で実行環境の構築をできる方、プログラムをカスタマイズして使用したい方は、プログラムを下記のリンクからダウンロードしてください。
  • ツールをすぐにでも実行したい方は、ツールを下記のリンクからダウンロードしてください。
  • ダウンロードファイルの中身
    プログラム一式とツール一式でダウンロードする中身の違いは、プログラムが入っているか、exe化したプログラムが入っているか、の違いのみです。
ファイル名 内容
config.ini アプリを実行するためにアプリのフルパスを記載する。
LICENSE ライセンスはMIT。再配布、改変など自由に行えます。
logging.conf ログ出力の設定。
mouse_keyboard_control.py
mouse_keyboard_control.exe
ツールを実行するためのプログラムの本体
exe化したプログラム
検証用動作設定ファイル_アプリ起動.xlsx 検証用ファイル。不要になったら削除しても問題ありません。
検証用動作設定ファイル_マウス移動.xlsx 検証用ファイル。不要になったら削除しても問題ありません。

ツールの実行方法

ツールの実行方法は下記の2つとなります。

  1. Pythonのプログラムから実行する方法
    Pythonの実行環境を自身で用意できる方や、プログラムをカスタマイズして実行したい方は、後述する手順を参考にして実行してみてください。
  2. EXEファイルから実行する方法
    掲載しているPythonコードをEXEファイルに変換したファイルを用意しました。Pythonの実行環境を用意しなくても、ツールを実行できます。

Pythonのプログラムから実行する方法

実行環境に必要なパッケージ

実行に必要な主なパッケージを下記に記載しました。

モジュール名 バージョン 概要
Python 3.10.8 ツールで使用するPython
PyAutoGUI 0.9.54 マウス・キーボードを制御するためのモジュール
openpyxl 3.1.2 エクセルを扱うためのモジュール
psutil 5.9.5 システムリソースにアクセスするためのモジュール

実行環境の構築

実行に必要なプログラムと設定ファイルをダウンロードし、任意の場所に展開してください。
本手順では、「C:\mouse_keyboard_control」に展開した場合の手順を記載しています。また、Pythonはインストールされている前提としています。

  1. コマンドプロンプトを起動し、展開したフォルダに移動します。
    > cd C:\mouse_keyboard_control
  2. Pythonの実行環境を作成します。
    > python -venv env
  3. Pythonの実行環境をアクティブにします。
    > .\env\Scripts\activate.bat
  4. 実行するとコマンドプロンプトの表示が下記のように「(env)」が付きます。
    (env) C:\mouse_keyboard_control > 
  5. Pythonの実行環境にツールを実行するためのライブラリの更新とインストールを行います。
    pipのアップグレードを行います。

    (env) > python.exe -m pip install --upgrade pip

    PyAutoGUIのインストールを行います。

    (env) > pip install pyautogui

    openpyxlのインストールを行います。

    (env) > pip install openpyxl

    psutilのインストールを行います。

    (env) > pip install psutil
  6. ツールを起動します。
    (env) > python mouse_keyboard_control.py

    上記の実行するとツールが起動します。

    以上で実行環境の構築は完了です。

ツールの使い方

  1. ツールを起動させてください。
  2. 「ファイルの選択」をクリックするとファイル選択ダイアログが表示されます。動作設定ファイルを選択していください。本手順では「検証用動作設定ファイル_マウス移動.xlsx」を選択して「開く」をクリックします。
  3. ツールの「選択したファイル」に選択した動作設定ファイルのパスが表示されます。
  4. 「実行」をクリックすると、マウスカーソルが下記のように動きます。
    XY座標(300, 300)→(5秒待ち)
    →XY座標(300, 600)→(5秒待ち)
    →XY座標(600, 600)→(5秒待ち)
    →XY座標(600, 300)→(5秒待ち)
    →XY座標(300, 300)
  5. 自動実行中に「強制終了」をクリックすると実行を強制終了します。
    「強制終了」は常に最前面に表示されます。
  6. マウス・キーボードの自動操作が終了すると下記のダイアログが表示されます。

EXEファイルから実行する方法

環境の準備などは必要ありません。ダウンロードしたzipファイルを任意の場所に展開し、「mouse_keyboard_control.exe」を実行してください。

動作設定ファイルの作成方法

  • 動作設定ファイルは「.xlsx」形式のエクセルファイルで作成してください。
  • 動作設定ファイルのファイル名は任意のファイル名で作成できます。
  • シート名は「動作設定」としてください。
  • 「動作設定」シートに記入するルールは下記となります。
    記入内容 記入例
    A列 マウスやキーボードに実行させる動作 move
    B列 A列で設定した動作の引数1 400
    C列 A列で設定した動作の引数2 300
    D列 A列で設定した動作の引数3
    (未使用。バージョンアップしたら使用する予定)
    E列 コメント(実行時に参照はしないので、記入内容は自由) マウスをX座標400、Y座標300に移動する
  • A列で設定できる動作一覧は下記となります。
    現状では、PyAutoGUIのすべての関数を利用することはできませんが、本ツールを改良する際に使用できる関数を増やしていく予定です。

    設定名 内容
    move 引数1(B列)と引数2(C列)で指定した座標にマウスカーソルを移動する。
    click 引数1(B列)と引数2(C列)で指定した座標を左クリックする。
    rightClick 引数1(B列)と引数2(C列)で指定した座標を右クリックする。
    doubleClick 引数1(B列)と引数2(C列)で指定した座標をダブルクリックする。
    press 引数1(B列)で指定したキーを押下する。※詳細は後述
    scroll 引数1(B列)でした数値でスクロールする。(5の場合、上に5回スクロールする。-5の場合、下に5回スクロールする)
    sleep 引数1(B列)でした秒数の間、スリープ(待ち)を発生する。
    input 引数1(B列)で指定した文字列を入力する。
    アプリ名 config.iniで設定したアプリを起動する。引数1(B列)で指定した内容を引数にすることができる。
  • 「press」で使用できるキーの一覧
    ‘\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’
  • 動作設定ファイルのサンプル

座標の確認方法

  • 横がX座標、縦がY座標となります。
  • 画面の左上が(0, 0)となります。
  • 画面サイズが1920×1080の場合、画面の右下が(1920, 1080)となります。
  • 座標を指定するときは、ツールを動作させながら調整してください。
  • 画面サイズが1920×1080の場合の座標イメージ

起動させたいアプリケーションの設定

  • 設定ファイル:config.ini
  • ダウンロード一式に含まれています。
  • 起動させたいアプリのフルパスを設定します。
  • Webブラウザのedgeを起動させたい場合は、config.iniに下記のように設定します。
    config.iniをメモ帳で開き、下記の内容を記載します。

    [application_path]
    edge = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
  • config.iniのサンプル
    edge、word、excel、powerpoint、メモ帳を起動させる場合の設定は下記となります。

    [application_path]
    edge = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
    word = C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
    excel = C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE
    powerpoint = C:\Program Files\Microsoft Office\root\Office16\POWERPNT.EXE
    memo = C:\WINDOWS\system32\notepad.exe

ログの出力設定

  • 設定ファイル:logging.conf
  • ダウンロード一式に含まれています。
  • 出力するログを保存するファイル、出力内容などを設定します。
  • 基本的には設定を変更する必要はありません。
  • 時刻のあとの数値は、処理を実行しているmouse_keyboard_control.pyの行数です。
  • logging.confのサンプル
    # config: utf-8
    [loggers]
    keys = root
    
    [handlers]
    keys = consoleHandler, fileHandler
    
    [logger_root]
    handlers = consoleHandler, fileHandler
    level = DEBUG
    
    [handler_consoleHandler]
    class = StreamHandler
    level = DEBUG
    formatter = logFormatter
    args = (sys.stdout,)
    
    [handler_fileHandler]
    class = FileHandler
    level = DEBUG
    formatter = logFormatter
    args = ('./mouse_keyboard_control.log',)
    
    [formatters]
    keys = logFormatter
    
    [formatter_logFormatter]
    class = logging.Formatter
    format = %(asctime)s:%(lineno)d:%(levelname)s:%(message)s
  • ログのサンプル
    2023-08-19 15:48:51,081:62:INFO:マウス・キーボードの自動操作を開始する
    2023-08-19 15:49:41,533:72:INFO:選択した動作設定ファイル:C:/mouse_keyboard_control/検証用動作設定ファイル_マウス移動.xlsx
    2023-08-19 15:49:41,575:113:INFO:X座標:300, Y座標:300, move
    2023-08-19 15:49:41,700:129:INFO:5, sleep
    2023-08-19 15:49:46,727:113:INFO:X座標:300, Y座標:600, move
    2023-08-19 15:49:46,849:129:INFO:5, sleep
    2023-08-19 15:49:51,866:113:INFO:X座標:600, Y座標:600, move
    2023-08-19 15:49:51,981:129:INFO:5, sleep
    2023-08-19 15:49:57,005:113:INFO:X座標:600, Y座標:300, move
    2023-08-19 15:49:57,118:129:INFO:5, sleep
    2023-08-19 15:50:02,135:113:INFO:X座標:300, Y座標:300, move
    2023-08-19 15:50:05,554:242:INFO:マウス・キーボードの自動操作を終了する
    2023-08-19 15:54:34,007:62:INFO:マウス・キーボードの自動操作を開始する
    2023-08-19 15:54:39,971:72:INFO:選択した動作設定ファイル:C:/mouse_keyboard_control/検証用動作設定ファイル_シート名NG.xlsx
    2023-08-19 15:54:40,004:82:WARNING:動作設定シートが見つかりませんでした
    2023-08-19 15:54:40,004:83:WARNING:'Worksheet 動作設定 does not exist.'
    2023-08-19 15:55:14,626:242:INFO:マウス・キーボードの自動操作を終了する

プログラムの説明

プログラムは下記URLからダウンロードすることができます。

作成した関数の一覧と概要

行数 概要 関数名 引数 戻値
57 メイン処理の開始位置 main()
71 動作設定ファイルを読み込む read_action_setting_file() 読み込んだ動作設定ファイルのシート内容
98 本ツールを実行したときのプロセスIDを記録する。(強制終了の際、Killする対象のプロセスIDを記録する) record_pid()
108 引数に応じたpyautoguiの関数を実行する execute_action(action, option1, option2, option3) 動作,オプション1,オプション2,オプション3
157 動作設定ファイルを選択したかを判定する execute_if_action_setting_file_selected()
170 終了ボタンを押したときの処理 exit_button()
187 ファイル選択ダイアログを表示して、動作設定ファイルを選択する select_action_setting_file()
200 動作設定シートからactionとoption1~3を抽出し、execute_action関数を実行する execute_action_list()
224 強制終了のウインドウを表示する forced_close_window()
240 マウス・キーボードの自動操作のプロセスを終了する terminate_process
258 動作設定ファイルを読み込んで実行するのと、強制終了ウインドウを表示する execute_and_show_forced_close_window()
270 動作設定ファイルを選択するためのウインドウを表示する main_window()

プログラムの詳細について、下記にまとめました。

ライブラリ読み込み箇所の説明とコード

説明

  • 読み込んでいるライブラリの概要は下記となります。
    • マウス・キーボードを操作するライブラリ
    • Excelファイルを扱うためのライブラリ
    • プロセス/システムの利用状況を取得するライブラリ
    • ダイアログを制御するライブラリ
    • スレッド処理のライブラリ
    • 標準ライブラリから設定、ログ、時間、サブプロセス、システムに関連するライブラリ

コード

#マウス・キーボードを操作するライブラリ
importpyautogui
importpyperclip

#Excelファイルを扱うためのライブラリ
importopenpyxl

#プロセス/システムの利用状況を取得するライブラリ
importpsutil

#標準ライブラリ
importos
importsys
importtime
importsubprocess
importconfigparser
importlogging.config

#ダイアログを制御するライブラリ
importtkinter
fromtkinterimportmessagebox
fromtkinterimportfiledialog
fromtkinterimportStringVar

#スレッド処理のライブラリを読み込む
importthreading

設定ファイル(config.ini)の読み込む処理箇所の説明とコード

説明

  • 設定ファイルを読み込むためにconfigparserを使用しています。

コード

# configの読み込み
config = configparser.ConfigParser()
config.read('./config.ini')

ダイアログ(tkinter)を使用するための設定箇所の説明とコード

説明

  • ダイアログの表示にはtkinterライブラリを使用します。
  • GUIの元(root)となるウインドウ作成とタイトルを設定します。

コード

# tkinterの設定
root = tkinter.Tk()
root.title('マウス・キーボードの自動操作')

動作設定ファイルのパスを保存する変数の定義箇所の説明とコード

説明

  • 動作設定ファイルを選択する関数で選択した動作設定ファイルのパスを、異なる関数で利用できるようグローバル変数として定義します。

コード

# 動作設定ファイルのパスを保存する変数
action_setting_file = tkinter.StringVar()

ログ設定(logging.conf)を読み込む処理とロガー設定箇所の説明とコード

説明

  • loggingを使用して、ログ設定の読み込みとロガーの設定を行います。

コード

# ログ設定を読み込み、ロガー設定する
logging.config.fileConfig('./logging.conf')
logger = logging.getLogger()

main()の説明とコード

説明

  • 本ツールを起動した際に実行される処理を記載しています。
  • main関数から本ツールの動作に必要な各関数を呼び出しています。

コード

def main():
    # プロセスIDを記録する
    record_pid()

    # 実行開始のログを記録する
    logger.info('マウス・キーボードの自動操作を開始する')

    # 動作設定ファイルを選択するウインドウを表示する
    main_window()

read_action_setting_file()の説明とコード

説明

  • 選択した動作設定設定ファイルを読み込み、動作設定ファイルに「動作設定」シートの有無をチェックします。
  • 「動作設定」シートがなければ、下記のダイアログを表示して本ツールを終了します。
  • エラーダイアログは「messagebox.showerror」を用いています。

  • エクセルファイルの開くために「openpyxl.load_workbook」を用いています。
  • 「openpyxl.load_workbook」の引数
    • 第1引数
      エクセルファイルのパスを指定する。「action_setting_file」に保存されているパス情報をget()で取得して指定している。
    • 第2引数
      「read_only=True」を設定し、読み取り専用でエクセルファイルを開いている。読み取り専用の場合、システムのエクセルファイルを開くための使用メモリが下がる。
    • 第3引数
      「data_only=True」を設定し、セルに設定されている数式ではなく、セルの値を取得する。

コード

def read_action_setting_file():
    logger.info('選択した動作設定ファイル:{0}'.format(action_setting_file.get()))

    # 選択された動作設定ファイル(excelファイル)を開く
    # excelファイルのブックを取得する
    book = openpyxl.load_workbook(action_setting_file.get(), read_only=True, data_only=True)

    # [動作設定]シートを選択する
    try:
        sheet = book['動作設定']
    except Exception as e:
        logger.warning('動作設定シートが見つかりませんでした')
        logger.warning('{0}'.format(e))
        messagebox.showerror('マウス・キーボードの自動操作',
                             '選択した動作設定ファイルから動作設定シートが見つかりませんでした。\n動作設定ファイルを選択してください。')
        # 終了プロセスを実行する
        terminate_process()

        # プログラムを終了する
        sys.exit()

    return sheet

record_pid()の説明とコード

説明

  • 本ツールを実行したときのプロセスIDを記録します。
  • 記録したプロセスIDは、強制終了あるいは自動実行を終えたときに、本ツールをKill(終了)する際に使用します。
  • プロセスIDは「pid_mouse_keyboard_control.txt」に保存します。

コード

def record_pid():
    pid = os.getpid()
    with open('pid_mouse_keyboard_control.txt', 'w') as f:
        f.write(str(pid))

execute_action(action, option1, option2, option3)の説明とコード

説明

  • 引数に応じたpyautoguiの関数を実行します。
    • 第1引数
      マウス、キーボードの動作を指定します。
    • 第2引数
      第1引数の動作に渡す数値あるいは文字列を指定します。
    • 第3引数
      第1引数の動作に渡す数値あるいは文字列を指定します。
    • 第4引数
      未使用です。本ツールのバージョンアップの際に使用する予定です。
  • 事前に実行アプリに[‘edge’, ‘word’, ‘excel’, ‘powerpoint’, ‘memo’]を登録しています。
  • 実行したいアプリを増やしたい場合は、config.iniに実行したいアプリのパスを記載して、下記の箇所を更新します。
for act in ['edge', 'word', 'excel', 'powerpoint', 'memo']:
  • 日本語の文字入力をPyAutoGuiのinput関数で実施するとエラーとなります。
  • 文字入力の処理は「入力する文字をクリップボードにコピーする」と「Ctrl+V」の2つの処理を実施しています。
                elif action == 'input':
                    pyperclip.copy(option1)
                    pyautogui.hotkey('ctrl', 'v')
  • アプリ実行は「subprocess」で行っています。
  • Popenメソッドを使用することで、実行したアプリの終了を待たずに処理を進めます。
               subprocess.Popen([app_path, option1])
  • ここで下記のようにcallメソッドを使用した場合は、実行したアプリの終了を待つことになり、処理が先に進みません。
                subprocess.call([app_path, option1])

コード

def execute_action(action, option1, option2, option3):
    # option1とoption2を必要とするclick,move,rightClick,doubleClickを実行する処理
    for act in ['click', 'move', 'rightClick', 'doubleClick']:
        if action == act:
            if option1 is not None and option2 is not None:
                logger.info('X座標:{0}, Y座標:{1}, {2}'.format(option1, option2, action))
                if action == 'click':
                    pyautogui.click(option1, option2)
                elif action == 'move':
                    pyautogui.moveTo(option1, option2)
                elif action == 'rightClick':
                    pyautogui.rightClick(option1, option2)
                elif action == 'doubleClick':
                    pyautogui.doubleClick(option1, option2)
            else:
                logger.warning('{0}, optionの設定に誤りがあります'.format(action))

    # option1を必要とするpress,scroll,sleep,inputを実行する
    for act in ['press', 'scroll', 'sleep', 'input']:
        if action == act:
            if option1 is not None and option2 is None:
                logger.info('{0}, {1}'.format(option1, action))
                if action == 'press':
                    pyautogui.press(option1)
                elif action == 'scroll':
                    pyautogui.scroll(option1)
                elif action == 'sleep':
                    time.sleep(option1)
                elif action == 'input':
                    pyperclip.copy(option1)
                    pyautogui.hotkey('ctrl', 'v')
            else:
                logger.warning('{0}, optionの設定に誤りがあります'.format(action))

    # アプリを実行する
    for act in ['edge', 'word', 'excel', 'powerpoint', 'memo']:
        if action == act:
            app_path = config['application_path'][act]
            if option1 is not None:
                logger.info('{0}, {1}'.format(action, option1))
                subprocess.Popen([app_path, option1])
            else:
                logger.info('{0}'.format(action))
                subprocess.Popen(app_path)

execute_if_action_setting_file_selected()の説明とコード

説明

  • 動作設定ファイルを選択(存在)したかを判定します。選択(存在)の判定には「os.path.isfile」を用いています。
  • 動作設定ファイルを選択(存在)した場合は、動作設定の実行の処理に移ります。
  • 動作設定ファイルを選択(存在)していない場合は、下記のエラーダイアログを表示します。
  • エラーダイアログは「messagebox.showerror」を用いています。

コード

def execute_if_action_setting_file_selected():
    is_file = os.path.isfile(action_setting_file.get())
    if is_file:
        execute_and_show_forced_close_window()

    else:
        # 終了メッセージを表示する
        messagebox.showerror('マウス・キーボードの自動操作', '動作設定ファイルを選択してください。')

exit_button()の説明とコード

説明

  • メインウインドウの終了ボタンを押したときの処理を記載しています。
  • 終了ボタンを押すと、ウインドウを閉じてプロセスIDのファイルを削除します。

コード

def exit_button():
    # rootウインドウを閉じる
    root.destroy()

    # マウス・キーボードの自動操作のプロセスIDを保存しているファイルを削除する
    os.remove('./pid_mouse_keyboard_control.txt')

    # 終了ボタンを選択したことをログに記録する
    logger.info('終了ボタンを選択しました')

    # プログラムを終了する
    sys.exit()

select_action_setting_file()の説明とコード

説明

  • ファイル選択ダイアログを表示して、選択されたファイルのパスをメインウインドウに表示します。

コード

def select_action_setting_file():
    file_select = filedialog.askopenfilename(title='動作設定ファイルを選択してください',
                                             filetypes=[('EXCEL file', '.xlsx')],
                                             initialdir='./')
    action_setting_file.set(file_select)

    # 選択した動作設定ファイルのパスをメインウインドウに表示する
    select_file_path['text'] = file_select

execute_action_list()の説明とコード

説明

  • 動作設定シートからactionとoption1~3を抽出し、execute_action関数を実行します。
  • 実行が終了したら、下記のインフォメーションダイアログを表示して本ツールを終了します。

  • 下記のコードについて説明します。
    # 動作設定シートに記載されている内容を実行する
    action_list = 2
    while action_list <= sheet.max_row:
        action = sheet.cell(row=action_list, column=1).value
        option1 = sheet.cell(row=action_list, column=2).value
        option2 = sheet.cell(row=action_list, column=3).value
        option3 = sheet.cell(row=action_list, column=4).value
        execute_action(action, option1, option2, option3)

        action_list += 1
  • 「sheet.max_row」で動作設定ファイルの動作設定シートに記載されている動作設定の行数を取得します。
  • 下記の動作設定ファイルだと「sheet.max_row」は「10」になります。

  • 1行目はカラム名となるので、処理対象から外すために「action_list = 2」としています。
  • 「action_list = 2」のとき
    • 「action = sheet.cell(row=action_list, column=1).value」の「action」には「move」が入ります。
    • 「option1 = sheet.cell(row=action_list, column=2).value」の「option1」には「300」が入ります。
    • 「option2 = sheet.cell(row=action_list, column=3).value」の「option2」には「300」が入ります。
    • 「option3 = sheet.cell(row=action_list, column=4).value」の「option3」には「null」が入ります。
    • 「execute_action(action, option1, option2, option3)」は「execute_action(move, 300, 300, )」となります。
      ※「execute_action」は引数に応じてpyautoguiの関数を実行する関数です。
  • 「action_list」が「10」になるまで繰り返します。

コード

def execute_action_list():
    sheet = read_action_setting_file()

    # 動作設定シートに記載されている内容を実行する
    action_list = 2
    while action_list <= sheet.max_row:
        action = sheet.cell(row=action_list, column=1).value
        option1 = sheet.cell(row=action_list, column=2).value
        option2 = sheet.cell(row=action_list, column=3).value
        option3 = sheet.cell(row=action_list, column=4).value
        execute_action(action, option1, option2, option3)

        action_list += 1

    # 終了メッセージを表示する
    messagebox.showinfo('マウス・キーボードの自動操作', 'マウス・キーボードの自動操作が終了しました')

    # 終了プロセスを実行する
    terminate_process()

forced_close_window()の説明とコード

説明

  • rootウインドウのサブウインドウとして、強制終了のウインドウを表示します。

  • 下記のコードで、強制終了のウインドウは常に最前面に表示します。
    sub_window.attributes('-topmost', True)

コード

def forced_close_window():
    # サブウインドウの作成
    sub_window = tkinter.Toplevel(root)
    sub_window.attributes('-topmost', True)

    # 強制終了ボタンを設置する
    button = tkinter.Button(sub_window,
                            text='強制終了',
                            font=('meiryo', 15),
                            command=terminate_process)
    button.pack(padx=50, pady=25)

terminate_process()の説明とコード

説明

  • マウス・キーボードの自動操作のプロセスを終了します。
  • プロセス終了にはpsutilモジュールを利用しています。

コード

def terminate_process():
    # 実行終了のログを記録する
    logger.info('マウス・キーボードの自動操作を終了する')

    with open('./pid_mouse_keyboard_control.txt') as f:
        pid = f.read()

    # マウス・キーボードの自動操作のプロセスIDを保存しているファイルを削除する
    os.remove('./pid_mouse_keyboard_control.txt')

    # プロセスを止める
    p = psutil.Process(int(pid))
    p.terminate()

execute_and_show_forced_close_window()の説明とコード

説明

  • 動作設定ファイルを読み込んで実行するのと、強制終了ウインドウを表示します。
  • 動作設定ファイルに従い行動を実行しつつ、強制ウインドウを表示するために「execute_action_list」はスレッドで呼び出しています。

コード

def execute_and_show_forced_close_window():
    # 動作設定ファイルに登録されているアクションを実行する
    thread = threading.Thread(target=execute_action_list)
    thread.start()

    # 強制終了のウインドウを表示する
    forced_close_window()

main_window()の説明とコード

説明

  • 動作設定ファイルを選択するためのウインドウを表示します。
  • ウインドウに表示する文言やボタンの挙動について定義しています。
def main_window():
    # 文言1
    label1 = tkinter.Label(root,
                           text='「ファイルの選択」をクリックし、動作設定ファイルを選択してください。',
                           font=('meiryo', 10))
    label1.pack(padx=10, pady=10, side=tkinter.TOP, anchor=tkinter.W)

    # ファイルの選択ボタンを配置する
    select_button = tkinter.Button(text='ファイルの選択', command=select_action_setting_file)
    select_button.pack(padx=10, pady=10, ipadx=30, side=tkinter.TOP, anchor=tkinter.W)

    # 文言2
    label2 = tkinter.Label(root, text='選択したファイル')
    label2.pack(padx=10, pady=10, side=tkinter.TOP, anchor=tkinter.W)

    # 選択したファイルのパスを表示
    global select_file_path
    select_file_path = tkinter.Label(root, text='動作設定ファイルは未選択です。', anchor=tkinter.W, relief='groove', bd=1)
    select_file_path.pack(padx=10, side=tkinter.TOP, fill=tkinter.X, anchor=tkinter.W)

    # フレーム
    frame = tkinter.LabelFrame(root, text='', relief=tkinter.FLAT)
    frame.pack(padx=10, pady=5, side=tkinter.RIGHT)

    # 実行ボタンを配置する
    button_read = tkinter.Button(frame, text='実行', command=execute_if_action_setting_file_selected)
    button_read.pack(padx=10, pady=10, ipadx=14, side=tkinter.LEFT, anchor=tkinter.W)

    # 終了ボタンを配置する
    button_exit = tkinter.Button(frame, text='終了', command=exit_button)
    button_exit.pack(padx=10, pady=10, ipadx=14, side=tkinter.LEFT, anchor=tkinter.W)

    # ウインドウの「×」をクリックした時の動作を設定する
    root.protocol('WM_DELETE_WINDOW', exit_button)

    # ウインドウを維持する
    root.mainloop()

main()の説明とコード

説明

  • 本ツールを起動した際に実行される処理を記載しています。
  • main関数から本ツールの動作に必要な各関数を呼び出しています。

コード

if __name__ == '__main__':
    main()

次回、試したいこと

この手の自動操作ツールは、世の中に沢山あります。
自動操作だけを目的にするのであれば、自作する必要はありません。ただ今回、自動操作ツールを作成することでGUIアプリの作成方法やexe化するための手順を学ぶことができました。また、自分が作成したツールを使ってもらい、フィードバックを受けれたのは、とても刺激になり、作成していて楽しかったです。

次回のバージョンでは、下記のいずれかを含めたいと思います。

  • PyAutoGUIの他の関数を利用できるようにする
  • マウスの座標を表示するGUIを追加する
  • メニューバーを設けて、環境設定・ログ設定・マニュアルなどを選択できるようにする

プログラムの全コード

# Excelで作成した動作設定ファイルに記載されたマウス・キーボードの操作を実行するプログラムです
# 実行方法
#  python mouse_keyboard_control.py
# 動作設定ファイル
#  ファイル形式:Excelファイル(.xlsx)
#  ファイル名:任意
#  シート名:動作設定
#  1列目:action,2列目:option1,3列目:option2,4列目:option3,5列目:comment
#

# マウス・キーボードを操作するライブラリ
import pyautogui
import pyperclip

# Excelファイルを扱うためのライブラリ
import openpyxl

# プロセス/システムの利用状況を取得するライブラリ
import psutil

# 標準ライブラリ
import os
import sys
import time
import subprocess
import configparser
import logging.config

# ダイアログを制御するライブラリ
import tkinter
from tkinter import messagebox
from tkinter import filedialog
from tkinter import StringVar

# スレッド処理のライブラリを読み込む
import threading

# configの読み込み
config = configparser.ConfigParser()
config.read('./config.ini')

# tkinterの設定
root = tkinter.Tk()
root.title('マウス・キーボードの自動操作')

# 動作設定ファイルのパスを保存する変数
action_setting_file = StringVar()

# ログ設定を読み込み、ロガー設定する
logging.config.fileConfig('./logging.conf')
logger = logging.getLogger()


# メイン処理
# 引数:無し
# 戻値:無し
def main():
    # プロセスIDを記録する
    record_pid()

    # 実行開始のログを記録する
    logger.info('マウス・キーボードの自動操作を開始する')

    # 動作設定ファイルを選択するウインドウを表示する
    main_window()


# 動作設定ファイルを読み込む
# 引数:無
# 戻値:読み込んだシート(sheet)
def read_action_setting_file():
    logger.info('選択した動作設定ファイル:{0}'.format(action_setting_file.get()))

    # 選択された動作設定ファイル(excelファイル)を開く
    # excelファイルのブックを取得する
    book = openpyxl.load_workbook(action_setting_file.get(), read_only=True, data_only=True)

    # [動作設定]シートを選択する
    try:
        sheet = book['動作設定']
    except Exception as e:
        logger.warning('動作設定シートが見つかりませんでした')
        logger.warning('{0}'.format(e))
        messagebox.showerror('マウス・キーボードの自動操作',
                             '選択した動作設定ファイルから動作設定シートが見つかりませんでした。\n動作設定ファイルを選択してください。')
        # 終了プロセスを実行する
        terminate_process()

        # プログラムを終了する
        sys.exit()

    return sheet


# 本ツールを実行したときのプロセスIDを記録する
# 強制終了の際、Killする対象のプロセスIDを記録する
# 引数:無し
# 戻値:無し
def record_pid():
    pid = os.getpid()
    with open('pid_mouse_keyboard_control.txt', 'w') as f:
        f.write(str(pid))


# 引数に応じたpyautoguiの関数を実行する
# 引数:action(str),option1(int/str),option2(int/str),option3(int/str)
# 戻値:無し
def execute_action(action, option1, option2, option3):
    # option1とoption2を必要とするclick,move,rightClick,doubleClickを実行する処理
    for act in ['click', 'move', 'rightClick', 'doubleClick']:
        if action == act:
            if option1 is not None and option2 is not None:
                logger.info('X座標:{0}, Y座標:{1}, {2}'.format(option1, option2, action))
                if action == 'click':
                    pyautogui.click(option1, option2)
                elif action == 'move':
                    pyautogui.moveTo(option1, option2)
                elif action == 'rightClick':
                    pyautogui.rightClick(option1, option2)
                elif action == 'doubleClick':
                    pyautogui.doubleClick(option1, option2)
            else:
                logger.warning('{0}, optionの設定に誤りがあります'.format(action))

    # option1を必要とするpress,scroll,sleep,inputを実行する
    for act in ['press', 'scroll', 'sleep', 'input']:
        if action == act:
            if option1 is not None and option2 is None:
                logger.info('{0}, {1}'.format(option1, action))
                if action == 'press':
                    pyautogui.press(option1)
                elif action == 'scroll':
                    pyautogui.scroll(option1)
                elif action == 'sleep':
                    time.sleep(option1)
                elif action == 'input':
                    pyperclip.copy(option1)
                    pyautogui.hotkey('ctrl', 'v')
            else:
                logger.warning('{0}, optionの設定に誤りがあります'.format(action))

    # アプリを実行する
    for act in ['edge', 'word', 'excel', 'powerpoint', 'memo']:
        if action == act:
            app_path = config['application_path'][act]
            if option1 is not None:
                logger.info('{0}, {1}'.format(action, option1))
                subprocess.Popen([app_path, option1])
            else:
                logger.info('{0}'.format(action))
                subprocess.Popen(app_path)


# 動作設定ファイルを選択したかを判定する
# 引数:無し
# 戻値:無し
def execute_if_action_setting_file_selected():
    is_file = os.path.isfile(action_setting_file.get())
    if is_file:
        execute_and_show_forced_close_window()

    else:
        # 終了メッセージを表示する
        messagebox.showerror('マウス・キーボードの自動操作', '動作設定ファイルを選択してください。')


# 終了ボタンの処理
# 引数:無し
# 戻値:無し
def exit_button():
    # rootウインドウを閉じる
    root.destroy()

    # マウス・キーボードの自動操作のプロセスIDを保存しているファイルを削除する
    os.remove('./pid_mouse_keyboard_control.txt')

    # 終了ボタンを選択したことをログに記録する
    logger.info('終了ボタンを選択しました')

    # プログラムを終了する
    sys.exit()


# ファイル選択ダイアログを表示して、動作設定ファイルを選択する
# 引数:無し
# 戻値:無し
def select_action_setting_file():
    file_select = filedialog.askopenfilename(title='動作設定ファイルを選択してください',
                                             filetypes=[('EXCEL file', '.xlsx')],
                                             initialdir='./')
    action_setting_file.set(file_select)

    # 選択した動作設定ファイルのパスをメインウインドウに表示する
    select_file_path['text'] = file_select


# 動作設定シートからactionとoption1~3を抽出し、execute_action関数を実行する
# 引数:無し
# 戻値:無し
def execute_action_list():
    sheet = read_action_setting_file()

    # 動作設定シートに記載されている内容を実行する
    action_list = 2
    while action_list <= sheet.max_row:
        action = sheet.cell(row=action_list, column=1).value
        option1 = sheet.cell(row=action_list, column=2).value
        option2 = sheet.cell(row=action_list, column=3).value
        option3 = sheet.cell(row=action_list, column=4).value
        execute_action(action, option1, option2, option3)

        action_list += 1

    # 終了メッセージを表示する
    messagebox.showinfo('マウス・キーボードの自動操作', 'マウス・キーボードの自動操作が終了しました')

    # 終了プロセスを実行する
    terminate_process()


# 強制終了のウインドウを表示する
# 引数:無し
# 戻値:無し
def forced_close_window():
    # サブウインドウの作成
    sub_window = tkinter.Toplevel(root)
    sub_window.attributes('-topmost', True)

    # 強制終了ボタンを設置する
    button = tkinter.Button(sub_window,
                            text='強制終了',
                            font=('meiryo', 15),
                            command=terminate_process)
    button.pack(padx=50, pady=25)


# マウス・キーボードの自動操作のプロセスを終了する
# 引数:無し
# 戻値:無し
def terminate_process():
    # 実行終了のログを記録する
    logger.info('マウス・キーボードの自動操作を終了する')

    with open('./pid_mouse_keyboard_control.txt') as f:
        pid = f.read()

    # マウス・キーボードの自動操作のプロセスIDを保存しているファイルを削除する
    os.remove('./pid_mouse_keyboard_control.txt')

    # プロセスを止める
    p = psutil.Process(int(pid))
    p.terminate()


# 動作設定ファイルを読み込んで実行するのと、強制終了ウインドウを表示する
# 引数:無し
# 戻値:無し
def execute_and_show_forced_close_window():
    # 動作設定ファイルに登録されているアクションを実行する
    thread = threading.Thread(target=execute_action_list)
    thread.start()

    # 強制終了のウインドウを表示する
    forced_close_window()


# 動作設定ファイルを選択するためのウインドウを表示する
# 引数:無し
# 戻値:無し
def main_window():
    # 文言1
    label1 = tkinter.Label(root,
                           text='マウス・キーボードの自動操作を行います。',
                           font=('meiryo', 10))
    label1.pack(padx=10, side=tkinter.TOP, anchor=tkinter.W)

    # 文言2
    label2 = tkinter.Label(root,
                           text='「ファイルの選択」をクリックし、動作設定ファイルを選択してください。',
                           font=('meiryo', 10))
    label2.pack(padx=10, side=tkinter.TOP, anchor=tkinter.W)

    # ファイルの選択ボタンを配置する
    select_button = tkinter.Button(text='ファイルの選択', command=select_action_setting_file)
    select_button.pack(padx=10, pady=10, ipadx=20, side=tkinter.TOP, anchor=tkinter.W)

    # 文言3
    label3 = tkinter.Label(root, text='選択したファイル')
    label3.pack(padx=10, pady=10, side=tkinter.TOP, anchor=tkinter.W)

    # 選択したファイルのパスを表示
    global select_file_path
    select_file_path = tkinter.Label(root, text='動作設定ファイルは未選択です。', anchor=tkinter.W, relief='groove', bd=1)
    select_file_path.pack(padx=10, side=tkinter.TOP, fill=tkinter.X, anchor=tkinter.W)

    # フレーム
    frame = tkinter.LabelFrame(root, text='', relief=tkinter.FLAT)
    frame.pack(padx=10, pady=5, side=tkinter.RIGHT)

    # 実行ボタンを配置する
    button_read = tkinter.Button(frame, text='実行', command=execute_if_action_setting_file_selected)
    button_read.pack(padx=10, pady=10, ipadx=30, side=tkinter.LEFT, anchor=tkinter.W)

    # 終了ボタンを配置する
    button_exit = tkinter.Button(frame, text='終了', command=exit_button)
    button_exit.pack(padx=10, pady=10, ipadx=30, side=tkinter.LEFT, anchor=tkinter.W)

    # ウインドウの「×」をクリックした時の動作を設定する
    root.protocol('WM_DELETE_WINDOW', exit_button)

    # ウインドウを維持する
    root.mainloop()


if __name__ == '__main__':
    main()

コメント

タイトルとURLをコピーしました