LoginSignup
13
19

More than 3 years have passed since last update.

【Python/Tkinter】PandasのDataFrameを検索→表示する簡易な検索フォームを作成する

Last updated at Posted at 2020-05-16

はじめに

DataFrameを利用する際,それを検索→表示できたらなと思う場面があります。特にGUIアプリを作成する際にはあると便利です。下記にPandasのDataFrameを検索し,表示する検索フォームをTkinterを利用して作ってみたいと思います。

サンプル

search.gif

コード全文

import pandas as pd
import tkinter as tk
import tkinter.ttk as ttk


class SearchWindow(tk.Frame):
    def __init__(self, master=None, parent=None):
        super().__init__(master)
        self.master = master
        self.master.geometry("300x300")
        self.master.title("検索ウインドウ")
        self.pack()
        self.set_data()
        self.create_widgets()

    def set_data(self):
        self.data = pd.read_csv("data.csv", encoding="utf-8")
        self.colname_list = ["食品コード", "食品名"]  # 検索結果に表示させる列名
        self.width_list = [100, 200]
        self.search_col = "検索キーワード"  # 検索キーワードの入力されている列名

    def create_widgets(self):
        self.pw_main = ttk.PanedWindow(self.master, orient="vertical")
        self.pw_main.pack(expand=True, fill=tk.BOTH, side="left")
        self.pw_top = ttk.PanedWindow(self.pw_main, orient="horizontal", height=25)
        self.pw_main.add(self.pw_top)
        self.pw_bottom = ttk.PanedWindow(self.pw_main, orient="vertical")
        self.pw_main.add(self.pw_bottom)
        self.creat_input_frame(self.pw_top)
        self.create_tree(self.pw_bottom)

    def creat_input_frame(self, parent):
        fm_input = ttk.Frame(parent, )
        parent.add(fm_input)
        lbl_keyword = ttk.Label(fm_input, text="キーワード", width=7)
        lbl_keyword.grid(row=1, column=1, padx=2, pady=2)
        self.keyword = tk.StringVar()
        ent_keyword = ttk.Entry(fm_input, justify="left", textvariable=self.keyword)
        ent_keyword.grid(row=1, column=2, padx=2, pady=2)
        ent_keyword.bind("<Return>", self.search)

    def create_tree(self, parent):
        self.result_text = tk.StringVar()
        lbl_result = ttk.Label(parent, textvariable=self.result_text)
        parent.add(lbl_result)
        self.tree = ttk.Treeview(parent)
        self.tree["column"] = self.colname_list
        self.tree["show"] = "headings"
        self.tree.bind("<Double-1>", self.onDuble)
        for i, (colname, width) in enumerate(zip(self.colname_list, self.width_list)):
            self.tree.heading(i, text=colname)
            self.tree.column(i, width=width)
        parent.add(self.tree)

    def search(self, event=None):
        keyword = self.keyword.get()
        result = self.data[self.data[self.search_col].str.contains(keyword, na=False)]
        self.update_tree_by_search_result(result)

    def update_tree_by_search_result(self, result):
        self.tree.delete(*self.tree.get_children())
        self.result_text.set(f"検索結果:{len(result)}")
        for _, row in result.iterrows():
            self.tree.insert("", "end", values=row[self.colname_list].to_list())

    def onDuble(self, event):
        for item in self.tree.selection():
            print(self.tree.item(item)["values"])


def main():
    root = tk.Tk()
    app = SearchWindow(master=root)
    app.mainloop()

if __name__ == "__main__":
    main()

なお,読み込んでいるCSVの中身は下記のようになっています。:

      食品コード              食品名                          検索キーワード
0      1001        アマランサス・玄穀                 アマランサス・玄穀 あまらんさす
1      1002           あわ・精白粒                        あわ・精白粒 あわ
2      1003          あわ・あわもち                  あわ・あわもち あわ あわもち
3      1004      えんばく・オートミール  えんばく・オートミール えんばく おーとみーる おーと おーつ
4      1005        大麦・七分つき押麦         大麦・七分つき押麦 おおむぎ ななぶつきおしむぎ

すこし解説

テキストを入力→Enterで検索結果がtreeに表示されます。

Enterキーで検索できるように,メソッドを紐づけています。下記の部分です:

        ent_keyword.bind("<Return>", self.search)

検索結果を表示させるウィジェットには,TTKのTreeviewを利用しています。表形式でシンプルに表示できるので気に入っています。searchが実行されると,その結果をupdate_tree_by_search_resultが受け取ります。Treeviewに挿入する前に全削除し,その後に追加しています。この部分が下記の処理です:

    def search(self, event=None):
        keyword = self.keyword.get()
        result = self.data[self.data[self.search_col].str.contains(keyword, na=False)]
        self.update_tree_by_search_result(result)

    def update_tree_by_search_result(self, result):
        self.tree.delete(*self.tree.get_children())
        self.result_text.set(f"検索結果:{len(result)}")
        for _, row in result.iterrows():
            self.tree.insert("", "end", values=row[self.colname_list].to_list())

なお,GIF中では触れていませんが,この検索結果を利用することも可能です。下記のコードでtreeのアイテムがダブルクリックされた際の処理として,onDoubleを紐付けています。

        self.tree.bind("<Double-1>", self.onDuble)

onDoubleは下記のようになっています。これで選択されたアイテムの値を取得することができます。

    def onDuble(self, event):
        for item in self.tree.selection():
            print(self.tree.item(item)["values"])
13
19
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
19