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

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

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

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

2547閲覧

tkinterを使って個別にプログラムを終了させたい

d.s.s

総合スコア1

Tkinter

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/09/02 12:51

編集2021/09/03 05:57

前提・実現したいこと

現在、Pythonを使い、GUIから複数プログラムの起動・終了を行えるものを開発中です。
そこで**、tkinterを使って起動させた複数の別のプログラム(.pyファイル)を個別に終了させたく、その手段を探しています。**
イメージ的には、個々の起動ボタンと終了ボタンが欲しい。という単純なものです。

全てのプログラムの終了は、sys.exit()を使い実装できました。しかし、個別のプログラムの終了方法が
なかなか見つかりません。

###現状の手段に至った経緯
別のアプローチとして、現状個別の.pyファイルとして分けているものを1つのpyファイルに纏めてはいいのではないか?という考えもあると思います。
しかし、このプログラムの用途上、「ひとつだけ止めて、そのプログラムだけ変数を変えてもう一度動かす」という需要があり、1つにまとめてしまってはそれを実現できないと私は考えています。

また、現状ではtkinterを使っていますが、目的に対し他のGUIライブラリの方が適切であれば、この限りではありません。

こういった目的に使えそうな手段があるかどうか、皆様のお知恵を貸していただけると幸いです。

###tkinterにより起動した各プログラムの概要
・無限ループをしている(プログラム自身に終点は無い)
・scheduleライブラリを使い定時実行している

補足情報(プログラム実行までの手順)

参考に、現状のプログラムを提示いたします。
上で解説した通り、「ボタンを押すと別のプログラムがsubprocess.Popenにより起動する」といったプログラムです。
※当初、「Threading.Threadにより~」と記載していましたが、誤りだったため修正しました。

import sys import tkinter import threading from tkinter.constants import tkinter.ttk from tkinter import ttk import subprocess def button_func_1(event): event.widget.config(fg="red") #ボタンの文字の色の設定 command = ["python",r"D:\python\15m_v03.py", "encoding='utf_8'"] proc = subprocess.Popen(command) print(event.widget.cget("text") + "を実行しました") def button_func_2(event): event.widget.config(fg="red") #ボタンの文字の色の設定 command = ["python",r"D:\python\30m_v03.py", "encoding='utf_8'"] proc = subprocess.Popen(command) print(event.widget.cget("text") + "を実行しました") def button_func_3(event): event.widget.config(fg="red") #ボタンの文字の色の設定 command = ["python",r"D:\python\1h_v03.py", "encoding='utf_8'"] proc = subprocess.Popen(command) print(event.widget.cget("text") + "を実行しました") def button_func_4(event): event.widget.config(fg="red") #ボタンの文字の色の設定 command = ["python",r"D:\python\4h_v03.py", "encoding='utf_8'"] proc = subprocess.Popen(command) print(event.widget.cget("text") + "を実行しました") def finish_menu(): # 終了ボタンをクリックしたときの動作の関数 sys.exit() # 操作画面の定義 root = tkinter.Tk() # このウィンドウの名前 root.title(u"起動メニュー") # タイトルバーの表記(操作メニュー) root.configure(bg="#ed9517") # メインフレームの作成と設置 frame = ttk.Frame(root,padding=5) frame.grid(column=0, row=0, padx=10, pady=10,) # ラベル label1 = tkinter.Label(frame, text='■■■通知システム■■■') label1.grid(row=0, column=0,padx=0,pady=10) # ボタンのインスタンス作成 button_1 = tkinter.Button(frame,text='時間:15分') button_1.grid(row=1, column=0,columnspan=2,padx=0,pady=10) button_2 = tkinter.Button(frame,text='時間:30分') button_2.grid(row=2, column=0,padx=0,pady=10,) button_3 = tkinter.Button(frame,text='時間:1時間') button_3.grid(row=3, column=0,padx=0,pady=10,) button_4 = tkinter.Button(frame,text='時間:4時間') button_4.grid(row=4, column=0,padx=0,pady=10,) # 終了ボタン button_finish = ttk.Button(frame,text='全て終了', command=finish_menu) button_finish.grid(row=5, column=0,padx=0,pady=10,) # ボタンクリック時のイベント設定 button_1.bind("<ButtonPress>", button_func_1) button_2.bind("<ButtonPress>", button_func_2) button_3.bind("<ButtonPress>", button_func_3) button_4.bind("<ButtonPress>", button_func_4) # スレッドの生成と開始 thread_1 = threading.Thread(target=button_func_1) thread_1.start() thread_2 = threading.Thread(target=button_func_2) thread_2.start() thread_3 = threading.Thread(target=button_func_3) thread_3.start() thread_4 = threading.Thread(target=button_func_4) thread_4.start() # メインループ root.mainloop()

