MacOS + Tkinter

MacOSで、tkinterが走らない!

先月、最初にこの現象に遭遇した。

第2波、第3波と、tkinterがらみで他段階でトラブルが発生したので、時系列に報告。第1波と第2波はよくあるトラブルのようで、第3波がかなりキツかった。

まず、第1波。Anacondaとpyenvの混在。
簡単に、原因と対策を書くならば、Anacondaの設定と動作の都合なのか、 pyenv のPATHがうまく参照されず(混ぜるな!危険!とは、あちこちで書かれていたけれど)結局 Anacondaを削除して、pyenv のバージョン切り替えはスムーズに行った。

第2波。import tkinterでエラーが出る。
(ここでも、半日以上浪費)どうやら、brew で昔入れたpython-tkが古くて、バージョンが噛み合わなかったようだ。
brew upgrade -> brew install python-tk
で、brew の python-tkを更新。pyenv の3.11.4を一旦削除し、改めて
pyenv install 3.11.4
でインストールし直し。
python-build: use tcl-tk from homebrew
このメッセージの意味するところが、理解できていなかったかも知れない。

第1波と、第2波については、ググるとかなりの情報がヒットするので、ご自身でググってください。matplotlibのバックグラウンド実行については、私の場合には非該当だった。

問題は、第3波。類似の現象についての報告が、ネットで全く見当たらない。
tk = tkinter.Tk()
を実行すると、以下のメッセージを出して落ちる。

2023-10-15 10:32:00.278 python[23422:1811770] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SDLApplication macOSVersion]: unrecognized selector sent to instance 0x7fae0de29710'

やれやれ。
で、解決策。(理由はわからないけれど、とにかく動いたので報告。苦節5時間。まさかの逃げ方。)

トラブっていたプログラムは、下記。

class SettingsPop:
    def __init__(self):
        debug_print("now initialize SettingsPop")
        self.tk = tkinter.Tk()
        debug_print("after Tk()")
        self.tk.title("環境設定")
        self.tk.geometry("500x400+100+100")

この tkinter.Tk()で落ちる。これなら、SettingsPopが実体化されなければ余計な資源は食わない。ただ、落ちる。結果的に動いたのは、このsettings.pyのプログラムファイルの冒頭に

tk = tkinter.Tk()
tk.title("環境設定")
tk.geometry("500x400+100+100")

を書いて、インスタンス変数ではなく、global変数としてtkを定義したら、だった。
(*** 以下、一部文章修正:修正前 ***)
言うまでもなく、この書き方(geometryもコンストラクタの外で記述)だとmain.pyが起動し、
import settings
の1行を通過した時点で、この「環境設定」のポップアップ画面が出てきてしまう。仕方ないので、一瞬画面に出てきてしまってから、隠すことにした。こういう醜いやり方はしたくないんだけれど、現時点で self.tk =で、インスタンス変数としてtkを保持するやり方では、前述の'NSInvalidArgumentException'が(少なくとも5時間の試行錯誤では)消せなかった。ネットでも、この感じのエラーの記事が全くヒットしなかった。

醜いけれど、諦めました。orz
(なんか、この一瞬画面が何か出て、すぐに消える、っていうのが、キタナイよねぇ〜〜。今は我慢。)

あとで、もっとちゃんとした解決策が見つかった時のために、self.tk=tkでコード修正が必要最小限で済むようにリファクタリングする予定。
だけれど、Windowsでは self.tk=tkinter.Tk()で全く問題ない。

(*** 一部文章修正:修正後***)
あまりキレイな書き方ではないけれど、とりあえず tk=tkinter.Tk()だけファイルレベルで記述してあれば動くことを確認した。こんな感じ。

tk = tkinter.Tk()

class SettingsPop:
    def __init__(self):
        self.tk = tk
        self.tk.title("環境設定")
        self.tk.geometry("500x400+100+100")

インスタンス変数とグローバル変数とでは、allocationされる領域が異なり、tkinterは前者ではうまく初期化できない、ということになるんだろうか?つまり「遅延起動」に対応していない?っていうことかな?というよりも、これは「仕様」なんだろうか?それにしては、Windowsではコンストラクタが呼ばれた時に初めてTk()が実行しても、きちんと動くのに・・・

とにかく、geometryで、実際のポップアップ画面を作成する部分は、コンストラクタ内に持たせておけば、この「環境設定」が呼ばれなければ画面は出てこないから、動作的には全く問題ない。
==> 嘘でした。窓が出てる。。。どうしましょうかね?隠さなきゃ。

(*** 一部文章修正:おしまい***)
同様の現象に悩まされている方にも、あまり、参考にはならないかも知れませんね。

以上、年寄りの「ないよりは、マシ」程度の情報提供でした。

この記事が気に入ったらサポートをしてみませんか?