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

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

ただいまの
回答率

89.10%

Python Tkinterでのスーパークラスのコンストラクタ用引数に関して。

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 127

chokokorone333

score 27

 完成予定品

Tkinterを使用し、タブ機能(notebook)を付ける。

現在のコーディング状況

class Gui(tk.Frame) からタブ機能のついた画面を一つ表示しようとしています。
class Gui(tk.Frame) のコンストラクタ内にある、Tab(ttk.Notebook) のコンストラクタにより、タブを画面を実装しようとしています。

エラー内容

エラー内容の解釈として、Tab(ttk.Notebook) は、Frameクラスのオブジェクトしか受け付けない。と考えております。
ですが、Tab(ttk.Notebook) へ渡している tab1 = tk.Frame(self.master) の tab1 は <class 'tkinter.Frame'> であり、混乱しております。

Traceback (most recent call last):
  File "app.py", line 28, in <module>
    app = Gui(master=root)
  File "app.py", line 16, in __init__
    Tab(master=tab)
  File "app.py", line 21, in __init__
    super.__init__(master)
TypeError: descriptor '__init__' requires a 'super' object but received a 'Frame'

実行環境

・windows10 コマンドプロンプト
・Python3.8.3

#coding:utf-8

import tkinter as tk
from tkinter import ttk


