tkinterでSubprocess利用時の割り込み(中断)
PythonでのGUIフレームワークであるtkinterプログラムにて、Subprocessを利用したときに、各種割り込みを使う方法を記載。
割り込み方法
Subprocessによる実行中プロセスを中断する方法として、次のものを取り上げる。
- 専用ボタンクリック
- Ctrl-c
- Windows Close(”☓”クリック)
プログラム
見た目は次のようなもの。
「Start subprocess」クリックにより指定されたプログラムが実行され、「Stop subprocess」(専用ボタン)クリックにより、そのプログラムが中断する。また、実行中に、「Ctrl-c」入力、右上の”☓”()クリックでもプログラムを中断させる。なお、ここでは、中断=プログラム終了とした。
ソースコード
ポイントを中心に説明。なお、実行結果はコンソールに出力(print())している。
メイン
import signal
import tkinter as tk
def check():
root.after(500, check) # time in ms.
signal.signal(signal.SIGINT, handler) # ctrl-c from console
root = tk.Tk()
root.after(500, check) # time in ms.
tk.Button(root, text="Start subprocess", command=start).pack()
tk.Button(root, text="Stop subprocess", command=stop).pack()
root.bind('<Control-c>', ctrlC)
root.protocol("WM_DELETE_WINDOW", stop)
root.mainloop()
print("exited")
各種定義およびtkinter。
- コンソールで「Ctrl-C」をCatchしやすくするための関数(check())および呼び出し
- コンソールで「Ctrl-C」が入力されたときに呼び出される関数(ハンドラ(handler))
- ”Start”および”Stop”用ボタン
- GUI(tkinter)上で「Ctrl-C」が入力されたときの関数(ctrlC)
- Windows Close時の関数(stop)
- 終了時にコンソールに表示されるメッセージ(exited)
check()については、下記サイト情報を利用。
Subprocess実行および中断
from subprocess import Popen, PIPE, STDOUT
cmd = ['ping', '-t', '192.168.10.1']
def stop():
print("stop")
killProcess()
def start():
global proc
print("start")
proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
- Subprocess(cmd):pingの無限実行(オプション:-t)を実施
- Start関数:コンソール表示(start)およびPoepnによるSubprocess実行
- Stop関数:コンソール表示(stop)およびSubprocess中断関数(killProcess)呼び出し
Ctrl-c
def handler(signum, root):
print("handler")
killProcess()
def ctrlC(event):
print("ctrlC")
killProcess()
- GUI上での「Ctrl-c」:コンソール表示(ctrlC)および中断関数(killProcess)呼び出し
- コンソール上での「Ctrl-c」:コンソール表示(ctrlC)および中断関数(killProcess)呼び出し
リアル中断関数
def killProcess():
if 'proc' in globals():
proc.terminate()
proc.wait()
output, err = proc.communicate()
output = output.decode('shift_jis')
print(output)
root.destroy()
- Subprocess実行時:プロセス中断(terminate)、実行結果の取得(communicate)、コンソールへの出力
- tkinterプログラムの終了(root.destroy())
全体
from subprocess import Popen, PIPE, STDOUT
import signal
import tkinter as tk
cmd = ['ping', '-t', '192.168.10.1']
def killProcess():
if 'proc' in globals():
proc.terminate()
proc.wait()
output, err = proc.communicate()
output = output.decode('shift_jis')
print(output)
root.destroy()
def handler(signum, root):
print("handler")
killProcess()
def ctrlC(event):
print("ctrlC")
killProcess()
def stop():
print("stop")
killProcess()
def start():
global proc
print("start")
proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
def check():
root.after(500, check) # time in ms.
#
signal.signal(signal.SIGINT, handler) # ctrl-c from console
root = tk.Tk()
root.after(500, check) # time in ms.
tk.Button(root, text="Start subprocess", command=start).pack()
tk.Button(root, text="Stop subprocess", command=stop).pack()
root.bind('<Control-c>', ctrlC)
root.protocol("WM_DELETE_WINDOW", stop)
root.mainloop()
print("exited")
結果
いずれも期待どおりの動作。
実行中に「Stop subprocess」クリックまたはWindows Close(”☓”クリック)
start
stop
192.168.10.1 に ping を送信しています 32 バイトのデータ:
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
exited
なお、ここでは、リアルタイムにコマンド(ping)の出力が表示されるのではなく、中断後に出力される(プログラムの構成上)。以下、同様である。
実行中にGUI上で「Ctrl-c」入力
start
ctrlC
192.168.10.1 に ping を送信しています 32 バイトのデータ:
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
exited
実行中にコンソール上で「Ctrl-c」入力
start
handler
192.168.10.1 に ping を送信しています 32 バイトのデータ:
192.168.10.1 からの応答: バイト数 =32 時間 =1ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =3ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =1ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =2ms TTL=63
192.168.10.1 からの応答: バイト数 =32 時間 =1ms TTL=63
192.168.10.1 の ping 統計:
パケット数: 送信 = 8、受信 = 8、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
最小 = 1ms、最大 = 3ms、平均 = 1ms
Ctrl+C
exited
実行前の中断(おまけ)
stop
exited
ctrlC
exited
handler
exited
参考にしたリンク
EOF