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

Python

はじめに

前回作成したマウス・キーボードの自動操作ツールの機能追加を行いました。
機能追加の内容は、以下となります。

  • 環境設定とログ設定の編集をメニューバーから呼び出せるようにしました。
  • マウスカーソルの座標確認を行う機能を追加しました。
  • ホットキー(Ctrl+cなど)を扱えるようにしました。

<メニューバーの画像>

<座標確認の画像>

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

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

ツールの実行方法

ツールの実行方法は前回から変更はありません。プログラムから実行する場合は、前回の実行方法を参照してください。EXEファイルから実行する場合、実行環境の準備などは不要です。「mouse_keyboard_control.exe」を実行してください。

前回の実行方法は下記を参照してください。

Pythonで作るマウス・キーボードの自動操作ツール①
はじめに Google Colaboratoryを使用して機械学習のプログラムを作成しています。機械学習のモデル作成中にGoogle colaboratoryとのセッション切れると、モデル作成を最初からやり直す必要があります。セッションが...

ツールの画面遷移

メニューバーに「ファイル」「ツール」「ヘルプ」を追加しました。
各メニューの画面遷移を下記となります。

ファイルメニューの画面遷移

  • 環境設定は、メモ帳で「config.ini」を開きます。
  • ログ設定は、メモ帳で「logging.conf」を開きます。
  • 環境設定とログ設定の再読み込みは、内部処。理となるでトップ画面に戻ります。
  • 終了は、ツールを閉じます。

ツールメニューの画面遷移

  • 座標確認ウインドウは、常に最前面で表示されます。
  • 座標確認ウインドウを閉じるためには、ウインドウの「✕」をクリックします。

ヘルプメニューの画面遷移

  • マニュアルは、ブラウザでマニュアルページ(作成中)を開きます。
  • ログ表示は、メモ帳で「mouse_keyboard_control.log」を開きます。
  • バージョン情報は、バージョン/リリース日/ライセンス/製品ページを表示します。
  • 製品ページは、ブラウザで製品ページを表示します。

通常利用の画面遷移

  • 強制終了ウインドウは常に最前面に表示されます。

動作設定ファイルを未選択の状態で「実行」をクリックした場合の画面遷移

シート名が正しくない動作設定ファイルを選択したときの画面遷移

プログラムの説明

前バージョンからの追加・変更点に絞って下記に記載しました。

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

太字が追加・変更した関数となります。

行数 概要 関数名 引数 戻値
55 メイン処理の開始位置 main()
72 環境設定とログ設定を読み込む read_conf()
89 動作設定ファイルを読み込む read_action_setting_file() 読み込んだ動作設定ファイルのシート内容
117 本ツールを実行したときのプロセスIDを記録する。(強制終了の際、Killする対象のプロセスIDを記録する) record_pid()
126 引数に応じたpyautoguiの関数を実行する execute_action(action, option1, option2, option3) 動作,オプション1,オプション2,オプション3
177 動作設定ファイルを選択したかを判定する execute_if_action_setting_file_selected()
190 終了ボタンを押したときの処理 exit_button()
207 ファイル選択ダイアログを表示して、動作設定ファイルを選択する select_action_setting_file()
220 動作設定シートからactionとoption1~3を抽出し、execute_action関数を実行する execute_action_list()
244 強制終了のウインドウを表示する forced_close_window()
260 マウスカーソルの座標をサブウインドウに表示する mouse_position()
281 マウスカーソルの座標を更新する update_mouse_position()
290 マウス・キーボードの自動操作のプロセスを終了する terminate_process
308 動作設定ファイルを読み込んで実行するのと、強制終了ウインドウを表示する execute_and_show_forced_close_window()
320 任意のアプリで指定したファイルを編集する edit_file(app_path, file_path) アプリのパス、ファイルのパス
328 バージョン情報を表示する show_version()
360 指定したURLをブラウザを開く open_link_browser(url) URL
367 動作設定ファイルを選択するためのウインドウを表示する main_window()

「72行目:環境設定とログ設定を読み込む」の説明とコード

説明

  • 環境設定とログ設定の再読み込み機能を追加するにあたり、環境設定とログ設定を読み込む処理を関数にまとめました。
  • configとloggerは関数外から参照することがあるので、グローバル変数にしています。

コード

def read_conf():
    # 環境設定を読み込む
    global config
    config = configparser.ConfigParser()
    config.read(config_path)

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

    logger.info('環境設定とログ設定の読み込み完了')

「260行目:マウスカーソルの座標をサブウインドウに表示する」の説明とコード