class Gui(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.pack()

        self.master.geometry("1100x600")
        self.master.title("NoteBook Test")

        tab = tk.Frame(self.master)
        Tab(master=tab)


class Tab(ttk.Notebook):
    def __init__(self, master = None):
        super.__init__(master)

        self.add(master, text="tab")


if __name__ == "__main__":
    root = tk.Tk()
    app = Gui(master=root)
    app.mainloop()
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 4

checkベストアンサー

+1

記述ミスです。親クラスのコンストラクタ呼び出しになってません。

  •         super().__init__(master) OK
  •         super.__init__(master) NG

super はクラスとして実装されコンストラクタ__init__があるので
エラーメッセージが解り難くなっているようです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/06/29 22:54

    ご指摘ありがとうございます。
    無事、エラーは無くなったようです。
    しかし、プロンプトが固まったまま望む画面も出ずの結果になってしまいました。
    現在調査中ですが、訂正が必要なコードになっていた場合、レビューをしていただけますと幸いです。

    キャンセル

  • 2020/06/29 23:00

    見た感じ、他の問題点は2点
    - Tabオブジェクトの pack()やgrid() が呼ばれてない為、非表示になってます
    - オブジェクトの親子関係がおかしい。親ウィジェットを add しようとしてますが、
     通常は、小ウィジェットをaddで追加するようにします

    キャンセル

  • 2020/06/29 23:08

    ありがとうございます。
    その2点を踏まえてもう少し知らべてみようと思います。

    キャンセル

  • 2020/06/29 23:17

    ありがとうございます。
    以上を踏まえ改定しました。
    ポイントとなった点としては、オブジェクトの親子関係を指摘していただいた点です。

    以下の通りにコーディングしました。

    ```Python
    #coding:utf-8

    import tkinter as tk
    from tkinter import ttk


    class Gui(ttk.Notebook):
    def __init__(self, master):
    super().__init__(master)

    self.master.geometry("1100x600")
    self.master.title("Tkinter with Class")

    tab = tk.Frame(self.master, width=1100,height=600)
    self.add(tab, text="tab1")
    Tab(master=tab)

    tab2 = tk.Frame(self.master, width=1100,height=600)
    self.add(tab2, text="tab1")
    Tab(master=tab)

    self.pack(side = "top")

    class Tab(tk.Frame):
    def __init__(self, master = None):
    super().__init__(master)

    if __name__ == "__main__":
    root = tk.Tk()
    app = Gui(master=root)
    app.mainloop()
    ```

    キャンセル

+1

無事解決することができました。ありがとうございます。
コメントですと、インデントがおかしくなるためこちらに貼らせていただきます。

#coding:utf-8

import tkinter as tk
from tkinter import ttk


class Gui(ttk.Notebook):
    def __init__(self, master):
        super().__init__(master)

        self.master.geometry("1100x600")
        self.master.title("Tkinter with Class")

        tab = tk.Frame(self.master, width=1100,height=600)
        self.add(tab, text="tab1")
        Tab(master=tab)

        tab2 = tk.Frame(self.master, width=1100,height=600)
        self.add(tab2, text="tab1")
        Tab(master=tab)

        self.pack(side = "top")

class Tab(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

if __name__ == "__main__":
    root = tk.Tk()
    app = Gui(master=root)
    app.mainloop()

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/06/29 23:21

    Tab(master=tab) 戻り値を受け取っていない、pack()も呼ばれてないので、
    ここは意図通りの動作になってないかもしれません。

    キャンセル

  • 2020/06/29 23:25

    申し訳ありません、理解ができていないのですが、
    Tab(master=tab)の呼び出しをしても意味がないということでしょうか。
    でしたら、class Tab(tk.Frame)以下に、tab, tab2の処理内容を記述しようと考えております。

    キャンセル

  • 2020/06/30 00:13

    一点見落としてましたが Gui(tk.Frame): -> Gui(ttk.Notebook) に変更してたのですね。

    tab = tk.Frame(self.master, width=1100,height=600)
    のフレームが Notebook に追加されて、
    Tab(master=tab) は、生成されて非表示のままになってます。
    ここを後から実装されるというなら納得ですが、間の Frame が一つ余分です。

    self.add(Tab(master=self), text="tab1")

    また、width=1100,height=600は 個別ではなく、親ウィジェットに指定した方が良いです。
    Notebook 内に配置されるとサイズはNotebook任せになります。

    キャンセル

+1

返信遅くなってしまいました。
試行錯誤の結果、Guiクラスにノートブックを構成する動作を入れ、追加したい分をFrameクラスのインスタンスからaddする。といった具合にしてみました。

その他notebookにaddしたいウィジェットがある場合は、
Frameクラス以外にも、Frame2, Frame3...と作りインスタンスを生成しとすれば良いという考えに基づき作成しております。

#coding:utf-8

import tkinter as tk
from tkinter import ttk

#import pub


class Gui(ttk.Notebook):
    def __init__(self, master):
        super().__init__(master)
        note = ttk.Notebook(master)

        page1 = Frame(note, "page1")
        page2 = Frame(note, "page2")

        array = [page1, page2]

        for i in array:
            note.add(i, text=i.ret_name())

        note.pack(fill=tk.BOTH, expand=True) 
        self.pack()


class Frame(tk.Frame):
    name = ""
    def __init__(self, master=None, txt=None):
        super().__init__(master)
        self.name = txt
        page = tk.Frame(master)
        self.widget(txt)
        self.pack()

    def widget(self, txt):
        label = tk.Label(self, text=txt, font=("", 30))
        label.pack()

    def ret_name(self):
        return self.name

def main():
    root = tk.Tk()
    root.geometry("600x600")
    root.title("tkinter test")

    app = Gui(master=root)
    app.pack()

    root.mainloop()


if __name__ == "__main__":
    main()

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/06 01:50

    self.pack() は2か所ともなくても大丈夫、レイアウトは
    クラス外で呼び出した方がgrid()でも使えるようになります。

    app.pack()でもう一度呼び出しているのと、
    Notebookに追加するFrameは、Notebook側で表示してくれます。
    (どちらも2重に呼び出している状態。但し動作に支障はありません)

    ----
    もう一点。これも本題とは関係ない部分ですが

    class Frame(tk.Frame):
      name = "" # <-- これはクラス変数(クラス間で共有する変数)
    になるので不要です。インスタンス変数 (self.name) に上書きされ
    意味のない宣言になってます。

    キャンセル

  • 2020/07/07 10:58

    self.pack()が必要ない理由として、
    ・note.addはnoteに自動で貼り付ける。・Guiのインスタンスappを外でapp.pack()している。
    ということでよろしかったでしょうか?

    また、クラス外で呼び出したほうがgirid()でも使えるようになる。という部分の理解が追いついていません。

    name = ""で最初に宣言する必要がなく、self.name = txtでインスタンス変数nameが作られ、txtが入るということですね。

    キャンセル

  • 2020/07/07 12:53

    > note.addはnoteに自動で貼り付ける。・Guiのインスタンスappを外でapp.pack()している。

    その通り。現行のコードでは特に問題はありませんが、省略可能です。

    > クラス外で呼び出したほうがgirid()でも使えるようになる。

    これは、クラスを再利用する場合を想定してます。

    実際にこのような使い方はしないかもしれませんが、一例で

    frame = tk.Frame(parent)
    frame.grid()
    gui = Gui(parent) # 内部で self.pack() してると、grid() と競合する

    Tkinter のウィジェットの配置は、pack/grid/place の3種類あるのですが、
    そのうち pack と grid は同一ウジェット上に互いに混在させるが出来ません。

    クラス内部で self.pack() とすると、packのレイアウトにしか配置できないクラスになってしまいます。
    出来ないといっても解決策は簡単で、一手間、間に grid() で配置したFrame を挟むと済む話ですが、
    それよりもレイアウトは利用側に任せた方が自然な解決策になります。

    レイアウトがどのように配置されるかについて、
    クラス内部のコードを見に行く必要がある、というコードの可読性にも繋がる為。

    label = tk.Label(self, text=txt, font=("", 30))
    label.pack()

    通常のウィジェットのように、独自のウィジェットを拡張したクラスを作る場合も同様、
    そのクラスの利用側(インスタンスを生成した側)に配置は任せた方が良いです。

    > name = ""で最初に宣言する必要がなく、
    > self.name = txtでインスタンス変数nameが作られ、

    はい。前者はクラス変数、実際に使っているのは後者のインスタンス変数のみです。

    キャンセル

0

ウィジェット構造

- root : tkinter.Tk
  - app : Gui (tk.Frame)
    - tab : Tab (ttk.Notebook)
      - page : tk.Frame
        - label : tk.Label

のようなツリー形式で各ウィジェットを構成してください。

tab に入れるウィジェットは通常 tk.Frame(tab) ※ tk.Frame(master=tab) と同じ。
として、生成したウィジェットを add で配置します。

#!/usr/bin/env python3.8

import tkinter as tk
from tkinter import ttk


class Gui(tk.Frame):
    def __init__(self, master):
        super().__init__(master)

        tab = Tab(master=self)

        page = tk.Frame(tab)
        label = tk.Label(page, text="Page1", font=("", 30))
        label.pack()
        tab.add(page, text="page1")
        # NOTE: page.pack は不要。ttk.Notebook内部で処理されます。

        tab.pack(fill=tk.BOTH, expand=True) # <- Tab クラス内では呼び出さない。利用側で grid()/pack() を呼び出す

        self.tab = tab


class Tab(ttk.Notebook):
    def __init__(self, master=None):
        super().__init__(master)



if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("1100x600")
    root.title("NoteBook Test")
    # geometry, title は外部で呼び出すと、
    # Guiのmaster に root 以外の任意のウィジェットを渡せます。

    app = Gui(master=root)
    app.pack(fill=tk.BOTH, expand=True) # 外部で呼び出す事で、 Gui オブジェクトを pack/grid/place で使えるようになります

    root.mainloop()

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/05 23:44

    ウィジェットの構成をそちらの図で理解することができました。ありがとうございます。

    キャンセル

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

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