Failed to execute script pyi_rth__tkinter

2020-06-30

Failed to execute script pyi_rth__tkinter

Python(プログラミング言語)のPyInstallerでexe化したexeを実行したときに表示されたエラーです。

このエラーについて調べたことを記載していきます。

私の場合は、exeの起動時に表示されましたが、終了時に表示される場合は、window.close()で解決する場合があるようです。

コメントされている方がいらっしゃいますので、気になる方はコメントをみてください。コメントはページの最後になります。

意味

Failed to execute script pyi_rth__tkinter

このエラーの意味は

スクリプトpyi_rth__tkinterの実行に失敗しました。

です。

importでエラーの場合

ライブラリをimportするときに例えば、

import tkinter.messagebox as tkmsg

と書くとNGになることがありました。

以下のように書くことで、エラーを回避できました。

from tkinter import messagebox as tkmsg

以下、それ以外のケースについて書いていきます。

エラー詳細

私の環境の場合、エラー詳細は以下のようになっていました。

Traceback (most recent call last):
 File "site-packages\PyInstaller\loader\rthooks\pyi_rth__tkinter.py" , line 30, in <modele>
FileNotFoundError: Tcl data directory "C:\User\user\AppData\Local\Temp\_MEI191162\tcl" not found.
[17352] Failed to execute script pyi_rth__tkinter

要するに

PyInstallerでexe化したexeを実行したときに、pyi_rth__tkinter.pyが一時フォルダ内の「tcl」フォルダを探しにいって、見つからないからエラーを発生させている、ということです。

pyi_rth__tkinter とは?

まず、エラーメッセージでは分かりづらいのですが、rthとtkinterの間は、アンダースコア(アンダーバー)「2つ」です。

私はアンダースコア1つで検索して、精度良く見つけられなかったので念のためお伝えしておきます。

ファイル名は「pyi_rth__tkinter.py」です。

場所

場所は

C:\Users\user\AppData\Local\Programs\Python\Python38\Lib\site-packages\PyInstaller\loader\rthooks

などにあるはずです。

どうしてexe化されたプログラムが、そんなパス(開発環境)のスクリプトを見に行くのでしょう…

一時的な作業フォルダにも「pyi_rth__tkinter.py」はできていないようですし。

内容

pyi_rth__tkinter.pyの中身は以下になります。

#-----------------------------------------------------------------------------
# Copyright (c) 2013-2020, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------


import os
import sys


try:
    FileNotFoundError
except NameError:
    # FileNotFoundError is new in Python 3.0
    # NB: Aliasing IOError is not a full emulation of FileNotFoundError,
    # but far enough for this usecase, where the whole frozen program
    # terminates when this exception occurs.
    FileNotFoundError = IOError

tcldir = os.path.join(sys._MEIPASS, 'tcl')
tkdir = os.path.join(sys._MEIPASS, 'tk')

if not os.path.isdir(tcldir):
    raise FileNotFoundError('Tcl data directory "%s" not found.' % (tcldir))
if not os.path.isdir(tkdir):
    raise FileNotFoundError('Tk data directory "%s" not found.' % (tkdir))

# Notify "tkinter" of such directories.
os.environ["TCL_LIBRARY"] = tcldir
os.environ["TK_LIBRARY"] = tkdir

コメントの機械翻訳はこんな感じです。

FileNotFoundError is new in Python 3.0

FileNotFoundErrorはPython 3.0の新機能です

NB: Aliasing IOError is not a full emulation of FileNotFoundError, but far enough for this usecase, where the whole frozen program terminates when this exception occurs.

注意:IOErrorのエイリアスはFileNotFoundErrorの完全なエミュレーションではありませんが、この例外が発生するとフリーズされたプログラム全体が終了するこのユースケースには十分です。

Notify "tkinter" of such directories.

そのようなディレクトリを「tkinter」に通知します。

以上、機械翻訳でした。

具体的な処理内容としては、「tcl」「tk」という一時的な作業フォルダを探し、

  • 無ければエラーを発生させる
  • あれば環境変数に追加する

ということだと思います。

PyInstallerで作成したexeのtkinterを動作させるための特殊処理なのでしょう。

最初のif文は「Python 3未満ではFileNotFoundErrorが定義されていないから、代替のIOErrorに差し替える」ということでしょう。

フォルダはあるのか?

「tcl」「tk」というフォルダが存在すべき親のフォルダ

C:\User\user\AppData\Local\Temp\_MEI999999

このフォルダ自体は、通常はexe化したプログラムの実行時に作成され、終了時に消えます。

その間のプログラム実行中は、上記フォルダ以下に2つとも存在します。

  • (1)C:\User\user\AppData\Local\Temp\_MEI999999\tcl
  • (2)C:\User\user\AppData\Local\Temp\_MEI999999\tk

しかしエラーが発生する場合は親の「_MEI999999」フォルダは存在しているものの「tcl」「tk」が存在しておらず、tkinterのメソッドを利用する前の段階でエラーになります。(おそらくimport時。)

ちなみに上記2つのフォルダに該当するのは以下と思われます。

  • (3)C:\Users\user\AppData\Local\Programs\Python\Python37\tcl\tcl8.6
  • (4)C:\Users\user\AppData\Local\Programs\Python\Python37\tcl\tk8.6

