はじめに
独立したプロセス間通信の応用例として、ダイアローグボックスを表示するプロセスとの通信をやってみました。
ダイアローグボックスを表示するプロセス(dialogsrv)は、表示を要求するプロセス(dialogcli)からの要求を待ちます。そして、依頼された文字列を表示して、入力待ちになります。文字列入力後、'ok'ボタンをクリックされると入力された文字列をdialogcliへ返します。もし、'cancel'をクリックされた場合には、文字コード'\18'を返します。
dialogcliから文字コード'\04'をdialogsrvへ送ることにより、dialogsrvは処理を終了します。
具体的に
ダイアローグの表示には、tkinterを使用しています。
ダイアローグを表示するプロセス
ダイアログを表示するプロセスは、起動されるとダイアローグを作成した後、ソケット通信で表示要求を待ちます。表示要求が来るとその文字列をラベルに表示した後、root.mainloop()で入力待ちのループに入ります。
ダイアローグには、'ok'ボタンおよび'cancel'ボタンがクリックされた際の処理を宣言しており、それぞれのボタンに応じた処理を行ったのち、root.quit()で入力ループから抜けます。そして、次のソケット通信による要求待ちになります。
ソケット通信での要求時に、文字コード'\04'を受け取ると root.destroyでダイアローグを破棄して、プロセスを終了します。
import multiprocessing as mp
import tkinter as tk
import tkinter.simpledialog as simpledialog
import tkinter.messagebox as tkmsg
import time
from socket import socket, AF_INET, SOCK_STREAM
HOST = 'localhost'
PORT = 51000
MAX_MESSAGE = 2048
NUM_THREAD = 4
CHR_CAN = '\18'
CHR_EOT = '\04'
class Dialog(mp.Process):
#----------------------------------------------------
def __init__(self):
mp.Process.__init__(self)
#----------------------------------------------------
def send_result(self, mess):
while True:
try:
# connect
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT + 1))
# send message
sock.send(mess.encode('utf-8'))
# disconnect
sock.close()
break
except:
print ('retry: ' + mess)
#----------------------------------------------------
def showDialog(self, mess):
self.root = tk.Tk()
self.root.title('scratch')
self.root.minsize(width = 500, height = 250)
self.frame = tk.Frame(self.root, width = 300, heigh = 200, bd = 11)
self.frame.pack()
self.label = tk.Label(self.frame, font = ("", 20), text = mess)
self.label.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5)
self.lbl1 = tk.Label(self.frame, font = ("", 20), text = '')
self.lbl1.grid(row = 1, column = 0)
self.entry = tk.Entry(self.frame, font = ("", 20), bd = 4)
self.entry.grid(row = 2, column = 0, columnspan = 3, padx = 5, pady = 5)
self.entry.focus()
self.lbl2 = tk.Label(self.frame, font = ("", 20), text = '')
self.lbl2.grid(row = 3, column = 0)
#+++++++++++++++++++++++++++++++++++
def click():
answer = self.entry.get()
self.send_result(answer)
self.root.quit()
# self.root.destroy()
self.button1 = tk.Button(self.frame,font = ("", 20), width = 4, height = 1, text = 'ok', fg = "blue", command = click)
self.button1.grid(row = 4, column = 0, sticky = tk.E )
#+++++++++++++++++++++++++++++++++++
def cancel():
answer = CHR_CAN
self.send_result(answer)
self.root.quit()
# self.root.destroy()
self.button2 = tk.Button(self.frame,font = ("", 20), width = 7, height = 1, text = 'cancel', fg = "red", command = cancel)
self.button2.grid(row = 4, column = 2, sticky = tk.W)
# self.root.mainloop()
#----------------------------------------------------
def messDialog(self, mess):
self.label.configure(text=mess)
#----------------------------------------------------
def loopDialog(self):
self.root.focus()
self.root.mainloop()
#----------------------------------------------------
def destroyDialog(self):
self.root.destroy()
#----------------------------------------------------
def run(self):
proc_name = self.name
# connect
sock = socket(AF_INET, SOCK_STREAM)
sock.bind ((HOST, PORT))
sock.listen (NUM_THREAD)
print ("receiver ready, NUM_THREAD = " + str(NUM_THREAD))
self.showDialog('start of scratch dialog')
# loop
while True:
try:
conn, addr = sock.accept()
mess = conn.recv(MAX_MESSAGE).decode('utf-8')
conn.close()
# exit ?
if (mess == CHR_EOT):
self.destroyDialog()
break
# text
self.messDialog(mess)
self.loopDialog()
except:
print ('Error:' + mess)
# disconnect
sock.close()
return
# ----------------------------------------------------
def proc():
# start dialog process
dialog = Dialog()
dialog.start()
# wait for the end of process
dialog.join()
if __name__ == '__main__':
proc()
起動は、dialogsrv.proc()です。
ダイアローグへ表示を依頼するプロセス
ダイアローグへ文字列の表示要求をするプロセスは、表示する文字列を準備した後、ソケット通信を使って文字列を送信します。その後、ダイアローグからの応答を待ちます。
ここでは、簡単のため、10回のループで'message:n' (n:0~9)を表示しています。
import multiprocessing as mp
import tkinter as tk
import tkinter.simpledialog as simpledialog
import tkinter.messagebox as tkmsg
import time
from socket import socket, AF_INET, SOCK_STREAM
HOST = 'localhost'
PORT = 51000
MAX_MESSAGE = 2048
NUM_THREAD = 1
CHR_CAN = '\18'
CHR_EOT = '\04'
class DialogIF(mp.Process):
#----------------------------------------------------
def connect(self):
# connect
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.bind ((HOST, PORT + 1))
self.sock.listen (NUM_THREAD)
print ("receiver ready, NUM_THREAD = " + str(NUM_THREAD))
return
#----------------------------------------------------
def dequeue(self):
try:
conn, addr = self.sock.accept()
mess = conn.recv(MAX_MESSAGE).decode('utf-8')
conn.close()
# cancel ?
if (mess == CHR_CAN):
print ('cancel')
# text
print ('text> ' + mess)
except:
print ('Error:' + mess)
return mess
#----------------------------------------------------
def disconnect():
self.sock.close()
return
#----------------------------------------------------
def enqueue(self, mess):
while True:
try:
# connect
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
# send message
sock.send(mess.encode('utf-8'))
# disconnect
sock.close()
break
except:
print ('retry: ' + mess)
#----------------------------------------------------
def terminate(self):
self.enqueue(CHR_EOT)
# ----------------------------------------------------
def proc():
dif = DialogIF()
dif.connect()
for i in range(10):
dif.enqueue('message:' + str(i))
r = dif.dequeue()
if r is None:
break
if (r == CHR_CAN):
print ('cancel')
else:
print ('result> ' + r)
dif.terminate()
if __name__ == '__main__':
proc()