f-ym
@f-ym

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

pythonのマルチプロセスでのデータ送受信についての質問

解決したいこと

pythonでマルチプロセスのプログラムを作っていますが、pipeを使用したデータ受け渡しがうまくいきません
image.png

実行ボタンを押すと答えがラベルに連続的に表示されることを目指しています。
解決方法をご教授お願い致します。

発生している問題・エラー

親プロセス(GUI)でデータが受け取れない
無題1.png
子プロセスからのデータ送出はできているようですが、親プロセスでの受信ができません

該当するソースコード

from multiprocessing import Pipe
import multiprocessing as mp
from tkinter import *
from tkinter import ttk
import time
#-------------------------------------------
#  コマンドの取得
#-------------------------------------------
def get_cmd():
    print('コマンドボタン 押下')
    cmd = cmd_get_ent.get().strip()
    cmd_get_ent.delete(0, END)      # テキストボックスをクリア
    cmd_l = [x.strip() for x in cmd.split(',') if not x.strip() == '']
    if not(len(cmd_l) == 0):
        if cmd_l[0] == '加算':
            add_Process(cmd_l)
        elif cmd_l[0] == '減算':
            pass
    print('コマンドボタン 押下処理終了')
# -------------------------------------------
#  プロセス 起動
# -------------------------------------------
def add_Process(cmd_l):
    print('プロセス作成')
    ans_get = mp.Process(name='gps_get', target=add, args=(s_pipe1,cmd_l))
    ans_get.daemon = True
    ans_get.start()
#    print(m_pipe1.recv())  #一回目だけ受信
# -------------------------------------------
#  プロセス
# -------------------------------------------
def add(s_pipe1,cmd_l):
    for i in range(5):
        x=int(cmd_l[1])+int(cmd_l[2])+i
        s_pipe1.send(x)
        print('データ送出',x)
        time.sleep(1)

if __name__ == "__main__":
# 
    m_pipe1, s_pipe1 = Pipe()  # 双方向Pipeを生成
# rootの作成
    root = Tk()
# Frameの作成
    frame = ttk.Frame(root, padding=1,borderwidth=10, relief='solid')
    frame.pack(fill='x')
#
    var1 = StringVar()
    var1.set("ans=")
    lat_lbl = ttk.Label(frame,textvariable=var1, relief='solid')
    lat_lbl.pack(fill='x', pady=5)
#
    cmd_get_ent = ttk.Entry(frame)
    cmd_get_ent.pack(fill='x', pady=5)
#
    cmd_get_btn = ttk.Button(frame,command=get_cmd, text="実 行")
    cmd_get_btn.pack(fill='x', pady=5)

    cmd_get_ent.insert(0,'加算,1,2')
# ループ実行
    root.mainloop()


自分で試したこと

・ラベルのStringVarをグローバル変数にしてみた
・データ受信は子プロセスを作成した関数のみで受信できた

0

2Answer

PIPEは2つのプロセス間通信なので、この場合は、multiprocessing.Queueを使うのがいいのではないかと思います。

0Like

Comments

  1. @f-ym

    Questioner

    ありがとうございました

tkinter なら、update を使って定期的にデータ受信をポーリングすればいいですよ。

import time
from multiprocessing import Process, Pipe
from tkinter import Tk, ttk, StringVar


def execute():
    """
    コマンド実行
    """
    print('コマンドボタン 押下')
    args = [arg for arg in map(str.strip, cmd.get().split(',')) if arg]
    cmd.set("")
    if args:
        if args[0] == '加算':
            start_proc(add, args)
        elif args[0] == '減算':
            pass
    print('コマンドボタン 押下処理終了')



def start_proc(func, args):
    """
    プロセス起動
    """
    print('プロセス作成')
    proc = Process(name=func.__name__, target=func, args=(pipe_snd, args))
    proc.daemon = True
    proc.start()



def add(pipe, args):
    """
    加算プロセス
    """
    ans = int(args[1]) + int(args[2])
    for data in range(ans, ans + 5):
        pipe.send(data)
        print('データ送出', data)
        time.sleep(1)


def receive():
    """
    受信処理
    """
    if pipe_rcv.poll():
        data = pipe_rcv.recv()
        print('データ受信', data)
        ans.set(data)
    root.after(200, receive)


if __name__ == "__main__":
    pipe_rcv, pipe_snd = Pipe()  # 双方向Pipe生成

    root = Tk()
    frame = ttk.Frame(root, padding=1, borderwidth=10, relief='solid')
    frame.pack(fill='x')

    ans = StringVar()
    ans.set("ans=")
    ans_lbl = ttk.Label(frame, textvariable=ans, relief='solid')
    ans_lbl.pack(fill='x', pady=5)

    cmd = StringVar()
    cmd.set('加算,1,2')
    cmd_ent = ttk.Entry(frame, textvariable=cmd)
    cmd_ent.pack(fill='x', pady=5)

    exe_btn = ttk.Button(frame, command=execute, text="実 行")
    exe_btn.pack(fill='x', pady=5)

    root.after(200, receive)
    root.mainloop()
0Like

Comments

  1. @f-ym

    Questioner

    回答ありがとうございます。
    ご指摘の点を別途作成中のプログラムに実装しても当初上手く送受信できませんでしたが、別の要因がありそれを修正すると無事に希望通りの動作が実現できました。
    回答いただいたコードには、本題以外にも参考になる記述方法があり大変役に立ちました。
    ただ、一つProcess(name=func.__name__,の部分が調べても理解ができませんでした。
    お時間がありましたら解説をいただけませんでしょうか?
  2. モジュール、関数、クラス が `__name__` 属性を持っています。
    引数に関数を渡せば、その関数名がプロセス名になります。

    ```py
    >>> __name__
    '__main__'
    >>> import math
    >>> math.__name__
    'math'
    >>> def sample():
    ... return "Hello"
    ...
    >>> sample.__name__
    'sample'
    ```
  3. @f-ym

    Questioner

    早速にありがとうございます。
    デバッグ時等に役に立つことがわかりました。

Your answer might help someone💌