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

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

ただいまの
回答率

87.36%

Python3 Tkinter ソースコードの書き方の質問

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 760

score 148

次のアプリを作成しました。

起動するとウィンドウが出ます。
ウィンドウのOpenボタンを押すと、ポップアップウィンドウが表示されます。
ポップアップウィンドウにはテキストボックスがあり、Enterボタンを押すとテキストボックスの文字列を控えてポップアップを閉じます。
メインウィンドウのDispボタンを押すと、先程の文字列を表示します。

ソースコード

from tkinter import ttk
import tkinter as tk


class Main(tk.Tk):
    def __init__(self):
        self.str = ""
        super().__init__()
        self.geometry("400x300+0+0")
        self.title("Main")
        self.view_create()

    def view_create(self):
        self.button1 = ttk.Button(self, text="Open", command=self.open)
        self.button1.grid()
        self.button2 = ttk.Button(self, text="Disp", command=self.disp)
        self.button2.grid()

    def open(self):
        self.tl = tk.Toplevel()
        self.tl.geometry("200x100+0+0")
        self.tl.title("toplevel")
        self.tl.attributes("-topmost", True)
        self.tl.resizable(False, False)
        self.tl.grab_set()
        self.tl.transient(self)
        self.entry = ttk.Entry(self.tl)
        self.entry.grid()
        self.entry.insert("end", self.str)
        self.button3 = ttk.Button(self.tl, text="Enter", command=self.enter)
        self.button3.grid()
        self.tl.protocol("WM_DELETE_WINDOW", self.close)

    def disp(self):
        str = self.str
        if str != "":
            print(str)

    def close(self):
        self.tl.grab_release()
        self.tl.destroy()

    def enter(self):
        self.str = self.entry.get()
        self.close()

def main():
    app = Main()
    app.mainloop()

if __name__ == "__main__":
    main()

以下質問です。

① 上のコードは、tkinterのイベントコールバック関数を全て インスタンスメソッド(self.***) にしていますが、実際そうしたほうがいいですか?それともインスタンスメソッドの中にメソッドを作るほうがいいですか?

def pushed(self):
    def close(tl):
        tl.destroy()
    self.tl = tk.Toplevel()
    self.tl.protocol("WM_DELETE_WINDOW", lambda:close(self.tl))

② 一番上のコードではボタンやエントリにself.をつけていますが、self.をむやみにいろんな変数につけない方が良いとかってありますか?self.を使わず引数で渡す手もありますが、引数の括弧の中がごちゃごちゃしているのって、個人的に見た目汚いようなイメージなので上のコードではself.を使いました。

③ メインウィンドウとポップアップウィンドウを同じクラスの中で処理していますが、実際このままでいいですか?それとも分けたほうがいいですか?

④ 配置するウィジェットが多いとウィジェット配置の記述(上のコードで言うview_create())が膨大になりますが、そういう場合はウィジェットのイベントアクション(上のコードで言うopen()、disp()、close()、enter())は別のクラスやファイルに記述したほうがいいですか?それとも、全部同じファイルまたは同じクラスに記述したほうがいいですか?

回答お願いします。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

0

厳しいかもしれませんが、率直な感想です。

Pythonの観点から

ソースコードの書き方は綺麗です。
ただ、PythonはC言語と違ってmain関数は必要としません。main関数を見ると、C言語になぜmain関数があるかを分かっていないのだろうなという印象を与えます。

オブジェクト指向の観点から

クラスというのは、複数のインスタンスの機能の共通化をはかるもので、それを使いやすくするために継承(インヘリタンス)があります。
Mainというクラスのインスタンスを複数作って利用するようには見えません。
、MainがTkから継承しているのはmainloopだけです。
ここでクラスを作る必然性はなにもありません。

また、オブジェクトには、内部情報を外部から保護して、余分なバグを発生させないという目的もありますが、全てをひとつのオブジェクトにしてしまうと、オブジェクト指向プログラミングの大きな利点の一つが全く生かされません。

クラスを作ればオブジェクト指向だとお考えのように見えますが、そうではありません。

GUIプログラミングの観点から

GUIでは処理ロジック(Model)と画面制御(View)を分離したコーディングが主流です。MVC(Model View Controller)とかMVP(Model View Presenter)とかMVVM(Model View ViewModel)とかはいずれも処理ロジックと画面制御を分離しています。

TKinterは、画面制御のライブラリとユーザ入力のライブラリの集まりですが、それらははっきりと区別されています。モデルは別に作ることを前提としています。
personさんのプログラムは、画面制御用のクラスであるTkの派生クラスを作ってその中に処理ロジックを入れ、ユーザ入力も入れてごちゃごちゃです。

使ってみた感想

なぜ、Displayを押したときコンソールに出力されるかが謎でした。

質問への回答

① 上のコードは、tkinterのイベントコールバック関数を全て インスタンスメソッド(self.***) にしていますが、実際そうしたほうがいいですか?それともインスタンスメソッドの中にメソッドを作るほうがいいですか?

インスタンスメソッドにする必然性はありません。

② 一番上のコードではボタンやエントリにself.をつけていますが、self.をむやみにいろんな変数につけない方が良いとかってありますか?

インスタンス変数にするしないかは、データ保護の観点で判断するものです。多い少ないで決めるものではありません。

③ メインウィンドウとポップアップウィンドウを同じクラスの中で処理していますが、実際このままでいいですか?それとも分けたほうがいいですか?

分けるべきです。

④ウィジェットのイベントアクション(上のコードで言うopen()、disp()、close()、enter())は別のクラスやファイルに記述したほうがいいですか?

クラスを作ることがオブジェクト指向プログラミングの本質ではありません。

今後に向けて

要素技術を学ぶのと平行して、オブジェクト指向プログラミングとかGUIプログラミングの本質について勉強することをお勧めします。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/01/11 22:50 編集

    回答ありがとうございます。


    > クラスを作ればオブジェクト指向だとお考えのように見えますが、そうではありません。

    申し訳ないです。そもそもクラス化した理由としては、別の関数でウィジェットを操作する際に引数に毎回変数を書くのが面倒だと感じたからです。self.をつければ、self以外の引数が必要ないので。完成したものがオブジェクト指向かどうかについては正直眼中になかったです。


    > なぜ、Displayを押したときコンソールに出力されるかが謎でした。

    Printのほうが良かったでしょうか?(名称の話?)
    あくまでサンプルとして作ったので動作自体に深い意味はありません。


    >> ①
    > インスタンスメソッドにする必然性はありません。

    必然性はないというのは、やっても問題はないと解釈してもいいですか?


    >> ②
    > インスタンス変数にするしないかは、データ保護の観点で判断するものです。多い少ないで決めるものではありません。

    データの保護が必要ないというのは、何らかの処理をするための一時変数などがそれにあたりますか?またデータの保護が必要というのは、外部からその変数にアクセスするような場合ですか(ウィジェット操作など)?


    >> ③
    > 分けるべきです。

    分ける場合、それぞれのクラスでtk.Tkとtk.Toplevelを継承するような形にしますか?それとも特に継承は必要としませんか?


    >> ④
    > クラスを作ることがオブジェクト指向プログラミングの本質ではありません。

    オブジェクト指向のソースコードとかネットにあるやつを参考に考え直します。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る