説明

  • マウスカーソルの座標を表示するウインドウを作成します。
  • 作成したウインドウは常に最前面に表示します。
  • マウスカーソルの座標を更新する「update_mouse_position」関数を呼び出しています。

コード

def mouse_position():
    logger.info('マウスカーソルの座標確認を実行')
    # サブウインドウを作成する
    sub_window = tkinter.Toplevel(root)
    sub_window.attributes('-topmost', True)

    # マウスカーソルの座標のラベルを設定する
    global position_label
    position_label = tkinter.Label(sub_window, font=('meiryo', 16))
    position_label.pack(padx=50, pady=20)

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

    # マウスカーソルの座標を更新する
    update_mouse_position()

「281行目:マウスカーソルの座標を更新する」の説明とコード

説明

  • マウスカーソルの座標は「pyautogui.position()」で取得しています。
  • afterメソッドを使用して、指定した時間で定期的に指定した関数を実行しています。
  • afterメソッドの第1引数で時間(ミリ秒)を指定し、第2引数で関数を指定します。

コード

def update_mouse_position():
    x, y = pyautogui.position()
    position_label.config(text='X: {0}, Y: {1}'.format(x, y))
    root.after(50, update_mouse_position)

「320行目:任意のアプリで指定したファイルを編集する」の説明とコード

説明

  • 「subprocess.run」を使用してアプリを実行しています。
  • 「subprocess.run」は同期処理となるので、処理が終わるまで次の処理に進みません。

コード

def edit_file(app_path, file_path):
    logger.info('"{0}" で "{1}" を開く'.format(app_path, file_path))
    subprocess.run([app_path, file_path])

「328行目:バージョン情報を表示する」の説明とコード

説明

  • 製品名/バージョン/リリース日/ライセンス/製品ページを表示します。
  • 製品ページにカーソルを合わせるとアイコンが変わるように「cursor=hand2」で設定しています。
  • 製品ページをブラウザで開くのは「open_link_browser」で実施しています。
  • 製品ページのURLは「config.ini」で指定しています。

コード

def show_version():
    version_w = tkinter.Toplevel()

    # 製品名を表示する
    product_name_label = tkinter.Label(version_w, text='製品名 : マウス・キーボード自動操作ツール')
    product_name_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # バージョンを表示する
    version_label = tkinter.Label(version_w, text='バージョン : 1.1.0')
    version_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # リリース日を表示する
    release_label = tkinter.Label(version_w, text='リリース日 : 2023/09/03')
    release_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # ライセンスを表示する
    license_label = tkinter.Label(version_w, text='ライセンス : MIT License')
    license_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # 製品ホームページのリンクを設定する
    homepage_label = tkinter.Label(version_w, text='製品ページ', fg="blue", cursor="hand2")
    homepage_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)
    homepage_label.bind("", lambda e: open_link_browser(config['manual_path']['manual']))

    # バージョン情報ウインドウを閉じる
    close_button = tkinter.Button(version_w, text='閉じる', command=lambda: version_w.destroy())
    close_button.pack(padx=10, pady=5, ipadx=20)

「360行目:指定したURLをブラウザを開く」の説明とコード

説明

  • ブラウザを開くために標準ライブラリの「webbrowser」を使用しています。

コード

def open_link_browser(url):
    webbrowser.open_new(url)

次回、試したいこと

試したいことは色々とあるので、アプリのデザインも勉強しながら下記を試していきます。

  • 動作設定ファイルの記載に誤りがあった場合、エラーメッセージを表示する。
  • エラー内容をログに残す。
  • 読み込んだ動作設定ファイルの内容をツールに表示する。
  • 画面の画像を認識し、指定した画像と合致した箇所をクリックする。

プログラムの全コード

# 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 webbrowser
import subprocess
import configparser
import logging.config

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

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

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

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

# 環境設定、ログ設定、ログファイルのパスを設定
config_path = r'./config.ini'
logging_path = r'./logging.conf'
logfile_path = r'./mouse_keyboard_control.log'


# メイン処理
# 引数:無し
# 戻値:無し
def main():
    # 環境設定とログ設定を読み込む
    read_conf()

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

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

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


# 環境設定とログ設定を読み込む
# 引数:無し
# 戻値:無し
def read_conf():
    # 環境設定を読み込む
    global config
    config = configparser.ConfigParser()
    config.read(config_path)

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

    logger.info('環境設定とログ設定の読み込み完了')


