教授に「オンラインミーティング自動で開催できるってホント?」って聞かれたので「ええ、まあ」と答えたのですが、後で考えたらミーティングの終了ができないのはまずいと思ったので機能の追加を行いました。
前回の続きからになります。今回修正していないファイルのコードはこの記事には記載しません。
目次
1.追加する機能
- 終了時刻になったらミーティングを終了する
2.修正後のコード
Zoom操作モジュール
import pyautogui as pg | |
import sys | |
import os | |
# ズーム起動部分(windows専用) | |
def run_zoom(): | |
pg.press('win') | |
pg.PAUSE = 1 | |
pg.write('zoom') | |
pg.PAUSE = 2 | |
pg.press('enter') | |
# 定期ミーティング起動部分 | |
def start_meeting(): | |
# ボタンがアクティブ\非アクティブ状態両方のスクリーンショットをそれぞれリストに入れる。スタートは変数で持つ。 | |
buttton_active = [r'button\active\zoom_meeting_active.PNG', | |
r'button\active\zoom_igrashi_meeting_active.PNG'] | |
button_nonactive = [r'button\nonactive\zoom_meeting_non.PNG', | |
r'button\nonactive\zoom_igarashi_meeting_non.PNG'] | |
button_start = r'button\active\zoom_igarashi-meeting_start.PNG' | |
# 画像認識部分 | |
for button_a, button_n in zip(buttton_active, button_nonactive): | |
# 非アクティブ時のボタンを探す | |
button_loc_n = pg.locateOnScreen(button_n) | |
if button_loc_n is None: | |
# 非アクティブ時のボタンが見つからない場合、アクティブ時のボタンを探す | |
button_loc_a = pg.locateOnScreen(button_a) | |
if button_loc_a is None: | |
# ボタンが見つからないならばプログラムを終了する | |
sys.exit() | |
else: | |
# ボタンがアクティブならば操作は必要ない | |
continue | |
# 公式ドキュメントのサンプルコードを参考に実装 | |
# ボタンの中心の位置の座標を取得しクリックする | |
button_center = pg.center(button_loc_n) | |
button_x, button_y = button_center | |
pg.click(button_x, button_y) | |
# 画面遷移のために1秒停止する | |
pg.PAUSE = 1 | |
# ミーティング開始ボタンを押す部分 | |
button_x, button_y = pg.center(pg.locateOnScreen(button_start)) | |
pg.click(button_x, button_y) | |
def Stop(): | |
#ミーティング終了するときに押すボタン | |
button_quit = r'button\active\zoom_meeting_quit.PNG' | |
#終了ボタンを押す(ショートカットキーで代用) | |
pg.hotkey('alt', 'q') | |
#画面遷移のために1秒停止する | |
pg.PAUSE = 1 | |
#全員に対してミーティングを終了する | |
button_loc = pg.locateOnScreen(button_quit) | |
if button_loc is None: | |
print('404 NOT FOUND') | |
else: | |
button_x, button_y = pg.center(button_loc) | |
pg.click(button_x, button_y) | |
#ミーティング開始時 | |
def Start_Zoom(): | |
try: | |
run_zoom() | |
pg.PAUSE = 5 #Zoomの起動待ち | |
start_meeting() | |
except KeyboardInterrupt: | |
print('ERROR: It can not start zoom meeting.\n') | |
pg.FAILSAFE = True | |
#ミーティング終了時 | |
def Stop_Zoom(): | |
try: | |
Stop() | |
except KeyboardInterrupt: | |
print('ERROR: It can not quit zoom meeting.\n') | |
pg.FAILSAFE = True |
メイン部分
import PySimpleGUI as sg | |
import datetime as dt | |
import getschedule as gs | |
import operate_zoom as oz | |
import time | |
import threading | |
sg.theme('DarkBrown1') | |
layout = [ | |
[sg.Text('Next', size = (5,1), justification = 'left'), sg.Text(size = (20,1), key='-START-'),sg.Text('~',size = (6,1)), sg.Text(size=(20,1), key='-END-')], | |
[sg.Text('Now',size = (5,1),justification = 'left'),sg.Text(size=(20,1), key = '-NOW-')], | |
[sg.Text('Log', size =(5,1), justification = 'left')], | |
[sg.Output(size=(100,10), key='-LOG-')], | |
[sg.Button('Start/Stop', focus=True), sg.Quit()] | |
] | |
window = sg.Window('ミーティング開催', layout) | |
count = -1 #初回ループ時に予定取得させるため | |
def start_zoom(now_date): | |
oz.Start_Zoom() | |
print('{} Start meeting'.format(now_date)) | |
#タイミング調整 | |
time.sleep(5) | |
#画面共有開始 | |
oz.screen_sharing() | |
def end_zoom(now_date): | |
global count | |
zoom_flag = True | |
oz.Stop_Zoom() | |
print('{} Quit meeting'.format(now_date)) | |
#ミーティング終了後、即座に予定を再取得する | |
count = -1 | |
def GUI(): | |
global count | |
RPA_running = False | |
next_start_date, next_end_date = '0000-00-00 00:00', '0000-00-00 00:00' | |
interval = 0 #ミーティング開始時、終了時から1分間カウントする変数 | |
zoom_flag = False #何度も起動するのを防ぐため。Trueの場合1分経過でFalseに戻す。 | |
while True: | |
event, values = window.read(timeout=1000) #ループ間隔は1秒 | |
#現在時刻を取得 | |
now = dt.datetime.now() | |
#時刻表示のために整形 | |
now_time = now.strftime('%Y-%m-%d %H:%M:%S') | |
#比較のためにstr型に変換 | |
now_date = now.strftime('%Y-%m-%d %H:%M') | |
#現在時刻の表示 | |
window['-NOW-'].update(now_time) | |
#ボタン操作 | |
if event in ('Quit'): | |
break | |
elif event == 'Start/Stop': | |
RPA_running = not RPA_running | |
if RPA_running: | |
print('{} Start'.format(now_date)) | |
else: | |
print('{} Stop'.format(now_date)) | |
#再開時に予定を取得させるため | |
count = -1 | |
if RPA_running: | |
#30分間隔で予定取得 | |
if count == -1 or count == 1800: | |
#カウンタを戻す | |
count = 0 | |
#予定を5つ取得し、直近の予定の開始時刻と終了時刻を格納 | |
schedule = gs.get_events() | |
start_date = schedule[0] | |
end_date = schedule[1] | |
#予定を整形 | |
start_date = start_date[0:10] + ' ' + start_date[11:16] | |
end_date = end_date[0:10] + ' ' + end_date[11:16] | |
#予定を取得したらログに記述 | |
print('{} Get next schedule'.format(now_date)) | |
#次の予定が変更されたらログに記述 | |
if start_date != next_start_date: | |
next_start_date = start_date | |
next_end_date = end_date | |
print('{} Update next schedule'.format(now_date)) | |
#GUI表示 | |
window['-START-'].update(start_date) | |
window['-END-'].update(end_date) | |
#ミーティング開始時、終了時にカウントを始める | |
if zoom_flag == True: | |
interval += 1 | |
#60秒経過したらフラッグを反転させ、変数を初期値に戻す | |
if interval == 60: | |
zoom_flag = not zoom_flag | |
interval = 0 | |
#開始時刻かつ直前にミーティングを起動していないならミーティングを開始する | |
if start_date == now_date and zoom_flag == False: | |
zoom_flag = True | |
zoom_thread = threading.Thread(target=start_zoom, daemon=True, args=(now_date,)) | |
zoom_thread.start() | |
#終了時刻にかつ直前にミーティングを終了していないならミーティングを終了する | |
if end_date == now_date and zoom_flag == False: | |
zoom_flag = True | |
zoom_thread = threading.Thread(target = end_zoom, daemon = True, args = (now_date,)) | |
zoom_thread.start() | |
#カウンタを増やす | |
count += 1 | |
else: | |
continue | |
window.close() | |
if __name__ == '__main__': | |
GUI_thread = threading.Thread(target=GUI) | |
GUI_thread.start() |
3.機能追加時の不具合
なかなか画像を認識しませんでした。次の画像でボタンを認識しました。
外側の黒い部分が少なかったり、ボタンの内側の画像だけだったりすると見つけてくれませんでした。
できる限り認識してほしいボタンの外側を厚くスクリーンショットを撮る必要があるようです。
4.その他修正
4-1.時計を秒単位まで表示
デバッグのときに便利なのと、秒単位まで表示したほうが時計としての機能を果たしていると思ったので変更しました。
これに伴い、timeoutは1秒の設定になっています。
4-2.Intervalの設定
timeoutを1秒にしたことで、何度もミーティングを開始/終了するようになりました。そのため、intervalを設けて、60秒経過(つまりnow_dateが変わるまで)するまでミーティングの開始/終了を行えないようにしました。
4-3.Stop後Startしたとき予定を再取得する
Stopを押したときにcountを-1に設定することで、再度Startを押した場合に予定を即座に再取得するようにしました。
4-4.Startを押した後にcountが増加するように変更
今まではcountは起動時から常に増加するようにしていました。また、Stopを押した後も増加するようになっていました。そのため、Startを押してRPA_runningがTrueのときだけcountを増加するようにしました。
4-5.ログ出力順番の変更
今まではログ出力後にミーティングを開始/終了していましたが、ミーティングを開始/終了後にログを出力するようにしました。
5.今後
画像認識をしている間に時計が止まるため、時計部分を並列処理し常に動くようにする予定です。
6.願望
ミーティング開始時の画像認識が60秒程度かかるため、画像認識を高速化させたいです。ただ、真面目にやると私の修論テーマになりかねないので考え中です。
または高速に画像認識できる既成のモジュールを用意するなども考えています。