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

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

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

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

Q&A

解決済

1回答

6467閲覧

Python Scheduleの実行中、UIから停止を受け付けることはできますか

saya24

総合スコア244

Python

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

0グッド

0クリップ

投稿2020/06/20 07:22

こちらの記事を参考に、予め定義しておく処理を定刻実行しようとしています。
TkInterのデスクトップアプリから、一連の定刻処理の起動をユーザに操作させる作りを考えています。

PythonのShceduleというと、起動ばかりがネットに紹介されている気がしますが
【ユーザの指示をもって これを停めるようなことはできないでしょうか?】

【ひょっとして....下記コードでループに入ると、TkInterのUIを一切受け付けなくなる・固まってしまう事象になるでしょうか???】

Python

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

あまりにも事例を見つけられないので、ひょっとして 非常識なことを尋ねているのでは??と若干不安です。
よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

1つのアプリケーション(プロセス)で「ジョブ」と「ジョブを制御する処理」がある場合、通常マルチスレッド化します。

Tkinter Window作成----------ジョブのStart/Stopを制御するスレッド------------> | -------ジョブを処理するスレッド ----------------------->

2つのスレッドで情報のやり取りを行う仕組みが必要になりますので、例えば共通の変数としてqueue.Queue()のインスタンスを用意し、制御するスレッドからqueue.put("STOP")で停止メッセージを送信し、ジョブを処理スレッド内でqueue.get()をメッセージを受け取り"STOP"を受け取ったら停止する、ということを行います。

複数のプロセス間でこれを行う場合、排他処理として空のファイルを作ることをやります。停止させたい場合にあるディレクトリに空ファイルを置くとプログラムが停止する、というような方式です。

サンプルソース

1秒毎に時刻を表示します。GUIでSTART/STOPが可能です。GUIの作成とスレッド作成を同じJobManagerクラス内に閉じ込めることで、メンバ変数を使ってstart/stopを制御しています。

[追記] teamiklさんの指摘事項(afterでループするとメインスレッドから呼ばれてしまうバグ)を修正しました。

python

1import datetime 2import schedule 3import threading 4import tkinter as tk 5import time 6 7MONITOR_PERIOD = 1 8 9 10def now(): 11 return datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M:%S") 12 13 14class JobManager: 15 def __init__(self, root): 16 self.root = root 17 self.running = True 18 self.job_enable = True 19 self.time = tk.StringVar() 20 21 # GUIの準備 22 frame = tk.Frame(root) 23 frame.pack() 24 button_start = tk.Button(frame, text="Start", command=self.start) 25 button_start.pack(side=tk.LEFT) 26 button_stop = tk.Button(frame, text="Stop", command=self.stop) 27 button_stop.pack(side=tk.LEFT) 28 button = tk.Button(frame, text="Quit", fg="red", command=self.quit) 29 button.pack(side=tk.LEFT) 30 label_time = tk.Label(root, textvariable=self.time) 31 label_time.pack() 32 33 # ジョブのスケジューリング 34 schedule.every(MONITOR_PERIOD).seconds.do(self.job) 35 36 # スレッドの開始 37 self.thread = threading.Thread(target=self.run_monitor) 38 self.thread.start() 39 40 def run_monitor(self): 41 while self.running: 42 schedule.run_pending() 43 time.sleep(0.2) 44 self.root.quit() 45 46 def job(self): 47 if self.job_enable: 48 self.time.set(now()) 49 50 def start(self): 51 self.job_enable = True 52 53 def stop(self): 54 self.job_enable = False 55 self.time.set("Job is disabled") 56 57 def quit(self): 58 self.running = False 59 60 61def main(): 62 root = tk.Tk() 63 JobManager(root) 64 root.mainloop() 65 66 67if __name__ == "__main__": 68 main() 69

投稿2020/06/21 01:31

編集2020/06/24 09:13
yymmt

総合スコア1615

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

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

saya24

2020/06/21 05:20

yymmtさん、ご見解をありがとうございます。 なんか見慣れない言葉がでてきて少々気がめいっていますが、まずはqueueをキーワードにネットで検索するところから始めたいと思います。 制御する側は TkInter側ですね。本当うまくいくと良いのですが
yymmt

2020/06/21 07:34

queueはあくまでも一例です。Tkinterとscheduleを使ったサンプルソースを追記しておきました。
saya24

2020/06/21 07:36