以下に示す対策の主な方法は上記の(3)(4)フォルダを見に行くようにしたいという修正ですが、そもそもが一時作業フォルダに(1)(2)が存在しない時点で無理なのかもしれません。

Tcl/Tkとは

そもそもtclやtkというのは何なのか。

Tcl/Tk ( ティクル・ティーケー ) は、スクリプト言語 Tcl と、その GUIツールキット Tk だそうです。

Tkinter は Tk を Python から使うためのモジュールで、Tkinter という名前も Tk との "インター" フェイスという意味とのこと。

きっと、PyInstallerとtkinter(Tcl/Tk)の連携がうまくいってないんでしょうね。

Tcl/Tkはpipでインストールするものでもないですし、他のライブラリとはレベルがちがうものなのでしょう。

Tkinter は通常は Python に含まれている、ようです。

Tcl/Tkについては以下が参考になるかもしれません。

https://docs.python.org/ja/3/library/tkinter.html

原因は

要するに原因は、exe実行時に作成される一時作業フォルダ

C:\User\user\AppData\Local\Temp\_MEI999999

内に「tcl」(と「tk」)フォルダができていないため

になります。

奥が深そうですね…

ちなみに当方の環境では「tcl」「tk」と同階層の「wx」フォルダは問題なく作成されています。

対策

エラー回避のためにいろいろと調べていろいろとやってみました。

以下、その内容を記載します。

私はまだこの問題を解決できていないのですが、私のプログラムではかなり特殊なことをやっているので、他の方については以下の対策のいずれかで解決できる場合もあるかと思います。

本来は「tcl」「tk」が作成されるように対策すべきなのですが、奥が深そうなのと、こちらで対処できる問題なのかどうかも分からないので、違う方法も含め調べてみました。

raiseをコメントアウト

以下のサイトを参考にraise部分をコメントアウトしてみました。

#if not os.path.isdir(tcldir):
#    raise FileNotFoundError('Tcl data directory "%s" not found.' % (tcldir))
#if not os.path.isdir(tkdir):
#    raise FileNotFoundError('Tk data directory "%s" not found.' % (tkdir))

(一応ですが、CやJava感覚で2行目と4行目だけをコメントにするとエラーになってしまいますのでご注意を)

上記サイトでは「ほとんどの場合、このファイルはアプリケーションの実行に必要ない場合があります。」ということでしたが、私の場合は解決できませんでした。

プログラム内で一番最初にtkinterを使用している以下の部分でエラーになりました。

root=tkinter.Tk()

エラー内容は以下でした。

  File "tkinter\__init__.py", line 2023, in __init__
_tkinter.TclError: Can't find a usable init.tcl in the following directories:
    {C:\Users\user\AppData\Local\Temp\_MEI103082\tcl} C:/Users/user/AppData/Local/Temp/_MEI103082/tcl8.6 C:/Users/user/AppData/Local/Temp/lib/tcl8.6 C:/python/SelfTool/test12/dist/lib/tcl8.6 C:/python/SelfTool/test12/lib/tcl8.6 C:/python/SelfTool/test12/dist/library C:/python/SelfTool/test12/library C:/python/SelfTool/test12/tcl8.6.9/library C:/python/SelfTool/tcl8.6.9/library

This probably means that Tcl wasn't installed properly.

結局tkinterを使用する場合は、コメントアウトしてもその先でエラーになってしまうということです。

import対象のディレクトリを追加

厳密には「Failed to execute script pyi_rth__tkinter」がどの時点で発生しているのか分からないのですが、「import tkinter」で起こっているのではないかと予想しています。

で、言われているのは「FileNotFoundError: Tcl data directory」ですので、import対象のディレクトリとして(ダメもとですが)「Tcl」「tk」を追加してみました。

import sys

sys.path.append("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python37\\tcl\tk8.6")
sys.path.append("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python37\\tcl\tcl8.6")

import tkinter

状況は変わりませんでした。(解決しませんでした。)

--hidden-import tkinter(オプション編)

以下のブログを参考に、PyInstallerで固めるときのコマンドに「--hidden-import tkinter」を追加してみました。

https://ameblo.jp/lucifep2525/entry-12488022359.html

状況は変わりませんでした。(解決しませんでした。)

--hidden-import tkinter(specファイル編)

以下のブログを参考に、PyInstallerで固めるときのspecファイルに「--hidden-import tkinter」を追加してみます。(現在検証中

後日、検証結果を追記予定です。

余談レベル

PyInstallerでexe化するプログラムについて

import tkinter.filedialog

↑こんな感じでインポートすると「Failed to execute script pyi_rth__tkinter」となってしまいます。

from tkinter import filedialog

↑みたいな感じでインポートしましょう。

現時点でのまとめ

私はこの問題についてまだ解決できていません。

どなたか分かる方がいらっしゃいましたら解決方法をご教示願います。

ちなみにPyInstaller×subprocessの以下の問題は解決しています。

以上。

後記

最後までお読みくださりありがとうございます。

こんなに更新したのに、現在はほぼ誰も見ていないゴミカスクズ無価値ブログです。

よければ(はてな)ブックマークや拡散をお願いいたします。

更新の原動力として励ましが欲しいのです…<(_ _)>

当ブログ内お薦め記事