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

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

ただいまの
回答率

87.38%

Tkinterのエラーの対処方法がわかりません

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,018

score 148

質問

Tkinterでリアルタイムで時間を表示するプログラムを作ろうと思って、Threadを用いてプログラムを作ってみました。

実際動作を確認できたので良かったと思って終了した(Tkinterのウィンドウを閉じた)ところ、エラーが出てしまいました。

発生しているエラー(RuntimeError: main thread is not in main loop)は、英語の記事にはいくつか出ていて、destroy()を使う対処法(というよりはIDLEでのエラー表示をウィンドウを閉じることで、無かったことにしようとしている?)があったのですが、ターミナル等で実行するとやはりエラーメッセージが表示されるので、適切な対処方法を教えていただきたいです。

自分は、最初はThreadが原因かと思ったのですが、Threadを用いないと時間の表示ができないのでThreadは残しておこうと思いました。

該当のソースコード

import tkinter as tk
import datetime
import time
import threading

#ウィンドウ作成
win = tk.Tk()
win.title("Tkinter Test")
win.geometry("720x480")

#現在日時を表示
time_label = tk.Label(win,text="")
time_label.grid()

#現在日時の更新
def print_nowtime():
    while True:
        now = datetime.datetime.now()
        time_label["text"] = "{0:%Y/%m/%d %H:%M:%S}".format(now)
        time.sleep(1) #動作が重くなるため


def main():

    #ボタンが押されたらテキストボックス内のの文字列を表示
    def push():
        str = txtbox.get()
        print(str)

    txtbox = tk.Entry()
    txtbox.grid()
    btn = tk.Button(win, text="click", command=push)
    btn.grid()



if __name__ == "__main__":

    #時間表示と入力文字列表示を並行
    try:
        thread_1 = threading.Thread(target=print_nowtime)
        thread_2 = threading.Thread(target=main)
        thread_1.start()
        thread_2.start()
    #とりあえず例外が生じたら弾く(試験的)
    except:
        print("except")
    win.mainloop()

該当のエラー

PS C:\Users\user\Desktop> & C:/Users/user/AppData/Local/Programs/Python/Python37/python.exe c:/Users/user/Desktop/test.py
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\threading.py", line 926, in _bootstrap_inner
    self.run()
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "c:/Users/user/Desktop/test.py", line 19, in print_nowtime
    time_label["text"] = "{0:%Y/%m/%d %H:%M:%S}".format(now)
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1492, in __setitem__
    self.configure({key: value})
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1485, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1476, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
RuntimeError: main thread is not in main loop
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

問題はWindowを破棄した後も、スレッドが動作し続けるためprint_nowtime 関数内より、すでに破棄されているtime_labelに対して書き込みが行われることです。

ですので対応としては、Windowが破棄されると同時に Threadを止める処理をいれることによりエラーは解決するかと思います。

具体的な方法としては

1. print_nowtime関数を外部から終了できるように

is_valid = True
def print_nowtime():
    while is_valid:
        now = datetime.datetime.now()
        time_label["text"] = "{0:%Y/%m/%d %H:%M:%S}".format(now)
        time.sleep(5) #動作が重くなるため

のように修正(is_validFalseにすることによってループを抜けるようにする)

2. Windowが破棄されたときにコールバック関数(on_closing())が呼ばれるように

win.protocol("WM_DELETE_WINDOW", on_closing)


を設定

3. 上記から呼ばれるコールバック関数内を

def on_closing():
    global is_valid
    is_valid = False
    thread_1.join()
    thread_2.join()
    # Windowを破棄
    win.destroy()

のように記述し、Threadを停止する処理を入れる

のようにすると良いのではないでしょうか

あと質問とは全く関係ありませんが、main()関数はスレッドにする必要がないように思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/30 09:55

    できました。ありがとうございます。

    理解しました。
    main()は普通に呼び出すことにします。

    キャンセル

+1

二点問題があります。
一点目はすでに回答が出ている終了処理問題。
二点目は別のスレッドでUIメッセージを渡すような処理を行うと、以下のメッセージが出ることです。

RuntimeError: main thread is not in main loop

対策としては、以下のURLにあるように、UI操作を別スレッドで行わないようにすることしかありません。
https://stackoverflow.com/questions/54237067/how-to-make-tkinter-gui-thread-safe

[multithread tkinter]でググるとやり方がわかると思います。

それか動作環境が限られますが、適当なモジュールを使うとか。
https://pypi.org/project/tkthread/

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/30 13:20

    2点目の問題は、僕の環境では出なかったので、参考にさせていただきます!

    キャンセル

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

  • ただいまの回答率 87.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る