はじめに
こんにちは。モノヒトです。PDF形式の英語論文をひたすら読んで内容を理解して簡単にまとめる。。。。みたいな業務をやることがありまして、論文の序論だけでもぱぱっと日本語訳できたら楽だなあと思い、それができるアプリみたいなもんを勉強を兼ねて作ろうと思いました。Cursorというエディタを使えば超簡単にできたので感動しました。以下に備忘録も兼ねて開発日記的なものを記します。
なお、PCのOSはWindows11にて開発しております。
Cursorのインストール
Cursorはここからダウンロードします。右上の「download」をクリックしてインストールするだけです。最近巷でコーディングに特化してとても優秀なエディタと噂のcursorですが、何がスゴイかご存じの方も多いかもしれませんが、ChatGPT4に作りたいアプリの概要をチャットすると、ぱぱっとコードを作ってくれてそのままRun and Debugを試すことができるのが便利すぎてビビりました。ChatGPT4への質問はフリープランだと回数が限られており、一定数質問数がカウントされるとクールダウンが必要なようです。私は無制限に使いたいので課金しました。費用は2023年12月現在で毎月20$です。
使用したモジュール 動作環境メモ 2023/12/11
-
tkinter バージョン8.6
GUIをpythonのコーディングによって簡単に作成します。便利。 -
PyPDF2 バージョン3.0.1
今後はPyPDF2は公式によるサポートがなくなり以降はpypdfによるサポートを行うという情報がありましたが、
CursorによるGPT4の返答ではPyPDF2を用いたコードスニペットを示したためそのまま使用しました。
- googletrans
- 最新版のバージョンでは翻訳が安定しませんでした。CursorのGPT4曰くgoogletrans==3.1.0a0というバージョンを試してみてください。このバージョンは一部のユーザーにとって安定して動作すると報告されています。以下のコマンドでインストールできます
-
- pip install googletrans==3.1.0a0
とのことだったのでこれを用います。翻訳結果は安定しました。なお、googletransというモジュールはスクレイピングを用いて
- pycryptodome
暗号化されているPDFを翻訳するためのモジュール。バージョン3.19.0 - time
コード一覧
コードはほぼすべてcursor上での対話によって生成されたコードスニペットをコピペして作られています。
コードの意味は後々勉強することにして、とりあえず動いたコードを以下に保存目的も兼ねてペーストしておきます。
ご参考まで。
import tkinter as tk
from tkinter import filedialog
import PyPDF2
from googletrans import Translator
import time
import traceback
def process_text(text):
lines = text.split('\n')
processed_lines = []
for i in range(len(lines)):
line = lines[i].strip()
next_line = lines[i + 1].strip() if i + 1 < len(lines) else ''
# Remove empty lines
if not line:
continue
# Check if the line is a heading
if line.startswith("I.") or line.startswith("II.") or line.startswith("III.") or line.startswith("IV.") or line.startswith("V."):
processed_lines.append(line)
continue
# Check if the line ends with an alphabet character, a reference number, or a comma
if processed_lines and (line[-1].isalpha() or line.endswith("]") or line.endswith("、")):
processed_lines[-1] += ' ' + line
continue
# Check if the line is part of the previous line
if processed_lines and (line.count('(') != line.count(')')):
processed_lines[-1] += ' ' + line
else:
# If the previous line is a heading, append the line without a newline
if processed_lines and (processed_lines[-1].isupper() or processed_lines[-1][0].isdigit()):
processed_lines[-1] += ' ' + line
else:
processed_lines.append(line)
return ' '.join(processed_lines)
def open_file():
filepath = filedialog.askopenfilename()
if filepath:
with open(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
page_number = int(page_number_entry.get())
page = reader.pages[page_number]
text = page.extract_text()
# Translate the text to Japanese
translator = Translator()
for _ in range(5): # Try up to 5 times
try:
# Limit the text to 5000 characters
text = text[:5000]
translated = translator.translate(text, dest='ja')
output_text = process_text(translated.text)
translation_length = int(translation_length_entry.get())
output_text = output_text[:translation_length]
break
except Exception as e:
print(f"Translation failed, retrying... ({e})")
traceback.print_exc()
time.sleep(1) # Wait for 1 second before retrying
else:
output_text = "翻訳できませんでした。"
# Display the output in the text widget
text_widget.delete(1.0, tk.END)
text_widget.insert(tk.END, output_text)
root = tk.Tk()
# Create entry widgets for page number, translation length and window size
tk.Label(root, text="Page Number").pack()
page_number_entry = tk.Entry(root)
page_number_entry.insert(0, "0") # Set initial value
page_number_entry.pack()
tk.Label(root, text="Translation Length").pack()
translation_length_entry = tk.Entry(root)
translation_length_entry.insert(0, "1200") # Set initial value
translation_length_entry.pack()
tk.Label(root, text="Window Size").pack()
window_size_entry = tk.Entry(root)
window_size_entry.insert(0, "80x60") # Set initial value
window_size_entry.pack()
# Create a button to apply the window size
def apply_window_size():
window_size = window_size_entry.get().split('x')
text_widget.config(width=int(window_size[0]), height=int(window_size[1]))
window_size_button = tk.Button(root, text="Apply Window Size", command=apply_window_size)
window_size_button.pack()
button = tk.Button(root, text="Open PDF", command=open_file)
button.pack()
# Create a text widget to display the output
text_widget = tk.Text(root, wrap='word', width=100, height=40) # 'word' wrap means that tkinter will break lines at words
text_widget.pack()
root.mainloop()
実行ファイル(exeファイル)の生成
pyinstallerをインストールするとexeファイルを生成することができるとのことなのでやってみました。上記のコードの名前がmain.pyだった場合、pyinstallerをpipでインストールしたのち、
pyinstaller --onefile main.py
とターミナルでコマンド入力するとmain.specというファイルが生成されます。実際のコードの名前にしたがって引数のmain.pyを適宜適切に変更して用います。で、main.specを開くと
hiddenimports=[],
という項目があるので、ここを
hiddenimports=['PyPDF2','googletrans','pycryptodome'],
と書き換えて上書き保存します。その後、ターミナルにてmain.pyが存在するディレクトリまで移動して
pyinstaller main.spec
とコマンド入力すると、そのディレクトリに存在するはずのさきほど編集したmain.specファイルに従ってPythonスクリプトを実行可能ファイルにビルドすることができます。
終わりに
動作結果は例えば以下のように動きました。感動しました。
以上です。今日も良い一日を! モノヒト