Python
Tkinter

Tkinterは親のオブジェクト変更できないんだってね。え?できるよ!ただし条件が必要だけどって話。

環境

開発環境

-Python3系
-VS2017Comminity

この記事の人のスペック

-Pytohn始めてまだ総計1週間弱くらい。
-設計とかほとんどやらないよ。うまくできる自信ないよ

対象

ー何らかの事情によって親オブジェクトにつくオブジェクトたちを変更したりしたい人。
ーTkinterを少し触っていて多少のUIを表示した事がある人。
ーTkinterを触っていて、親オブジェクトを変更したい人 <-Best

そもそもTkinterって何?

Python単体では、GUIまでサポートされていないので、それをサポートするためのもの
追加でインストールを行う事で使用可能。
追加の仕方等は、別の方のものを参考にしてください。

今回記述しようと思った理由

ー私「親オブジェクト一回しか指定できないじゃん。なんか制限にとらわれたくなねんけど」
ー私「特定の組み合わせのオブジェクト、別の下に付け替えようと思ったら、大変じゃんめんどうそう
といったところから来ています。
但し、あくまでも自己満足のためのものなので、参考程度によろしくお願いします。

どうやって変更するのか?

今持っている知識の中で私の前提知識

・基本的に、親オブジェクトを指定する際には、オブジェクト生成時しか親オブジェクトを指定できないっぽい。
例えばフレームとボタンがあって以下のようなプログラムを書いてしまうとする。

sample.py
import Tkinter as tk
frame = tk.Frame()
frame.pack()
button = tk.Button()
button.pack()

「あ、やっぱり、Frameの中にボタンを入れたい」となったときにどうするか?

sample2.py
import Tkinter as tk
frame = tk.Frame()
frame.pack()
button = tk.Button(frame)
button.pack()

としたら、解決は出来ます。

これって結構不便じゃない?

これって微妙に面倒。
確かにUIって基本固定だし、問題はなさそう。

出来る事なら
button.setparent(frame)
とすれば、
他のUIパーツがあってもsetparentで調べる事が出来ると生成だけは、別途行ってー
そのパーツはここのフレームにつけるんだーみたいな事が出来る訳です。
みたいな経緯があって作りたいわけです。

具体的な方法 それはpack_forget()

ざっくり説明してしまうと
pack_forget()を呼び出すと、UI表示の設定から、外される模様で、
表示されるUIから排除されます。
これは、TkinterがジオメトリーマネージャなるマネージャでUI(Weiget)を管理しており
このpack_forgetの関数は、そのジオメトリーのマネージャに対して、登録した内容を消すことが出来ます。

ただし、そのままだと、表示されなくなるので、再びpack()関数を呼び出す。
それで、親のオブジェクトが変更されます。
ただし!
このpack_forget関数、設定したテキスト、コールバック、色等々全て忘れます。
大事なことなのでもう一度言いますね。
設定したもの全て忘れます!無に帰ります!

なので、必ず、それらの設定を保持する機構や、一時的に保存しておく機構を作成しておきましょう。

例えばこんな感じ

sample3.py
class ui_base(object):
    def __init__(self):
       self._dic_event = {}##ディクショナリーでの管理
       self._callback_list =[]

といった感じの設定を別途保存するためのものを作成すると便利そうです。

まだまだ不便な点は残っている

とはいえ、ラップするクラス作っても、結局の所クラス作った所で、生成する時に、
何のオブジェクトか決まるのだったら、意味なくない?

たぶんごもっともです。
なので、以下のような対応をします。

sample4.py
import Tkinter as tk

class Button
 def __init__(self):
     self._uiobject = tk.Button()
     self._uiobject.pack()

 def SetParent(self,parent_obj):
     self._uiobject.pack_forget()  ##一度設定忘れて
     self._uiobject.__init__(parent_obj) ##初期化

あってるかはわかりませんが
initはWigetクラスが持っている基底としてのinitが呼ばれ、
これが結果的に、UIの親とか、子とかの設定になるみたいです。

免責事項

プログラムを使用するにあたって、それによる被害が出た場合は一切責任を負いません。
ご了承の上で使用していただいて結構です。

なお、こうした方が、いいよ!っていうのは、教えていただけると幸いです。
さらに説明の際に、丁寧だとさらにGood!