6
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PySimpleGUIとmultithreadを使った例

Last updated at Posted at 2020-03-29

##はじめに
PySimpleGUIが使いやすいので、以前使っていたLabVIEWのデザインパターンの一つ「キューメッセージハンドラデザインパターン」を実現できないか取り組んだ結果です。

LabVIEWキューメッセージハンドラの説明
アプリケーションデザインパターン:生産者/消費者
【LabVIEW スキルアップ】デザインパターン”QMH”とは?

LabVIEWについてはWikiなどを見ていただければと思いますが、計測装置の制御プログラムでよく使われています。(最近はPythonに押されていますが。)
特徴は、グラフィカルプログラミングで、whileループを一つ書けば、1つのコアまたはスレッドを使う。なので、Whileループをいっぱい書けばマルチタスクになります。キューメッセージハンドラデザインパターンはWhileループ同士のデータのやり取りをキューで行うものです。

##環境
前回の記事 PySimpleGUIでの時刻表示(timeoutの使い方)と同じです。

Win10Pro
Anaconda
Python3.7

##やりたいこと
PySimpleGUIは、イベント(ボタンを押すとか、入力するとかのイベント)待機と、処理されたデータのUIへの表示を、受け持ちます。マルチスレッドを使って、受け取ったイベントを処理する関数へ、キューでデータを送ります。受け取ったデータをもとに関数は処理を行い、結果をキューで返します。
スレッド間通信(プロセス間通信)では、グローバル変数も使うことができますが、今回はキューを使っています。
図に書くと以下のようなものです。
simplegui_que.jpg

参考にしたデモプログラムは、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()

##実行例とコードの解説
psg_que.JPG

Dataに数字や文字を入れてSubmitを押すと1番最初の文字が返ってきます。
同じようにData2に数字や文字を入れてDoを押す入力した文字列の長さが返ってきます。

layoutの中にあるsg.Outputはコードの中のprintで書いたところが出力されます。submitボタンが押されると、worker1のスレッドが立ち上がり、datadata_queで送ります。worker1で処理して、ui_queで結果を送ります。
doボタンも同様の処理を行います。
前回の記事にもあるようにwindow.readtimeoutを利用して状態監視を行います。
timeoutelifをつかって明示的に書いています。(Pyhtonのポリシーとして明示的に書いた方が良いと思ったので。)
ui_que.get_nowait()では、何も情報が入っていない時には、エラーを出します。エラーを受け取るためにtry-exceptを使っています。

##まとめ
LabVIEWでよく使っていたキューメッセージハンドラデザインパターに似たものをPySimPleGUIで作ることができました。
もう少し使いやすくするために改良していこうかとも思います。

6
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?