【Tkinter開発】 Tkinterでファイル読み取りGUIを開発する【まとめ】

Tkinterで作ったファイル読み込みGUIのまとめです。
全体のソースコードはここに記述する。
アーカイブ
ソースコード
アプリケーションは
python main.py
で起動する。
main.py
import tkinter as tk
from application import Application
def main():
root = tk.Tk()
app = Application(root)
app.pack()
app.mainloop()
if __name__ == "__main__":
main()
application.py
import tkinter as tk
from tables import Tables
from menu import Menu
class Application(tk.Frame):
def __init__(self, master: tk.Tk):
super().__init__(master)
self.title = "Tkinter Application"
master.geometry("800x600")
master.title(self.title)
# クラス間で共通で使われる変数
props: dict = {}
Menu(self.master, props)
self.mainframe = tk.Frame(self.master)
self.mainframe.grid_rowconfigure(0, weight=1)
self.mainframe.grid_columnconfigure(0, weight=1)
self.mainframe.pack()
self.tables = Tables(self.mainframe, props)
self.tables.grid()
tables.py
import tkinter as tk
from tkinter import ttk
import xml.etree.ElementTree as ET
import csv
class Tables(tk.Frame):
def __init__(self, master: tk.Tk, props: dict):
super().__init__(master)
self.props = props
# テーブルの作成
self.create_table()
# # テーブルへデータ挿入
# self.import_table_data(sample_data)
# テーブルデータの詳細ディスプレイ
self.create_display()
# アプリケーション全体で独自イベントを捉える
self.master.bind_all("<<OpenFile>>", lambda _: self.open_file())
self.master.bind_all("<<SaveFile>>", lambda _: self.export_csv_data())
def create_table(self):
# 表の列名
columns = [
{"name": "Title", "width": 200},
{"name": "Description", "width": 300},
{"name": "Link", "width": 190},
{"name": "Date", "width": 90},
]
# Treeviewウィジェットを作成
self.treeview = ttk.Treeview(
self.master, columns=[col["name"] for col in columns], show="headings"
)
# 列名を設定
for col in columns:
name = col["name"]
width = col["width"]
self.treeview.heading(name, text=name)
self.treeview.column(name, width=width)
# スクロールバーを追加
scrollbar = ttk.Scrollbar(
self.master, orient="vertical", command=self.treeview.yview
)
self.treeview.configure(yscrollcommand=scrollbar.set)
# グリッドに配置
self.treeview.grid(row=0, column=0, sticky="nsew")
scrollbar.grid(row=0, column=1, sticky="ns")
# グリッド設定
self.master.grid_rowconfigure(0, weight=1)
self.master.grid_columnconfigure(0, weight=1)
# Treeviewの選択イベントをバインド
self.treeview.bind("<ButtonRelease-1>", lambda _: self.show_selected_row())
def import_table_data(self, data):
# 既存テーブルのデータを削除
for item in self.treeview.get_children():
self.treeview.delete(item)
for row in data:
self.treeview.insert("", "end", values=row)
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")
self.ok_button = tk.Button(
self.display_frame, text="OK", command=self.insert_display_data
)
self.ok_button.grid(row=5, columnspan=2)
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)
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)
def open_file(self):
# ファイルパスの取得
file_name = self.props.get("open_file_name")
xml_data = self._parse_xml_data(file_name)
self.import_table_data(data=xml_data)
def _parse_xml_data(self, file_path):
xml_data = []
# ElementTreeオブジェクトの作成
tree = ET.parse(file_path)
root = tree.getroot()
# ルート以下のchannelを取得
channel = root.find(".//channel")
# channel以下のitemを取得
for item in channel.findall(".//item"):
# item以下の情報を取得する
title = item.find(".//title")
title_data = title.text if title is not None else ""
description = item.find(".//description")
description_data = description.text if description is not None else ""
link = item.find(".//link")
link_data = link.text if link is not None else ""
date = item.find(".//pubDate")
date_data = date.text if date is not None else ""
xml_data.append([title_data, description_data, link_data, date_data])
return xml_data
def export_csv_data(self):
# テーブルのデータを取得
csv_data = [
self.treeview.item(item_id, "values")
for item_id in self.treeview.get_children()
]
file_name = self.props.get("save_file_name")
with open(file_name, "w", newline="", encoding="utf-8") as f:
csv_writer = csv.writer(f, quoting=csv.QUOTE_ALL)
csv_writer.writerows(csv_data)
menu.py
import os
import tkinter as tk
from tkinter import filedialog
class Menu(tk.Menu):
def __init__(self, master: tk.Tk, props: dict):
super().__init__(master)
self.props = props
# メニューバー
menubar = tk.Menu(self.master)
menu_file = tk.Menu(menubar, tearoff=0)
menu_file.add_command(label="ファイルを開く", command=self.open_filedialog)
menu_file.add_command(label="名前を付けて保存", command=self.save_filedialog)
# Ctrl+Sで保存
self.master.bind("<Control-Key-s>", func=lambda _: self.save_filedialog())
menubar.add_cascade(label="ファイル", menu=menu_file)
# メニューバーの設定
self.master.config(menu=menubar)
def open_filedialog(self):
# ファイルフィルタ
file_type = [("XMLファイル", "*.xml"), ("", "*")]
# 最初に開くフォルダ
initial_directory_path = os.path.abspath(os.path.dirname(__file__))
file_name = filedialog.askopenfilename(
filetypes=file_type, initialdir=initial_directory_path
)
# ファイルを開いたら, イベントを発生させる
if file_name != "":
self.props["open_file_name"] = file_name
self.master.event_generate("<<OpenFile>>")
def save_filedialog(self):
# ファイルフィルタ
file_type = [("CSVファイル", ".csv")]
# 最初に開くフォルダ
if self.props.get("open_file_name") is not None:
# ファイルを開いたパスのフォルダにする
initial_directory_path = os.path.abspath(
os.path.dirname(self.props["open_file_name"])
)
else:
# ファイルを開いていなければ実行しているパスのフォルダ
initial_directory_path = os.path.abspath(os.path.dirname(__file__))
file_name = filedialog.asksaveasfilename(
filetypes=file_type,
initialdir=initial_directory_path,
defaultextension="csv",
)
if file_name != "":
self.props["save_file_name"] = file_name
self.master.event_generate("<<SaveFile>>")











ディスカッション
コメント一覧
まだ、コメントがありません