def関数を別のdef関数から停止させたい
解決済
回答 2
投稿
- 評価
- クリップ 1
- VIEW 1,144
現在pythonのtkinterを用いてプログラムを実行停止するようなアプリを作っているのですが、
実行中のdef関数を別のdef関数から停止させることは可能ですか?
def button_start():
while True:
#各種処理
def button_stop():
sys.exit(0) #的な?
# Startボタン
button2 = ttk.Button(frame2, text='Start', command=button_start)
button2.pack(side=LEFT)
# Cancelボタン
button3 = ttk.Button(frame2, text='Cancel', command=button_stop)
button3.pack(side=LEFT)
大雑把にこんな感じで書いたのですが、予想通り止めることはできず、キャンセルボタンを押すと
tkinterのguiが応答なしになります。(pythonプログラムは実行されている)
global変数でフラグを作ってdef button_startの中にif文(フラグ判定)で終了させるようなことも試したのですが
うまくいかず、pyhtonはまだ勉強したてなので
どうかご教授を、、、
調べてもこれ!!って感じの見つからなくて質問したほうが早いかなぁ、、、なんて
よろしくお願いいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
button_start がメインスレッド上で実行されるので、
while ループが終わるまでは他の処理は実行されません。
メインループ → button_start → メインループ →
と順番に実行されるので、
button_start 内で時間の掛かる処理を行い、
メインループに処理が戻らないと応答なしになります。
GUI等のイベント駆動型プログラミングで、よくある誤解なのですが、
イベントハンドラの関数(button_start)は、
勝手に裏で同時に実行される訳ではありません。
イベントハンドラ内でループを使う場合は、
スレッドを使う等が一般的な解決策です。
他に幾つか解決案を紹介します。
ウインドウが固まらないように、ループ内で定期的に update() を呼ぶ
これは本当に一時的な回避策。使える状況は限られますが、有効な場合も有。別スレッド/プロセスを作り、ループを外部から中断できるフラグを仕込む。
y_waiwaiさん提案の方法に該当します。タイマーを使う
after や after_idle を使って、
イベントループの合間に任意の処理を実行します。
但し、1回の処理は、直ぐに終わる程度に分割が必要です。ジェネレーター(とタイマー)を使う
同上ですが、ジェネレータを使ってループ文で記述可能な方法。非同期ライブラリ (asyncio等) を使う
1と3、4 は、ネットワークやデータベースが絡む処理、
時間の掛かる計算処理には向きません。
簡単なアニメーション等やスケジューリングの用途には使えます。
2 のスレッドは汎用的な方法ですが、別スレッドからGUIにアクセスする際は、
もうひと工夫が必要なので注意。
「各種処理」の内容次第では、適切な解決策は変わってきます。
1, 2, 3 は探せばサンプルコードは見つかると思いますので、
4 の方法のみコードを紹介します。
5 は選択肢として言及したものの、長くなるので割愛。
興味があれば、「非同期プログラミング」について調べて見て下さい。
import tkinter as tk
from tkinter import ttk
def after_timed_gen(root, gen, done=None, _stop=None):
"""
ジェネレーターをTkinterのタイマーで消化する関数
after() に指定する 遅延ミリ秒を yield で指定できます。
"""
def next_gen():
interval = next(gen, _stop)
if interval is not _stop:
root.after(interval, next_gen)
else:
if done:
done()
root.after_idle(next_gen)
return gen
if __name__ == "__main__":
root = tk.Tk()
label = ttk.Label(root)
label.pack()
def countUp(count=0):
while True:
count += 1
label.config(text="Count: {}".format(count))
# メインスレッド内で GUIの応答を止めることなく、
# time.sleep(1) としたい場合の代替策です。
yield 1*1000
gen = None
def button_start():
global gen
gen = after_timed_gen(root, countUp())
def button_stop():
global gen
if gen:
gen.close()
gen = None
button1 = ttk.Button(root, text='Start', command=button_start)
button1.pack()
button2 = ttk.Button(root, text='Cancel', command=button_stop)
button2.pack()
root.mainloop()
スレッドを使う方法の停止・中断デモ source
※ブラウザ上で実行できます
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
-3
グローバル変数でbool変数を定義しておき、
ループの実行をその変数で回すようにしとけば、
もう片方の関数で、その変数をfalseにすれば、そのループを止めれますね
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.09%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2020/07/24 08:19
GUIプログラムは初めて書くのでとっても参考になりました。
現在作っているプログラムは処理が長いのでスレッド型を採用したいと思います。
またその他の解決案も一つ一つ、実践してみようと思います。
この度は本当にありがとうございます!