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

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

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

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

Tkinter

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

Q&A

解決済

1回答

1642閲覧

Python3 Tkinter 実行中、画面を一度、消して再表示したい。

ITOMO5963

総合スコア98

Python 3.x

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

Tkinter

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

0グッド

0クリップ

投稿2022/12/13 11:12

実現したいこと

以下の流れのプログラムを作成したいです。
①長い処理を実行中に[実行中UI]を表示。
②一定時間で、継続するかのUIを表示させる。
③「はい」を押下時は、再度、[実行中UI]を表示。
「いいえ」の時は、終了。
④長い処理後は、[完了UI]を表示し終了。

以下の問題があります。
並行処理で実行中に実行中UIを出すのは、あるあるなので、簡単かとおもいましたが、
Tkinterやthreadingについて初心者のため、うまく動作しません。
お力沿いお願いします。

発生している問題と気になる問題。

①続きを実行しますか?で「いいえ」を押下しても終了しない。
②もっと、シンプルにならないか。。。

該当のソースコード

python3

1 2import sys 3import threading 4import tkinter as tk 5from tkinter import messagebox 6import time 7 8 9def Centering(window): 10 window.update_idletasks() 11 width = window.winfo_width() 12 height = window.winfo_height() 13 frm_width = window.winfo_rootx() - window.winfo_x() 14 win_width = width + 2 * frm_width 15 titlebar_height = window.winfo_rooty() -window.winfo_y() 16 win_height = height + titlebar_height + frm_width 17 x = window.winfo_screenwidth() // 2 - win_width // 2 18 y = window.winfo_screenheight() // 2 - win_height // 2 19 window.geometry('{}x{}+{}+{}'.format(width, height, x, y)) 20 window.deiconify() 21 22class GUI: 23 # お待ちくださいUI 24 def wait_ui(self): 25 self.running = True 26 self.window1 = tk.Tk() 27 self.window1.resizable(0,0) 28 self.window1.geometry("380x80") 29 self.window1.attributes('-alpha', 0.0) 30 Centering(self.window1) 31 self.window1.attributes('-alpha', 1.0, '-topmost', True) 32 self.window1.title("実行中...") 33 label1 = tk.Label(self.window1, text="ただいま実行中", font=("Meiryo",11)) 34 label1.pack() 35 label2 = tk.Label(self.window1, text="しばらくお待ちください", font=("Meiryo",11)) 36 label2.pack(pady = 10) 37 self.window1.after(1000, self._check_to_quit) 38 self.window1.protocol('WM_DELETE_WINDOW', self.quit) 39 self.window1.mainloop() 40 41 # 完了しましたUI 42 def generate_end(self): 43 self.window2 = tk.Tk() 44 self.window2.resizable(0,0) 45 self.window2.geometry("300x70") 46 self.window2.attributes('-alpha', 0.0) 47 Centering(self.window2) 48 self.window2.attributes('-alpha', 1.0, '-topmost', True) 49 self.window2.title("完了! ! !") 50 label1 = tk.Label(self.window2, text="完了しました", font=("Meiryo",11)) 51 label1.pack() 52 button = tk.Button(self.window2, text=u"OK", command=self.program_end) 53 button.pack() 54 self.window2.mainloop() 55 56 57 # Program終了関数 58 def _check_to_quit(self): 59 if self.running: 60 self.window1.after(1000, self._check_to_quit) 61 else: 62 self.window1.destroy() 63 def quit(self): 64 self.running = False 65 def program_end(self): 66 sys.exit() 67 68 69 70### メイン関数 ### 71def main(): 72 73 # 開始 74 gui = GUI() 75 thread1 = threading.Thread(target=gui.wait_ui) 76 thread1.start() 77 78 # 長い処理1 79 time.sleep(5) 80 81 gui.window1.attributes('-alpha', 0.0) # 透明化 82 83 ret = messagebox.askyesno('確認', '続きを実行しますか?') 84 if ret == False: 85 gui.window1.quit() 86 gui.window1.destroy() 87 sys.exit() 88 89 90 gui.window1.attributes('-alpha', 1.0, '-topmost', True) # 透明化解除 91 # 長い処理2 92 time.sleep(5) 93 94 # 終了 95 gui.quit() 96 thread1.join() 97 gui.generate_end() 98 99if __name__ == '__main__': 100 main()

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

②もっと、シンプルにならないか。。。

現状でもシンプルだと思います。

①続きを実行しますか?で「いいえ」を押下しても終了しない。

まず、別スレッドで動かしているGUIを止めても、メインのプログラムは止まりません。スレッド側からメインのプロセスを強制的に止める方法は基本的にありません。
現在の方式で止めたいのであれば、GUI側のスレッドから、メインプロセスにメモリ(=変数)などで停止を伝えて、メイン側でそれをチェックして止めるのがいいでしょう。

だだ、現在の方式だとメインのプロセスは一本道なので途中で止められません。

自分ならこんな感じにします。

  • メインのプロセスは、GUIのプロセスにする。プログラムの進行を制御する。
    • 定期的に継続の確認ダイアログを出す(現状とだいたいおなじ)
      • 中断を選択されたら、スレッドに停止を指示する。
    • 定期的に「長い処理」スレッドの終了をチェック
  • 長い処理をスレッドで実行する。
    • 実行中に停止の指示があれば、処理を中断して適切に終了する造りにする。
    • 終了したら親プロセスに伝えるようにする。

追記

threadのちょっとした例です。
global変数stopをメインプロセスとスレッドで共有していて、メインで変更するとスレッドで検出できて止まるようになっています。

python

1import threading 2import time 3 4stop = False 5 6 7def func(): 8 global stop 9 while True: 10 if stop: 11 break 12 print('thread runnnig.') 13 time.sleep(3) 14 print('stopping') 15 16 17if __name__ == "__main__": 18 19 th = threading.Thread(target=func) 20 th.start() 21 print("press return to stop") 22 input() 23 stop = True 24 th.join()

投稿2022/12/13 11:36

編集2022/12/13 13:43
TakaiY

総合スコア12765

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

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

ITOMO5963

2022/12/13 11:57 編集

回答ありがとうございます。 > メインのプロセスは、GUIのプロセス 上記のように変更した際、「mainloop」実行中に定期的に「長い処理」スレッドの終了をチェック等できるのでしょうか? > 終了したら親プロセスに伝えるようにする 上記は、どのようなコードを書いたらよいでしょうか? スレッドからの戻り値は受け取れるのでしょうか?
TakaiY

2022/12/13 13:40

> 上記のように変更した際、「mainloop」実行中に定期的に「長い処理」スレッドの終了をチェック等できるのでしょうか? そこでも、after を使えばいいでしょう。 tkinterで定期的になにかをしたければ、afterです。 > どのようなコードを書いたらよいでしょうか? スレッドであれば、メモリ=変数 は共有しているので、スレッドで変数を変更すれば、親に伝わります。 検索すればいろいろ例は出てくると思いますが、ちょっとしたコードを書いてみました。
ITOMO5963

2022/12/13 13:49

ありがとうございます😭
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問