マルチスレッド化にすることで、スケジュールを停止させるための指示を UIもしくくは他の制御からを受け付けるようにする手立ては こちらの記事を参考に大方理解致しました。(https://teratail.com/questions/147319) SCHEDULEに登録した定刻タスクを取り除く、という措置は 単純に ループを終わらせてschedule.run_pending()の機会をとっぱらう、ということなのですね?? 勝手に.remove とか .killとか あるようなイメージを思い描いていて....なんか皆さんと違う感覚でおおりました 横やりを入れて、スレッド内のループを終わらせる、ということなのかなぁ....
saya24

2020/06/21 07:36

おっと、行き違い
yymmt

2020/06/21 07:45

いえ、ブロッキングが入る処理だとスレッドごと強制終了する必要があるため、常にこの方法が取れる訳ではないです。
saya24

2020/06/21 08:10

せっかく理解できた、と思ったら違いましたか。残念...ブロッキングが入る処理???ですか。 ますます難しくなってきまたしよ(汗) いづれにしも ご丁寧に例を示して頂きありがとうございます。只今理解に努めています。 (手元の環境でご提示のコードを試す上で 日時の部分は全角文字を除去した上で実行させて頂きました。なんか文字コードに起因するようなエラーになったので)
saya24

2020/06/21 11:55

マルチスレッドという言葉も分からなかった私に ご丁寧に事例を交えて説明頂き、なんとか初心者である私が目的を達成することができました。ありがとうございました。
teamikl

2020/06/24 00:52

サンプルの # スレッドの開始 ですけど、Tkinterのタイマー(after)の場合、 以降の run_monitor は mainloopからメインスレッドで呼び出されるので、これだと2回目以降の呼び出しがマルチスレッドになってません。 (→時間の掛かるジョブがあった場合、GUIのメインループは止まります) 確認方法: スリープ等のジョブを入れるとGUIがフリーズする。 run_monitor内で、print(threading.current_thread()) すると 実行中のスレッドが確認できます。 理由: Tkinterではイベントループがメインのひとつのみ、だからです。 例えば、Qt のタイマーだとスレッド毎にイベントループを持てるので、 別スレッド内でタイマー処理等も可能と、ライブラリ毎に事情が異なる部分です。 (本題の、質問に対しての回答として)中断のアプローチとしては問題ありません。 が、スレッドに関しては恐らく意図してない動作になってるので、確認して見て下さい。 スレッドを使わずに self.run_monitor() とした場合とほぼ(初回以外)変わらない挙動になってます。
saya24

2020/06/24 03:41

いつもお世話になっております。だんだんと話についていけなくなってきました.... 秒間隔のschedule.run.pendingの最中 UIが遮断されるから されないようにするため、threadingの導入を決定しました。よくスレッドの概念ができていないまま、24時にリスケしたいという次の課題に取り組んでしまい、また色々なリスクもご提案頂き 話が複雑化してきた印象をもっています。 ひとえに私の勉強不足です まず、スケジュールのクリアを、定刻処理の一つとして登録するよう対処してみます。 親スレッド子スレッドも まだよくわかっていないので、今回ご提示頂きました print(threading.current_thread()) は 試して実態をみたいところです。 まずは 引き続いてのご支援にお礼まで
yymmt

2020/06/24 03:50

> 例えば、Qt のタイマーだとスレッド毎にイベントループを持てるので、 > 別スレッド内でタイマー処理等も可能と、ライブラリ毎に事情が異なる部分です う、かなり昔に作ったQtのタイマーから適当に作ったソースというのがバレてしまいましたね。
yymmt

2020/06/24 09:15

threading.current_thread()を叩いて見たら1回目(子スレッド)と2回目以降(親スレッド)のスレッドが違っていてビックリしました。指摘事項をそのままにするのは気持ちが悪いのでソースコードを修正しました。
teamikl

2020/06/25 11:07

Qtでは自分は逆のパターンではまったことがありました。 Tkinter ではこれを利用して after() を 子スレッド → メインスレッド への 通信手段として使えるのですが、Qt(QThread) では同スレッド内で実行と。 タイマーを使う時は、 イベントループ (Tkinterでは mainloop) の存在を意識しないといけないなという教訓になりました。 ---- >だんだんと話についていけなくなってきました.... オフトピ&別の質問にあるコードでは修正済と同じ形なので大丈夫ですよ。 スレッドの確認方法についてはデバッグ時にも役に立つので、 マルチスレッドでプログラムを作るなら、覚えておいて損はないと思います。 print()で表示するのは暫定的な方法でしたが、 マルチスレッドでは複数のスレッドから同時書き込みがあった場合 というのを気にしなくてはいけなくなってくるので、実際にはprint代わりに スレッドでも安全に使える logging モジュールを使います。 使い方は長くなるので、モジュール名前の紹介のみになってしまいますが、 設定でスレッド名を表示といったカスタマイズも出来ます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問