# 動作設定ファイルを読み込む
# 引数:無
# 戻値:読み込んだシート(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', 'hotkey']:
        if action == act:
            if option1 is not None and option2 is not None:
                logger.info('option1:{0}, option2:{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)
                elif action == 'hotkey':
                    pyautogui.hotkey(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 mouse_position():
    logger.info('マウスカーソルの座標確認を実行')
    # サブウインドウを作成する
    sub_window = tkinter.Toplevel(root)
    sub_window.attributes('-topmost', True)

    # マウスカーソルの座標のラベルを設定する
    global position_label
    position_label = tkinter.Label(sub_window, font=('meiryo', 16))
    position_label.pack(padx=50, pady=20)

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

    # マウスカーソルの座標を更新する
    update_mouse_position()


# マウスカーソルの座標を更新する
# 引数:無し
# 戻値:無し
def update_mouse_position():
    x, y = pyautogui.position()
    position_label.config(text='X: {0}, Y: {1}'.format(x, y))
    root.after(50, update_mouse_position)


# マウス・キーボードの自動操作のプロセスを終了する
# 引数:無し
# 戻値:無し
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 edit_file(app_path, file_path):
    logger.info('"{0}" で "{1}" を開く'.format(app_path, file_path))
    subprocess.run([app_path, file_path])


# バージョン情報を表示する
# 引数:無し
# 戻値:無し
def show_version():
    version_w = tkinter.Toplevel()

    # 製品名を表示する
    product_name_label = tkinter.Label(version_w, text='製品名 : マウス・キーボード自動操作ツール')
    product_name_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # バージョンを表示する
    version_label = tkinter.Label(version_w, text='バージョン : 1.1.0')
    version_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # リリース日を表示する
    release_label = tkinter.Label(version_w, text='リリース日 : 2023/09/03')
    release_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # ライセンスを表示する
    license_label = tkinter.Label(version_w, text='ライセンス : MIT License')
    license_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)

    # 製品ホームページのリンクを設定する
    homepage_label = tkinter.Label(version_w, text='製品ページ', fg="blue", cursor="hand2")
    homepage_label.pack(padx=10, pady=5, side=tkinter.TOP, anchor=tkinter.W)
    homepage_label.bind("", lambda e: open_link_browser(config['manual_path']['manual']))

    # バージョン情報ウインドウを閉じる
    close_button = tkinter.Button(version_w, text='閉じる', command=lambda: version_w.destroy())
    close_button.pack(padx=10, pady=5, ipadx=20)


# 引数で指定したURLをブラウザで開く
# 引数:URL
# 戻値:無し
def open_link_browser(url):
    webbrowser.open_new(url)


# 動作設定ファイルを選択するためのウインドウを表示する
# 引数:無し
# 戻値:無し
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)

    # メニューバーを作成する
    menubar = tkinter.Menu(root)
    root.configure(menu=menubar)

    # メニューバーにファイルを配置する
    file_menu = tkinter.Menu(menubar, tearoff=0)
    menubar.add_cascade(label='ファイル', menu=file_menu)

    # ファイルに環境設定を配置する
    file_menu.add_command(label='環境設定',
                          command=lambda: edit_file(config['application_path']['memo'],
                                                    config_path))

    # ファイルにログ設定を配置する
    file_menu.add_command(label='ログ設定',
                          command=lambda: edit_file(config['application_path']['memo'],
                                                    logging_path))

    # ファイルに環境設定とログ設定の再読み込みを配置する
    file_menu.add_command(label='環境設定とログ設定の再読み込み', command=read_conf)

    # セパレータを追加する
    file_menu.add_separator()

    # ファイルに終了を配置する
    file_menu.add_command(label='終了', command=exit_button)

    # ツールバーを作成する
    tool_menu = tkinter.Menu(menubar, tearoff=0)
    menubar.add_cascade(label='ツール', menu=tool_menu)

    # ツールに座標確認を配置する
    tool_menu.add_command(label='座標確認', command=mouse_position)

    # ヘルプバーを作成する
    help_menu = tkinter.Menu(menubar, tearoff=0)
    menubar.add_cascade(label='ヘルプ', menu=help_menu)

    # ヘルプにマニュアルを配置する
    help_menu.add_command(label='マニュアル',
                          command=lambda: edit_file(config['application_path']['edge'],
                                                    config['manual_path']['manual']))

    # ヘルプにログ表示を配置する
    help_menu.add_command(label='ログ表示',
                          command=lambda: edit_file(config['application_path']['memo'],
                                                    logfile_path))

    # ヘルプにバージョン情報を配置する
    help_menu.add_command(label='バージョン情報', command=show_version)

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


if __name__ == '__main__':
    main()

コメント

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