###補足情報(FW/ツールのバージョンなど)
python 3.9.2

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

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

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

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

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

teamikl

2021/09/03 03:52

>別のプログラムがThreading.Threadにより起動する とありますが、別プログラムを起動しているのは ​subprocess.Popen です。 subprocess.Popen の場合は、処理をブロックしないので 「別プロセスと何かやりとりをしない限り」はスレッドは省くことができます。 subprocess.call を利用の場合は、処理をブロックする (GUIが応答なしになる)為、スレッド内で実行する必要があります。 ## 質問 - 別のプログラムは強制終了しても問題ないものですか - それとも、安全に停止する必要がありますか (リソースの解放等後始末が必要な場合)  → TakaiY さんの回答が該当します。  プロセス間通信で通知し、無限ループを抜けるように
d.s.s

2021/09/03 05:49

回答ありがとうございます。 >とありますが、別プログラムを起動しているのは ​subprocess.Popen です。 確かにそのようです。失礼いたしました。 - 別のプログラムは強制終了しても問題ないものですか 問題ありません。例えば、個別に起動してctrl+cで強制終了しても問題は起きません。 >プロセス間通信で通知し、無限ループを抜けるように こちらについては、shceduleモジュールによるループのため、何かアクションを起こしたら抜けられるというものでは無いと私としては認識しています。例えば一般的なwhileループのようにfalseを渡すとかbreakやcontinueで脱出できれば、それが可能なのですが…。
teamikl

2021/09/03 09:19

返信は回答に投稿しました。 報告のみですが、コードを実行した際のエラーを2点 - from tkinter.constants ... importがない - button_func_1(event): ... スレッドでの呼び出し時に event は渡されない
d.s.s

2021/09/03 14:57

ありがとうございます。 constantsの方は解決できましたが、eventの方のエラーについては前から出ており、解決法がまだ出ていない状況です。今後、解決法を考える予定です。
guest

回答2

0

ベストアンサー

別のプログラムは強制終了しても問題ないものですか

問題ありません。例えば、個別に起動してctrl+cで強制終了しても問題は起きません。

強制終了で問題なければ、proc.kill() で個別に終了できます。
内部ではシグナルが使われてます。os.kill(proc.pid, signal.SIGKILL)

shceduleモジュールによるループのため、何かアクションを起こしたら抜けられるというものでは無いと私としては認識しています。

ここに関しては、具体的なコードを元に話をした方が良いと思いますが、

  • 時間の掛かかる処理を実行中で、中断したい場合
  • スケジュール予約されたタスクを、実行前に取りやめたい場合

で内容が変わってきます。

前者は、その処理自体を中断可能な設計にするか、
プロセス自体を強制終了することになります。

後者は、scheduleライブラリ であれば、ループは独自に組むことができます。
(他のライブラリだとしても同様で、ループ部分が固定で内部を触れないとしても、
独自のループを組む為の方法が提供されてるはずです)

python

1 2while True: 3 schedule.run_pending() 4 time.sleep(1) 5

このようなループを組んでいるはずなので、
while True: の部分を、変数にすればループを中断できます。

但し、別プロセスから変数を直接操作することはできないので、
プロセス間通信等の手段を用いて通知し、
通知を受けて変数を変更するといった手順をとります。

  • subprocess モジュールでの起動では、

 情報を渡す手段が限られるので、プロセス間通信の手段に制限があります。

  • multiprocessing モジュールを使って実装すると、

 共有メモリの利用等、コード内で柔軟な対応が取れます。


