はじめに
DataFrameを利用する際,それを検索→表示できたらなと思う場面があります。特にGUIアプリを作成する際にはあると便利です。下記にPandasのDataFrameを検索し,表示する検索フォームをTkinterを利用して作ってみたいと思います。
サンプル
##コード全文
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"])