tkintertとmultiprocessingの同居

#背景
どうも初投稿です．
私のことはさておき，世の中には沢山の情報があふれています．
ネットは便利ですが，探すの大変です．
何かしたいことがあって探して，これを読んでるあなた，ご苦労様です．

今回のpython(3.6)でtkinterとmultiprocessingを同居させてみたという話なんですが．
サンプルファイルあるだろうと思ってネットで検索して1日をつぶし，あきらめ．
(日本語の情報少なすぎじゃないですか?)
適当に書き始めて，なんかよくわかんない感じになって一日を費やした．
(日給1万だとしたら2万⇒いいマウスぐらい買えるな。。。)

今後私みたいな日本人が一人でも減るようにこの記事を初投稿にすることを決意したわけであります．
コードを公開するのは，自分の大切なモノを公開することなので，
露出狂ではない私にとっては，お恥ずかしいこと極まりないですが，勇気出します．

#目的
前置きはこのくらいにして，本題です．
pythonを初めて1年ぐらい経ちますが，Tkinterを最近使い始めました．
ユーザからの入力をイベントトリブンで受け取りやすくて重宝します．

一方で，pythonはpyserialやpython-canを使うと，いろんなものと簡単につながるので,
IoT時代(←まだ死語になってないですよね？@ユビキタス社会の子)には，重宝してます．
pyserialやpython-canなんですが他のデバイスからドンドン情報が飛んできます．
この飛んできた情報を，Tkinterで処理をしようとすると，.afterで再帰呼び出しをすることで処理できます．
ただ，処理する量が増えてくると,その処理に引っ張られて，
せっかくのTkinterのインターフェースにもっさり感が出てきます．
このもっさり感を取り除くために，処理内容をmultiprcessiongで並列化してやろうという話です．

#内容
今回記載のプログラムは，python3.6のWindows,Linuxで一応動作は確認済みです．
早速サンプルプログラムを以下に示します．

```python:multiTinker.py
import multiprocessing as mp
from multiprocessing import Process,Manager
import tkinter as tk
import os
from time import sleep

class MainFrame(tk.Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.grid()
        self.MainCounter = 0
        self.MainMsg = "main({}):{}".format(\
                             os.getpid(),self.MainCounter)
        self.SubMsg = "sub:"
        self.CreateWidgets()
        self.afterId = self.after(1000,self.Update)
        
        self.shareDict = Manager().dict()
        self.SubProcess = Process(target=SubProcess\
                                  , args=(self.shareDict,))
        self.SubProcess.start()
    def __del__(self):
        print("killing me")
        self.SubProcess.terminate()
    def CreateWidgets(self):
        self.MainLabel = tk.Label(self\
                ,text = str(self.MainMsg)\
                ,width=32)
        self.MainLabel.grid(row=1,column=1)
        self.SubLabel = tk.Label(self\
                ,text = str(self.SubMsg)\
                ,width=32)
        self.SubLabel.grid(row=1,column=2)
    def Update(self):
        self.MainCounter += 1
        self.MainMsg = "main({}):{}".format(\
                             os.getpid(),self.MainCounter)
        self.MainLabel.configure(text=self.MainMsg)
        self.SubMsg = "sub({}):{}".format(self.shareDict["PID"]\
                           ,self.shareDict["val"])
        self.SubLabel.configure(text=str(self.SubMsg))
        self.afterId = self.after(1000,self.Update)

def SubProcess(shareDict):
    i = 0
    while i<1000:
        i += 1
        shareDict["PID"] = os.getpid()
        shareDict["val"] = i
        print(i)
        sleep(0.1)

if __name__ == '__main__': 
    root = tk.Tk()
    app = MainFrame(master=root)
    app.mainloop()
    app.__del__()

    

```

上記プログラムをWindowsで実行したときのスクリーンショットを次に示します．

![sc.png](https://qiita-image-store.s3.amazonaws.com/0/258602/b8b5a674-3fc8-548a-e0ab-ed81c7b16e62.png)

コマンドプロンプトに表示されているのは，
SubProcess関数内から，出力されたprint(i)によるものです．
その上のtkウィンドウは，一秒間隔のUpdateメソッドで
画面更新を行っているウィンドウです.
左側はmainプロセス(tkinterのafterの再帰呼び出し)で
作成したメッセージであるカウンター値(カッコ内はプロセスID)です．
右側は，SubプロセスからのDictから作成したメッセージです．

プログラムの詳細は，要望がありそうだったら今後記載します．

#注意事項
上のサンプルプログラムは完ぺきではないかもしれません．
特にマルチプロセスで開始したSubProcessの終了が，
```app.__del___()```で絶対問題ないかと聞かれると遠くを見たくなります．
上記プログラムでは，SubProcessを有限ループにしていていますが，
通信系の処理をした場合，無限ループに変更されるでしょう．
その時いつまで動作しつづけるかはわかりません．
各自，確認しながら実装してみてください．