asana_yui
@asana_yui (結 麻菜)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Pythonでtkinterをthreadingに乗せることが出来ない

Q&A

Closed

解決したいこと

Pythonでtkinterをthreading上で動かした時、ランタイムエラーが送られてしまいます。
あくまで以下の該当するプログラムのように書きたいのですが、エラーは回避可能でしょうか?
それともtkinterがマルチスレッド非対応っぽいから回避不可能でしょうか?
別のライブラリを使う、適切なオプションを付けるなど、回避の方法があればそれを、不可能なら不可能ですと回答お願いします。
なお、tkinter.Frameのafterメソッドは使わずにお願いします。

出力

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python39\lib\threading.py", line 950, in _bootstrap_inner
Hello
    self.run()
  File "C:\Python39\lib\threading.py", line 888, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Python39\lib\tkinter\__init__.py", line 1422, in mainloop
    self.tk.mainloop(n)
RuntimeError: Calling Tcl from different apartment

ソースコード

import tkinter
import threading

def greet():
    print("Hello")

root = tkinter.Tk()

t1 = threading.Thread(target=root.mainloop)
t2 = threading.Thread(target=greet)

t1.start()
t2.start()

環境

OSはWindows10です。

> py --version
Python 3.9.0
0

1Answer

今回の質問の意図は、tkinterと並列で別の処理をしたい、というものだと解釈しています。

少なくともtkinterのメインループをメインスレッド以外で実行するのは不可能だと思います。
別プロセスを作って、そのプロセスのメインスレッド上でメインループを実行してはどうでしょうか。

import threading
import multiprocessing


def greet():
    print("Hello")


def tk_main():
    import tkinter
    root = tkinter.Tk()
    root.mainloop()


def main():
    t1 = multiprocessing.Process(target=tk_main)
    t2 = threading.Thread(target=greet)

    t1.start()
    t2.start()
    t1.join()
    t2.join()


if __name__ == '__main__':
    main()
1Like

Comments

  1. @asana_yui

    Questioner

    回答ありがとうございます。
    しかしmultiprocessingを用いた方法だと、別プロセスとしてやらせる際にtargetに対して直列化をするようで、tkinterはpickleでは処理できないそうなのでこの方法ではうごきませんでした。


    ```
    Traceback (most recent call last):
    File "C:\Users\asana\tk_thr_test.py", line 17, in <module>
    main()
    File "C:\Users\asana\tk_thr_test.py", line 13, in main
    t1.start()
    File "C:\Python39\lib\multiprocessing\process.py", line 121, in start
    self._popen = self._Popen(self)
    File "C:\Python39\lib\multiprocessing\context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
    File "C:\Python39\lib\multiprocessing\context.py", line 327, in _Popen
    return Popen(process_obj)
    File "C:\Python39\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
    reduction.dump(process_obj, to_child)
    File "C:\Python39\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
    TypeError: cannot pickle '_tkinter.tkapp' object

    ```
  2. 動作確認しておらずすみません。
    tkinter.Tk()を別プロセス側で作成すれば直列化する必要がなくなるので動作すると思います。

    回答のコードを修正しました。
    (Windows 10, python 3.8で動作確認済み)
  3. @asana_yui

    Questioner

    上の下さったコードで期待していた動きが作れそうです!ありがとうございます!

Your answer might help someone💌