【Python】シンプルな日⇒英翻訳ツールつくってみた

##やりたいこと
Python等でコードを書いている時、変数名に使用する英単語が思い浮かばないことが多々あります。
今まではブラウザを立ち上げてgoogle翻訳を行なっていたのですが、ちょいと煩わしいと感じる今日この頃。
そんな英語力ゼロの自分をサポートする、簡単な日⇒英オンリーの翻訳ツールを作ってみました。
![簡易翻訳ツールデモ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/1e060f40-96bb-4372-22d3-e0a4840d2a65.gif)
↑ 出来上がりはこんな感じ。
なるべくスピーディな立ち上がりと、少ない操作数で実行できるツールを目指しました。
肝心の翻訳機能はgoogletransライブラリを活用しています。目新しい使い方はしていないので説明は割愛。
参考ページ ⇒ [Python – googletransを試してみました。](https://dev.classmethod.jp/articles/python-py-googletrans/)

##環境
Windows10 Home
Python3.8.2（32bit）
Visual Studio Code 1.45.1

##コードの中身

```python
from ctypes import windll
from tkinter import Tk,Entry,Label
import tkinter as tk

from googletrans import Translator # 外部ライブラリ
import win32gui # 外部ライブラリ
import pyperclip # 外部ライブラリ
import pyautogui # 外部ライブラリ

# 変換済みか否かのフラグ
translated_flg = False

# IMEをONにする関数 --------------------------------------------------
def imm_on(event):
    hWnd = win32gui.GetForegroundWindow() # アクティブウィンドウの取得
    himc = windll.LoadLibrary("imm32").ImmGetContext(hWnd) # コンテキスト取得
    windll.LoadLibrary("imm32").ImmSetOpenStatus(himc, 1) # 取得したコンテキストでIMEをON
    windll.LoadLibrary("imm32").ImmReleaseContext(hWnd, himc) # コンテキストを開放

# 翻訳処理を行なう関数 --------------------------------------------------
def translate_jp(japanese):
    global translated_flg

    # 連続処理を判定するためのIF
    if translated_flg == True:
        root.destroy() # すでに変換済みなら終了
    else:
        # 翻訳処理 日（ja）⇒英（en）
        english = Translator().translate(japanese, src="ja", dest="en")
        # 変換ラベルのフォント・色を変更
        translated["font"] = ("segoe UI",14)
        translated["foreground"] = "black"
        # 各ラベルの中の文字列（StringVar）を更新
        trans_var.set("「 "+english.text.lower()+" 」") # 小文字に変換
        ex_var.set("もう一度Enterを押すと終了")
        # クリップボードにコピー
        pyperclip.copy(english.text.lower()) # 小文字に変換
        translated_flg = True

# 初期化処理を行なう関数 --------------------------------------------------
def initialize(event):
    global translated_flg

    # 各ラベルの中の文字列（StringVar）を更新
    trans_var.set("「ここに変換した日本語が表示されます」")
    ex_var.set("翻訳する日本語を入力してください")
    # 変換ラベルのフォント・色を変更
    translated["font"] = ("Meiryo UI",8)
    translated["foreground"] = "gray"
    # 変換済みフラグをFalse
    translated_flg = False

# メイン処理部 --------------------------------------------------
# tkウィンドウ描画の準備
root = Tk()
root.geometry("+600+400") # ウィンドウ描画位置
root.option_add("*font",("Meiryo UI",14))
root.title("簡易翻訳（日⇒英）")

# tk用の文字列変数StringVarを準備
ex_var = tk.StringVar()
ex_var.set("翻訳する日本語を入力してください")
trans_var = tk.StringVar()
trans_var.set("「ここに変換した日本語が表示されます」")

# 上段のラベル（label_1）描画
label_1 = Label(root,textvariable=ex_var,font=("Meiryo UI",8))
label_1.pack(pady=10) # 配置（上下の余白 10px）
# 日本語入力Entry（jp_input）の描画
jp_input = Entry(root, width="14")
jp_input.pack(padx=10) # 配置（左右の余白 10px）de
jp_input.focus_set() # フォーカスの初期位置に指定
# 下段のラベル（translated）のラベル描画
translated = Label(root,textvariable=trans_var,font=("Meiryo UI",8))
translated["foreground"] = "gray"
translated.pack(padx=10,pady=10)

# jp_inputが描画されたときにimm_on関数実行
jp_input.bind("<Expose>", imm_on)

# jp_inputでEnter押されたときに関数実行
# bindはコールバック関数しか実行できないためラムダ式で引数渡す
jp_input.bind("<Return>", lambda f:translate_jp(jp_input.get()))

# jp_inputで何かしらキーが押されたらinitialize関数実行
jp_input.bind("<Key>",initialize)

# tkウィンドウ描画
root.mainloop()

# 終了後「Alt」+「Tab」でウインドウ切替（直前にアクティブだったウインドウに戻る）
pyautogui.hotkey("Alt","Tab",interval=0.05)
```
##工夫したポイント

+ ショートカットによる起動
+ IME入力モードの自動切り替え
+ 終了時の工夫（シンプル操作による終了、ウインドウの自動遷移etc）

####ショートカットキーによる起動
今回は起動用バッチファイルを作成し、ショートカットキーを設定して一発起動できるようにしました。

```起動.bat（例）
@echo off
start /min C:\Python\Python38-32\python.exe C:\Users\aaa\Desktop\python\簡易翻訳ツール（日英）.py
```
①上記のコマンドをメモ帳などで作成し、拡張子を.batにして保存。
（上記の場合、2byte文字が入っているので、文字コードをANSIに変更して保存する必要がありました）
②作成したbatファイルのショートカットを任意の場所に作成
③ショートカットを右クリック⇒「プロパティ」⇒「ショートカット」タブの「ショートカットキー」から設定

といった感じ。これにより、任意のキーでpyファイルのショートカット起動・実行ができるようになります。
また、batファイルで`start /min ~`と入力することで、コンソール画面を最小化してツール起動できるようにしました。
（本当はexe化しようと思ったけど、何故かpyinstallerでの変換が上手くいかない…）

####IME入力モードの自動切り替え
今回のツールは日本語翻訳のため、基本IMEがON（日本語入力モード）でしか使用しません。
しかし、tkウインドウは常にIMEがOFF状態で起動してしまいます。
その為、いちいち半角キーを押してIME切り替えを行わなければならず、ちょっと面倒。
そこで、**ツール起動と同時に、自動でIMEをONに**するようにしてみました。
まず、tkinterで作成したEntryウィジェットに対し`.focus_set()` を指定。起動と同時に入力ができるようにしています。
さらにbindのイベントシーケンスを`<Expose>`に指定して、ウィジェットが描画されたとき
（つまりウインドウが立ち上がった時）にIMEを自動的にONにする関数を実行するようにしました。
IMEをONにするコードは以下の通り。

```python
hWnd = win32gui.GetForegroundWindow() # アクティブウィンドウの取得
himc = windll.LoadLibrary("imm32").ImmGetContext(hWnd) # コンテキスト取得
windll.LoadLibrary("imm32").ImmSetOpenStatus(himc, 1) # 取得したコンテキストでIMEをON
windll.LoadLibrary("imm32").ImmReleaseContext(hWnd, himc) # コンテキストを開放
```
（windllに関して理解はまだ不十分なものの、ひとまず上記でIMEをONにすることができました）

####終了時の工夫（シンプル操作による終了、ウインドウの自動遷移etc）
ツールの終了動作もシンプルを意識し、**Enterキーだけで終了**できるようにしています。
（ボタンによる終了は余計なマウス動作が発生するため避けたかった）
まずEnter（Return）が押されたら、判定用のフラグtranslated_flgをTrueにし、
その状態でさらにEnterが押されたら、終了処理`root.destroy()`を行なうようになっています。
尚、BackSpace等でウィジェットの中身が更新されると、フラグは初期化されます。

終了後、起動時にアクティブだったウインドウのフォーカスは失われています。もとのウィンドウに戻すために、
`pyautogui.hotkey("Alt","Tab",interval=0.05)`でWindowsのショートカットキー「Alt」+「Tab」が押され、
**自動で元作業していたウインドウへのアクティブ切替えを実行する**ようにしました。
ただし、この方法はショートカットによるウインドウの切り替えが、直前アクティブだったものに戻るという仕様を利用しただけのもので、環境によってはうまくいかない可能性もあります。
（何か良いやり方を知っている方がいましたら、ご教示ください）

加えて、終了時に最後に翻訳した結果をクリップボードにコピーするようにしました。
元ウインドウに遷移後、Ctrl+Vだけで翻訳結果を貼り付けられるようにしています。

##振り返り
最初はツールづくりの練習として簡単なものを予定だったのですが、途中あれやこれやと手を加えてしまい、
意外と手間がかかってしまいました。。
（その分色々と勉強になりましたが）
反省は、文中でも書いた通りなぜかPyinstallerによるexe化ができず、batを使用したことです。
そのためか、ツール起動に少し時間がかかってしまっている印象。。
この辺は時間があるときに調査してみたいです。

####参考
今回初めてtkinterによるGUI作成に挑戦したのですが、以下のサイトが大変参考になりました。
　⇒ [お気楽 Python/Tkinter 入門](http://www.nct9.ne.jp/m_hiroi/light/pytk01.html)
イベントシーケンスについて参考になるサイトが少なかった中で、以下サイトが役に立ちました。
　⇒ [Tkinter の bind とイベントシーケンス](http://www.rouge.gr.jp/~fuku/tips/python-tkinter/bind.shtml)