他の手段について

別のGUIライブラリでは、別プロセスを扱いやすくした
クラスを提供されたりしますが、ライブラリ選定に関わる程の要素ではなく、
Pythonの標準ライブラリで代用の効く範囲です。

参考になりそうな技術としては、Job Queue (Task Queue) という仕組みが、
似たような用途の背後で良く使われます。

投稿2021/09/03 09:13

teamikl

総合スコア8760

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

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

d.s.s

2021/09/03 16:21 編集

ありがとうございます。 scheduleライブラリ自身にも抜けられる方法もあるのですね。 まず、ご提示のproc.kill()による強制終了から試してみたのですが、 proc.kill() を関数に対して与える事ができないのではないでしょうか。button_func_nというそれぞれの関数により別個のプログラムが実行されているので、 button_func_1のproc button_func_2のproc...以下略 となります。これらにそれぞれkill()の指示を出すにも、procがそれぞれの関数に内包されているため関数外から指示を与える事ができないという状況になってしまいます。 > 時間の掛かかる処理を実行中で、中断したい場合 >スケジュール予約されたタスクを、実行前に取りやめたい場合 これについては、前者が該当しそうです。時間がかかるもの(終わりの設定されていないループ処理)を中断と考えられます。 >このようなループを組んでいるはずなので、 >while True: の部分を、変数にすればループを中断できます。 との事ですが、これは、例えばプロセス間通信を使ってFalseを渡してもいいのでしょうか? この「変数にすれば」という部分について明確な理解ができませんでした。 GUIについては、現状のままでも要求を実現できそうとの事ですので、変更等せずに進めたいと思います。 素人のため初歩的な質問等あるかもしれませんが、何卒よろしくお願い致します。
teamikl

2021/09/04 05:54

> proc.kill() を関数に対して与える事ができないのではないでしょうか。 現状ではローカル変数で、直ぐに破棄されているので、 生成時に何処か(簡易的にはグローバル変数の辞書)に格納しておく必要があります。 注意点: 多重起動した場合の対処 ---- 中断については、subprocess を使う場合は proc.kill() が恐らく妥当な方法です。 制限として、終了時に何か実行したい場合は、プラットフォーム依存な挙動になるかもしれません。 (シグナルハンドラといった割り込み処理の機構がありますが、プラットフォーム制限有) なので、強制終了でも良い(終了時に何もする必要がない)といった場合に取れる手段です。 他の方法は、 プログラムの呼び出し自体を subprocess ではなく multiprocessing モジュールを使う方法に 変更する必要があります。 通常の変数は、別プロセスからは共有されてないのですが、 multiprocessing ではプロセス間でデータを共有できる、共有メモリを用いたデータ型が提供されます。 例えば、ループの制御であれば、multiprocessing.Event オブジェクトを用いて、 ループの一時停止・再開・中断を別プロセス側から操作する、といった実装も可能です。 ==== (1) proc オブジェクトをグローバル変数の辞書に保存する (2) proc.kill() による個別の終了 (3) 多重起動の抑制 (ボタンを複数回連打した場合の挙動) proc が上書きされないように。 multiprocessing の導入は、より柔軟な制御が必要になった場合に検討くらいで良いと思います。
d.s.s

2021/09/04 15:14

毎回丁寧にありがとうございます。 proc.kill()によりどうやら求めていた挙動は実現できそうです。 ひとつ気になる点として、実装してみたkillを実行すると TypeError: proc_kill_1() takes 0 positional arguments but 1 was given というエラーが出ます。ただプロセスは終了しているのでこのエラーは無視(というかやむを得ない)しても良いのか、それとも何か私の記述に問題があるのでしょうか? 実行までの段取りとしては、以下の通りです。 >(1) proc オブジェクトをグローバル変数の辞書に保存する が実現できていないような気もしますが、いかがでしょうか。 ~~~~~~~~~~ def button_func_1(event): event.widget.config(fg="red") command = ["python",r"D:\sample.py", "encoding='utf_8'"] proc = subprocess.Popen(command) global proc_1 proc_1 = proc def proc_kill_1(): proc_1.kill() button_kill_1 = tkinter.Button(frame,text='ひとつだけ終了', command=proc_kill_1) button_kill_1.bind("<ButtonPress>", proc_kill_1)
teamikl

