##はじめに
PySimpleGUIが使いやすいので、以前使っていたLabVIEWのデザインパターンの一つ「キューメッセージハンドラデザインパターン」を実現できないか取り組んだ結果です。
LabVIEWキューメッセージハンドラの説明
アプリケーションデザインパターン:生産者/消費者
【LabVIEW スキルアップ】デザインパターン”QMH”とは?
LabVIEWについてはWikiなどを見ていただければと思いますが、計測装置の制御プログラムでよく使われています。(最近はPythonに押されていますが。)
特徴は、グラフィカルプログラミングで、whileループを一つ書けば、1つのコアまたはスレッドを使う。なので、Whileループをいっぱい書けばマルチタスクになります。キューメッセージハンドラデザインパターンはWhileループ同士のデータのやり取りをキューで行うものです。
##環境
前回の記事 PySimpleGUIでの時刻表示(timeoutの使い方)と同じです。
Win10Pro
Anaconda
Python3.7
##やりたいこと
PySimpleGUIは、イベント(ボタンを押すとか、入力するとかのイベント)待機と、処理されたデータのUIへの表示を、受け持ちます。マルチスレッドを使って、受け取ったイベントを処理する関数へ、キューでデータを送ります。受け取ったデータをもとに関数は処理を行い、結果をキューで返します。
スレッド間通信(プロセス間通信)では、グローバル変数も使うことができますが、今回はキューを使っています。
図に書くと以下のようなものです。
参考にしたデモプログラムは、Demo_Multithreaded_Multiple_Threadsです。
ちなみに、Demo_Multithreaded_Long_Task_Simpleはグローバル変数を使ったものです。
##作成したコード
from queue import Queue
from threading import Thread
import time
import PySimpleGUI as sg
ui_que = Queue()
data_que = Queue()
def show_time():
jikan = time.strftime('%p %I:%M:%S')
return jikan
def worker(data_que,ui_que):
result = data_que.get()
time.sleep(1)
ui_que.put(result[0])
def worker_do(data_que,ui_que):
result_do = data_que.get()
time.sleep(0.5)
ui_que.put(len(result_do))
sg.theme('Dark')
layout = [
[sg.Text(size=(15, 1), font=('Helvetica', 20),justification='center', key='-jikan-')],
[sg.Text('Data', size=(8, 1)),sg.Input(key='-data-')],
[sg.Submit()],
[sg.Text('Data2', size=(8, 1)),sg.Input(key='-data2-')],
[sg.Button('DO',key='-do-'), sg.Cancel()],
[sg.Text('Status:', size=(8, 1)),sg.Text('',size=(8,1), key='-status-')],
[sg.Output(size=(70, 2))]
]
window = sg.Window('template',layout)
while True:
event, values = window.read(timeout=100,timeout_key='-timeout-')
# event, values = window.read()
#timeoutを指定することで、timeoutイベントが起こります。timeoutの単位ms
# print(event,values)
#↑コメントアウトを外すと、どんなイベントが起こっているか確かめることができます。
if event in (None,'Cancel'):
break
elif event in 'Submit':
print('Input data: {}'.format(values['-data-']))
# デーモンスレッドはデーモンスレッド以外のスレッドがなくなった時点で全て自動終了する
thread = Thread(target=worker, args=(data_que,ui_que), daemon=True).start()
data_que.put(values['-data-'])
elif event in '-do-':
print('do somthing: {}'.format(values['-data2-']))
thread_do = Thread(target=worker_do, args=(data_que,ui_que), daemon=True).start()
data_que.put(values['-data2-'])
elif event in '-timeout-':
jikan = show_time()
window['-jikan-'].update(jikan)
try:
ui_data=ui_que.get_nowait()
except :
ui_data = None
if ui_data:
print(ui_data)
window['-status-'].update(ui_data)
window.close()
Data
に数字や文字を入れてSubmit
を押すと1番最初の文字が返ってきます。
同じようにData2
に数字や文字を入れてDo
を押す入力した文字列の長さが返ってきます。
layout
の中にあるsg.Output
はコードの中のprint
で書いたところが出力されます。submit
ボタンが押されると、worker1
のスレッドが立ち上がり、data
をdata_que
で送ります。worker1
で処理して、ui_que
で結果を送ります。
do
ボタンも同様の処理を行います。
前回の記事にもあるようにwindow.read
のtimeout
を利用して状態監視を行います。
timeout
はelif
をつかって明示的に書いています。(Pyhtonのポリシーとして明示的に書いた方が良いと思ったので。)
ui_que.get_nowait()
では、何も情報が入っていない時には、エラーを出します。エラーを受け取るためにtry-except
を使っています。
##まとめ
LabVIEWでよく使っていたキューメッセージハンドラデザインパターに似たものをPySimPleGUIで作ることができました。
もう少し使いやすくするために改良していこうかとも思います。