【Tkinter開発】第3回 Tkinterで詳細ディスプレイを作成し、編集できるようにする

Python,エンジニアリングPython,Tkinter

Tkinterで表を表示できるようになったら、その表を選択し、編集できるようにする。

詳細ディスプレイを作成する

詳細ディスプレイ用のウィジェットの配置

タイトル、説明、リンク、日付のエントリーボックス及びテキストボックスを以下のように配置する。

class Tables(tk.Frame):
    def __init__(self, master: tk.Tk, props: dict):
        super().__init__(master)

        ...中略...

        # テーブルへデータ挿入
        self.import_table_data(sample_data)

        # テーブルデータの詳細ディスプレイ
        self.create_display()

    ...中略...

    def create_display(self):
        # 表示フレーム
        self.display_frame = ttk.Frame(self.master)
        self.display_frame.grid(row=1, column=0)

        # ラベル, エントリを作成
        # タイトルエントリ
        self.title_label = tk.Label(self.display_frame, text="Title: ")
        self.title_label.grid(row=1, column=0, sticky="w")
        self.title_entry = tk.Entry(self.display_frame, width=100)
        self.title_entry.grid(row=1, column=1, sticky="we")

        # 説明テキストボックス
        self.description_label = tk.Label(self.display_frame, text="Description: ")
        self.description_label.grid(row=2, column=0, sticky="w")
        self.description_text = tk.Text(self.display_frame, width=100, height=3)
        self.description_text.grid(row=2, column=1, sticky="we")

        # リンクエントリ
        self.link_label = tk.Label(self.display_frame, text="Link: ")
        self.link_label.grid(row=3, column=0, sticky="w")
        self.link_entry = tk.Entry(self.display_frame, width=100)
        self.link_entry.grid(row=3, column=1, sticky="we")

        # 日付エントリ
        self.date_label = tk.Label(self.display_frame, text="Date: ")
        self.date_label.grid(row=4, column=0, sticky="w")
        self.date_entry = tk.Entry(self.display_frame, width=100)
        self.date_entry.grid(row=4, column=1, sticky="we")

前回と同様にcreate_display()関数を作成し、そこにウィジェットを配置していく。

タイトル、リンク、日付は1行の表示で良いので、エントリ(tk.Entryクラス)を利用している。また、説明は複数行(3行)表示させたいので、テキストボックス(tk.Textクラス)を利用している。

ここまで書けると以下のようになる。

第3回 詳細ディスプレイの配置

表を選択し、詳細ディスプレイに表示させる

表を選択し表示させるには、

①選択したときのイベントをキャッチする。
②選択された表のデータを取得する。
③取得したデータを詳細ディスプレイに表示させる。

の手順で進める。

class Tables(tk.Frame):
    ...中略...

    def create_table(self):
        ...中略...

        # Treeviewの選択イベントをバインド
        self.treeview.bind("<ButtonRelease-1>", lambda _: self.show_selected_row())

まず、create_teble()関数の最後にTreeviewの選択イベントをバインドする。
“<ButtonRelease-1>"の文字列でキャッチしたいイベントを指定する(self.treeviewインスタンスにボタンをはなしたときのイベントがバインドされる)。
第2引数で実行する関数を指定している。まだ、self.show_selected_row()関数は定義していないが、この関数に②, ③の処理を実行させる。

続いて、self.show_selected_row()関数を定義する。

class Tables(tk.Frame):
    ...中略...

    def show_selected_row(self):
        selected_item = self.treeview.selection()

        if selected_item:
            # 選択した行の値を取得
            values = self.treeview.item(selected_item, "values")
            title, description, link, date = values[0], values[1], values[2], values[3]

            # Entryに表示
            self.title_entry.delete(0, "end")
            self.title_entry.insert(0, title)

            # Textに表示
            self.description_text.delete(1.0, "end")
            self.description_text.insert(1.0, description)

            # Entryに表示
            self.link_entry.delete(0, "end")
            self.link_entry.insert(0, link)

            # Entryに表示
            self.date_entry.delete(0, "end")
            self.date_entry.insert(0, date)

self.treeview.selection()関数は表の選択された位置情報を返す。

この情報をself.treeview.item(selected_item, “values")として渡すと、1行分が配列として得られる。
表の列は、タイトル, 説明, リンク, 日付の列順なので, 10行目のように受け取ればよい。

エントリ及びテキストボックスへの表示は、delete()して、insert()で入れ込む。

エントリは、delete(0, “end")ですべて削除でき、insert(0, value)で文字列を入力できる。
テキストボックスは、delete(1.0, “end")ですべて削除でき、insert(1.0, description)で文字列を入力できる。
(「1.0」は1行目の0文字目という意味。テキストボックスは複数行に対応しているため)

ここまで書けたら実行してみる。

第3回 詳細ディスプレイに表示

表を選択すると、詳細ディスプレイに情報が選択される。

ここまでできると、なんとなくアプリっぽいね

まあ、文字列を書き換えても、まだ何も起こらないけどね、、、

書き換えたデータを表に反映する

反映ボタンの配置

詳細ディスプレイの文字列を書き換えたら反映できるようにボタンを配置する。

class Tables(tk.Frame):
    ...中略...

    def create_display(self):
        ...中略...

        self.ok_button = tk.Button(
            self.display_frame, text="OK", command=self.insert_display_data
        )
        self.ok_button.grid(row=5, columnspan=2)

create_display()関数の最後に上記のようにボタンを配置する。
ボタンの名前は「OK」で, ボタンを押したときに実行される関数self.insert_display_data()を指定しておく。

表に詳細データを反映する

データの反映は以下のように処理する。

①ボタンを押したら関数を実行する
②詳細ディスプレイの文字列を取得する
③取得したデータを選択された行に入れ込む

self.insert_display_data()関数を以下のようにする。

class Tables(tk.Frame):
    ...中略...

    def insert_display_data(self):
        # 詳細ディスプレイの情報を表に反映する
        # 詳細ディスプレイの値を取得
        title = self.title_entry.get()
        description = self.description_text.get(1.0, "end").strip()
        link_entry = self.link_entry.get()
        date = self.date_entry.get()

        # 表に入力
        selected_item = self.treeview.selection()
        if selected_item:
            self.treeview.set(selected_item, column=0, value=title)
            self.treeview.set(selected_item, column=1, value=description)
            self.treeview.set(selected_item, column=2, value=link_entry)
            self.treeview.set(selected_item, column=3, value=date)

エントリ及びテキストボックスはget()メソッドで入力されたデータを取得できる。
テキストボックスのみ取得する範囲を指定しているが、上記の書き方で全文字列を取得している。(改行分も取得してしまうので、strip()で余白を削除している。)

self.show_selected_row()関数と同様にself.treeview.selection()関数で選択した行を取得し、self.treeview.set()関数で値を指定する。
第一引数が行位置、第2引数が列位置、第三引数が入れる文字列である。
列はタイトル, 説明, リンク, 日付の順なので、それぞれcolumnを0, 1, 2, 3と指定する。

ここまで書けると以下のように表示される。

それぞれの項目を書き換えてみる。

「OKボタン」を押す。

これで表に反映されていたら成功!

ようやくアプリっぽいことができるようになったわね

これだけの機能のアプリを作るだけでも、考えることがたくさんあって大変なんだね

機能として簡易的でも、データの流れや処理はしっかり設計して進めないと難しいかもね


次回はファイル読み込みをできるようにして、もっとアプリらしくします