概要
前回作成したツール を他の人でも使えるようにexeにしてみました。
exe化にあたり毎回起動させると時間がかかってしまうのでこちらを参考に複数ウィンドウを起動させるようにしました。
こうすることでスクリーンショットを取るたびにツールを起動させる手間が省けます。
処理の流れ
処理としては以下のようになっています。
- スクリーンショット取得用のウィンドウを起動
- スクリーンショットを取得後は前回と同じ処理の流れになります
- 文字を取得後ウィンドウを閉じることで再度別のスクリーンショットを取得できます。
ソースコード全体は以下になります。
プロパティファイル使ってTesseractのパスを読み込む形にしています。実際に手元で使う際は直接指定するなどしてください。
ソースはこちら
import os
import tkinter
import pyocr
import pyautogui
import configparser
from datetime import datetime
from PIL import Image, ImageEnhance, ImageTk
import tkinter as tk
from tkinter import ttk,messagebox
iniFile=configparser.ConfigParser()
iniFile.read('resources/appConfig.ini','UTF-8')
#Pah設定
TESSERACT_PATH = iniFile.get('env','TESSERACT_PATH')
TESSDATA_PATH = iniFile.get('env','TESSDATA_PATH')
os.environ['PATH'] += os.pathsep + TESSERACT_PATH
os.environ['TESSDATA_PREFIX'] = TESSDATA_PATH
#OCRエンジン取得
tools = pyocr.get_available_tools()
tool = tools[0]
#OCRの設定 ※tesseract_layout=6が精度には重要。デフォルトは3
builder = pyocr.builders.TextBuilder(tesseract_layout=6)
RESIZE_RETIO = 2 # 縮小倍率の規定
hasOpenWindow=False
class BaseWindow(tk.Frame):
def __init__(self,master):
super().__init__(master)
self.pack()
self.master.geometry("200x50")
self.master.title("readScreen")
self.create_widgets()
def create_widgets(self):
# Button
self.button_new_win = ttk.Button(self)
self.button_new_win.configure(text="スクリーンショット取得処理起動")
self.button_new_win.configure(command = self.new_window)
self.button_new_win.pack()
#Call back function
def new_window(self):
global hasOpenWindow
if hasOpenWindow:
messagebox.showinfo('処理起動中','処理がすでに起動しているようです。\r\n ×で閉じてしまった場合はツールを再起動してください。')
else:
hasOpenWindow=True
self.newWindow = tk.Toplevel(self.master)
self.app = ReadScreen(self.newWindow)
# 画面取得用の処理 メインディスプレイのスクリーンショットを取得する
class ReadScreen(tk.Frame):
def __init__(self,master):
super().__init__(master)
self.pack()
global canvas1,img_tk
# 表示する画像の取得(スクリーンショット) メインディスプレイのみが対象です
img = pyautogui.screenshot()
# スクリーンショットした画像は表示しきれないので画像リサイズ
img_resized = img.resize(size=(int(img.width / RESIZE_RETIO),
int(img.height / RESIZE_RETIO)),
resample=Image.BILINEAR)
# tkinterで表示できるように画像変換
img_tk = ImageTk.PhotoImage(img_resized)
# Canvasウィジェットの描画
canvas1 = tkinter.Canvas(master,
bg='black',
width=img_resized.width,
height=img_resized.height)
# Canvasウィジェットに取得した画像を描画
canvas1.create_image(0, 0, image=img_tk, anchor=tkinter.NW)
def start_point_get(event):
global start_x, start_y # グローバル変数に書き込みを行なうため宣言
canvas1.delete('rect1') # すでに'rect1'タグの図形があれば削除
# canvas1上に四角形を描画(rectangleは矩形の意味)
canvas1.create_rectangle(event.x,
event.y,
event.x + 1,
event.y + 1,
outline='red',
tag='rect1')
# グローバル変数に座標を格納
start_x, start_y = event.x, event.y
def rect_drawing(event):
# ドラッグ中のマウスポインタが領域外に出た時の処理
if event.x < 0:
end_x = 0
else:
end_x = min(img_resized.width, event.x)
if event.y < 0:
end_y = 0
else:
end_y = min(img_resized.height, event.y)
# 'rect1'タグの画像を再描画
canvas1.coords('rect1', start_x, start_y, end_x, end_y)
# ドラッグを離したときのイベント 処理が多いので中身は外だし
def release_action(event):
self.release_action_main(event,img)
# Canvasウィジェットを配置し、各種イベントを設定
canvas1.pack()
canvas1.bind('<ButtonPress-1>', start_point_get)
canvas1.bind('<Button1-Motion>', rect_drawing)
canvas1.bind('<ButtonRelease-1>', release_action)
self.master.title("スクリーンショット")
self.create_widgets()
def create_widgets(self):
# Button
self.button_quit = ttk.Button(self)
self.button_quit.configure(text="スクリーンショットを閉じる")
self.button_quit.configure(command=self.quit_window)
self.button_quit.pack()
def quit_window(self):
global hasOpenWindow
hasOpenWindow=False
self.master.destroy()
# はなしたときの処理を外だし
def release_action_main(self,event,img):
# 'rect1'タグの画像の座標を元の縮尺に戻して取得
start_x, start_y, end_x, end_y = [
round(n * RESIZE_RETIO) for n in canvas1.coords('rect1')
]
cropImag =img.crop(box=(start_x,start_y,end_x,end_y))
nowDate=datetime.now().strftime('%Y%m%d')
fileName=datetime.now().strftime('%Y%m%d%H%M%S')
currentDirectory=os.getcwd()
workDir=os.path.join(currentDirectory,nowDate)
tempDir=os.path.join(currentDirectory,nowDate,'tmp')
os.makedirs(tempDir, exist_ok=True)
# 画像のファイル名
pngFileName=os.path.join(tempDir,nowDate+'.png')
# 画像をいったん保存
cropImag.save(pngFileName)
# 保存した画像を開く
readimg = Image.open(pngFileName)
#適当に画像処理 このあたりはオリジナルのままです
img_g = readimg.convert('L') #Gray変換
enhancer= ImageEnhance.Contrast(img_g) #コントラストを上げる
img_con = enhancer.enhance(2.0) #コントラストを上げる
#画像からOCRで日本語を読んで、文字列として取り出す
readText = tool.image_to_string(img_con , lang='jpn', builder=builder)
#半角スペースを消す ※読みやすくするため 英語の場合は単語がつぶれてしまうので日本語用のみ
readTextJp = readText.replace(' ', '')
print(readTextJp)
# クリップボードにコピー
self.master.clipboard_append(readTextJp)
# テキストファイルとしても出力しておく 日本語用と英語用で分ける
textFileJp=os.path.join(workDir,fileName+'jp.txt')
textFileEn=os.path.join(workDir,fileName+'.txt')
with open(textFileJp, mode='w') as f:
f.write(readTextJp)
with open(textFileEn, mode='w') as f:
f.write(readText)
# ダイアログにも表示させる
pyautogui.alert(readTextJp)
if __name__ == "__main__":
root = tk.Tk()
app = BaseWindow(master=root)
app.mainloop()
処理の説明
- 以下の処理でウィンドウを起動します。
- 多重起動の防止処理も行っています。
class BaseWindow(tk.Frame):
def __init__(self,master):
super().__init__(master)
self.pack()
self.master.geometry("200x50")
self.master.title("readScreen")
self.create_widgets()
def create_widgets(self):
# Button
self.button_new_win = ttk.Button(self)
self.button_new_win.configure(text="スクリーンショット取得処理起動")
self.button_new_win.configure(command = self.new_window)
self.button_new_win.pack()
#Call back function
def new_window(self):
global hasOpenWindow
if hasOpenWindow:
messagebox.showinfo('処理起動中','処理がすでに起動しているようです。\r\n ×で閉じてしまった場合はツールを再起動してください。')
else:
hasOpenWindow=True
self.newWindow = tk.Toplevel(self.master)
self.app = ReadScreen(self.newWindow)
- 初期化処理の中で各動作の設定を行っています。
- マウスを離したときの処理は長いので別メソッドに切り出しています。
def __init__(self,master):
super().__init__(master)
self.pack()
global canvas1,img_tk
# 表示する画像の取得(スクリーンショット) メインディスプレイのみが対象です
img = pyautogui.screenshot()
# スクリーンショットした画像は表示しきれないので画像リサイズ
img_resized = img.resize(size=(int(img.width / RESIZE_RETIO),
int(img.height / RESIZE_RETIO)),
resample=Image.BILINEAR)
# tkinterで表示できるように画像変換
img_tk = ImageTk.PhotoImage(img_resized)
# Canvasウィジェットの描画
canvas1 = tkinter.Canvas(master,
bg='black',
width=img_resized.width,
height=img_resized.height)
# Canvasウィジェットに取得した画像を描画
canvas1.create_image(0, 0, image=img_tk, anchor=tkinter.NW)
def start_point_get(event):
global start_x, start_y # グローバル変数に書き込みを行なうため宣言
canvas1.delete('rect1') # すでに'rect1'タグの図形があれば削除
# canvas1上に四角形を描画(rectangleは矩形の意味)
canvas1.create_rectangle(event.x,
event.y,
event.x + 1,
event.y + 1,
outline='red',
tag='rect1')
# グローバル変数に座標を格納
start_x, start_y = event.x, event.y
def rect_drawing(event):
# ドラッグ中のマウスポインタが領域外に出た時の処理
if event.x < 0:
end_x = 0
else:
end_x = min(img_resized.width, event.x)
if event.y < 0:
end_y = 0
else:
end_y = min(img_resized.height, event.y)
# 'rect1'タグの画像を再描画
canvas1.coords('rect1', start_x, start_y, end_x, end_y)
# ドラッグを離したときのイベント 処理が多いので中身は外だし
def release_action(event):
self.release_action_main(event,img)
# Canvasウィジェットを配置し、各種イベントを設定
canvas1.pack()
canvas1.bind('<ButtonPress-1>', start_point_get)
canvas1.bind('<Button1-Motion>', rect_drawing)
canvas1.bind('<ButtonRelease-1>', release_action)
self.master.title("スクリーンショット")
self.create_widgets()
- スクリーンショットを取得したウィンドウの設定を行っています。
- 閉じるボタン押下時にフラグを初期化して再度実行できるようにしています。
def create_widgets(self):
# Button
self.button_quit = ttk.Button(self)
self.button_quit.configure(text="スクリーンショットを閉じる")
self.button_quit.configure(command=self.quit_window)
self.button_quit.pack()
def quit_window(self):
global hasOpenWindow
hasOpenWindow=False
self.master.destroy()
動作例
- exe形式だと以下のようなフォルダ構成になります。別途Tesseractをインストールしてもらう必要がありますが・・・
最後に
pyinstallerでexeにすると起動に時間がかかります。
そこで複数のウィンドウを起動するようにするとすぐに別のスクリーンショットを取得できるようになるので欠点を補えます。
これで他の人に配布するなどする方法も可能になるかなと