質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

2回答

4796閲覧

Python3 Tkinter アプリケーションを開いてTkinterを閉じてもPythonが終了しない

person

総合スコア224

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

0クリップ

投稿2021/08/03 10:56

編集2021/08/03 11:33

Python3 Tkinter ボタンを押したら実行中アプリウィンドウを最前面に移動したい

で質問した時のソースコードについて、問題点が後から見つかりました。

実行するとボタンが配置されたTkinterウィンドウが表示されます。
ボタンを押すとIEを開きます。
IEを開いたままTkinterを閉じるとPythonが終了せず、タイムアウトの例外のprint()が表示されます。

一応、threadにdaemon=Trueをつければ強制終了するのですが、
なぜIE開いたままでTkinterを閉じるとPythonが終了しないのでしょうか。

Python

1import subprocess 2import threading 3import tkinter as tk 4 5def on_button(): 6 thread = threading.Thread(target=control_task) 7 thread.start() 8 9def control_task(): 10 ret = check_task() 11 if ret == 1: 12 show_ie_on_the_front() 13 elif ret == 0: 14 open_ie() 15 else: 16 print("command error") # ここに該当する可能性はないと思っている 17 18def check_task(): 19 cmd = 'tasklist /fi "imagename eq iexplore.exe"' 20 outs, errs = mypopen(cmd, 3) 21 # Popenのreturncodeで実行の有無の判別はできない(どちらも正常に実行したら 0)ので出力をチェック 22 if outs: 23 data = outs.split("\n") 24 for i, row in enumerate(data): 25 if "iexplore.exe" in row: 26 return 1 27 return 0 28 return -1 29 30def show_ie_on_the_front(): 31 print("show_ie_on_the_front") 32 33def open_ie(): 34 cmd = "C:\Program Files\Internet Explorer\iexplore.exe" 35 ret = mypopen(cmd, 3) 36 print(ret) 37 38def mypopen(cmd, time): 39 outs = b"" 40 errs = b"" 41 42 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 43 44 try: 45 #proc.wait(timeout=time) 46 outs, errs = proc.communicate(timeout = time) 47 except subprocess.TimeoutExpired as e: 48 print(type(e)) 49 print(e) 50 proc.kill() 51 outs, errs = proc.communicate() 52 53 outs = outs.decode("cp932") 54 errs = errs.decode("cp932") 55 56 return outs, errs 57 58 59if __name__ == "__main__": 60 root = tk.Tk() 61 62 button = tk.Button(root, text="Open", width=10, command=on_button) 63 button.grid() 64 65 root.mainloop()

その時の表示
(例外処理にあるprint()2つはTkinterの閉じるボタンを押して約3秒後ぐらいに表示され、IEを閉じると('', '')が表示された。)

PS C:\Users\ユーザ名> & C:/Users/ユーザ名/AppData/Local/Programs/Python/Python38/python.exe c:/Users/ユーザ名/Desktop/test.py <class 'subprocess.TimeoutExpired'> Command 'C:\Program Files\Internet Explorer\iexplore.exe' timed out after 3 seconds ('', '')

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

一応、threadにdaemon=Trueをつければ強制終了するのですが、
なぜIE開いたままでTkinterを閉じるとPythonが終了しないのでしょうか。

非daemonスレッドの正常な挙動です。

  • ボタンを押すとIEを開きます ... myopen がサブスレッドで呼び出される
  • Tkinterを閉じる ... メインスレッド が終了します。サブスレッドは稼働中。

サブスレッドは非daemonスレッドなので、メインスレッド終了時に強制終了されず、
プロセスは生存したまま、サブスレッドの終了を待ちます。

厳密には、show_ie_on_the_front() が呼び出される時と open_ie() のときで挙動が変わるはずです。
前者の場合は、「ブロッキング処理」がないので、直ぐに実行は終わるはずです。
後者は、mypopen関数内の proc.communicate が「ブロッキング処理」で、
プロセスの終了もしくはタイムアウトの間待機します。

解決策は、
特に解消が必要なリソースがないのなら(Popenで開いたIEをそのままにしても良いなら)
daemonスレッドでも問題有りません。

強制終了したくない場合(この場合、Popenで開いたIEを Pythonスクリプト終了時に終了させたい)場合は、
サブスレッドで実行する時間のかかる(ブロッキング)処理を中断できるように設計する必要があります。

終了時の問題に関していうと、
要点は、「ブロッキング処理」が何処かで待機中になって、処理が終わらない状態になってるため、
ブロッキング処理が発生しないような実装にする事が解決に繋がります。

解決案A: 2回目の communicate に timeout を設定する。(例外補足等も対応の必要あり)
もしくは、communicate を呼ばずに、エラーや出力を無視する。

(非daemonスレッドで) Popenを使い communicate を timeout なしで呼び出すと、
IE のプロセスが終わるまで(※問題点・後述)、Pythonも終われない構造になってしまいます。

解決案B: 非daemonスレッド内ではIEは起動のみにして、
メインスレッドの最後で、IEが起動中であれば終了するようにする。


恐らく、コードの意図と実際の挙動が異なっている点

IE はマルチプロセスなアプリケーションなので、
proc.kill() ではアプリケーション終了にはなりません。
kill 後もウィンドウは残ったままになってるはずです。→ 後の communicate() でブロッキングが発生。

動作検証用

diff

1- outs, errs = proc.communicate() 2+ # outs, errs = proc.communicate()

他の解決策。大幅な変更が必要なため、コード例は示せませんが参考に

  • threading.Event を使うと、「中断可能な待機処理」を実現できます。
  • 非同期版 asyncio.subprocess を使う。tkinter も非同期IOのイベントループで動かす必要あり。

投稿2021/08/05 08:51

編集2021/08/05 08:52
teamikl

総合スコア8760

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

person

2021/08/06 12:54

一旦daemonで様子見して、変な挙動したら変更試みます。
guest

0

同一プロセスからのスレッドだから、ではないでしょうか。
このあたりの記事に図解などあるのでイメージしやすいかと思います。
https://www.atmarkit.co.jp/ait/articles/1410/30/news150.html
https://imokuri123.com/blog/2013/12/difference-between-process-and-thread/

投稿2021/08/03 12:45

odataiki

総合スコア973

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

person

2021/08/04 09:44

では、今回のようにTkinterから各アプリケーションを起動する際は、マルチプロセスにしたほうがいいのでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問