2021/09/05 11:42 編集

bindで登録した関数には、呼び出し時に引数が渡されるので、 エラー自体は、引数を取れるようにすれば解消出来ます。 def proc_kill_1(event=None): 但し、エラーが出てる場合、関数は呼び出されてないはずなので エラーが出るがプロセスは終了しているのは別で、 command と bind の両方に登録しているのが原因です。 command の場合は引数は渡されないので、 この場合は、 bind の行を削除してください。
d.s.s

2021/09/05 15:09

ありがとうございます。bindにあるproc_kill_1を削除し、エラーは出なくなりました。 >(3) 多重起動の抑制 (ボタンを複数回連打した場合の挙動) proc が上書きされないように。 上記の挙動なのですが、tkinterの仕様で、bindを使っている場合はボタンのstateをDISABLEDにしても見かけ上押せないだけであり、実態としては連打してしまうと重複して押されてしまいます。 この点について、何かお知恵がありましたらご教示いただけると幸いです。質問が続いて恐縮ではありますが、何卒よろしくお願い致します。
teamikl

2021/09/06 05:13

A1. proc オブジェクトの有無で多重起動を判別する。  kill 時には後始末 global proc_1; proc_1 = None A2. command で設定すると state でのクリック防止が可能です。  event オブジェクト経由で button にアクセス出来なくなりますが、  任意の引数を渡して呼び出す方法があります。functools.partial を利用 def onclick(button):   button.config(state="DISABLED")   button.after(3*1000, lambda: button.config(state="NORMAL")) import functools button = tkinter.Button(root, text="Click") button.config(command=functools.partial(onclick, button))
d.s.s

2021/09/13 11:26

レスポンスが遅くなり申し訳ありません。ありがとうございます。 A1のような手段を使うことで、判別でき、なんとか求めていた機能を実装することができました。 今回はとても多くの質問に丁寧にお付き合いいただきまして、本当にありがとうございました。
guest

0

こういう場合によく採られる方法は、動作中のプロセスに対して、何らかのメッセージを投げて、止まってもらうことです。
メッセージにはいろいろな手段がありますが基本的にはプロセス間通信と言えます。

  • スレッドであれば、共有メモリを使って通信できます。子スレッドは、特定の変数などを監視していて、特定の値になったときに停止処理に入ります。
  • シグナルを送るという方法もよく採られます。 子プロセスは特定のシグナルを受けると停止処理に入ります。
  • キューを使います。以下同様
  • ファイルを使います。
  • ソケットを使います。

など、まあ、いろいろあります。

いずれにしても、親プロセスがメッセージを作成して、子プロセスはそれを監視していて見つけたら終了処理を行なうという感じです。

投稿2021/09/02 13:41

TakaiY

総合スコア13790

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

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

teamikl

2021/09/03 03:36

> スレッドであれば、共有メモリを使って通信できます。 「スレッド」では、同一プロセス内なので、「共有メモリ」は不要です。 別プロセスとの通信の場合ですね。 ※ 質問文にはスレッドを使ってとありますが、コードでは、 プログラム本体は subprocess.Popen で別プロセスで稼働してます。
TakaiY

2021/09/03 04:20

スレッドではメモリ上のデータは共有できて、それ"も"「共有メモリ」と言うと思ってたんですが、違うんですかね? だとすると、何と言えばいいんでしょう?
teamikl

2021/09/03 04:45

スレッドの場合は、元々メモリ空間を共有してるので 「共有メモリ」に対して、特に区別する為の名称はありません。 用語の定義の話ではなく、プロセス間通信の話をしてる文脈上、 プロセス間通信での「共有メモリ」と混同しない為に区別が必要なので、 説明としては 「プロセスでは」共有メモリを使って通信できます。「子プロセス」は~ とした方が適切です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問