【Tkinter開発】第6回 Tkinterでファイルを指定して出力する【最終回】

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

最後に編集した表データをCSVファイルとして出力する機能を実装する。

メニューの機能追加

ファイルダイアログと保存イベント

まずは、メニューで「名前をつけて保存」を押したときに、ファイルダイアログを出して出力先ファイルを指定できるようにする。

open_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)

        menubar.add_cascade(label="ファイル", menu=menu_file)

        # メニューバーの設定
        self.master.config(menu=menubar)

    ...中略...

    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>>")

12行目はsave_filedialog()関数を実行するように書き直している。

save_filedialog()関数ではopen_filedialog()関数と同じように、file_typeとinitial_directory_pathを設定している。
initial_directory_pathは、ファイルを開いていれば、その開いたフォルダを保存時に最初に開く場所としている。

保存のダイアログを使用するときは、filedialog.asksaveasfilename()関数を使う。保存先を指定でき、同名のファイルがあって上書きしてしまう場合もこの関数が警告などを出してくれる。

filedialog.asksaveasfilename()関数は、ファイルを指定したときはファイル名を返し、キャンセルを押したときは空文字""を返す。

空文字かを判定し、そうでないとき、self.propsにキーを"save_file_name"としてファイル名を代入する。

Tablesクラスが保存したイベントを検知できるように、"<<SaveFile>>"イベントを発生させる。

ファイルダイアログはいつものOSのファイル機能が使えるのね

Ctrl+sで保存できないの?

簡単に実装できるので追加してみよう

キーバインド

ファイル編集ソフトでは利便性からショートカットキーが使えるものが多い。

Tkinterでも簡単にショートカットキーが利用できるので追加する。

class Menu(tk.Menu):
    def __init__(self, master: tk.Tk, props: dict):
        ...中略...

        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())

        ...中略...

「コントロールキーCtrl+s」のキーボードイベントを検知するには、self.masterに対して"<Control-Key-s>"をバインドする。これで、funcに指定した関数が呼び出される。
(add_command()関数と仕様が異なり、funcには引数が挿入されるので、lambda関数にして引数に対応するようにしている。ここで受け取る引数は使用しない。)

全キーボード入力に対応しているので、ぜひ調べてお好みのショートカットキーを作ってみてください。

デーブルの機能追加

イベントを検知してCSV出力

Tablesクラスに"<<SaveFile>>"イベントを検知し、表データを取り出し、CSV出力できる機能を追加する。

CSVファイル出力できるように、python標準のcsvをインポートする。

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.create_display()

        # アプリケーション全体で独自イベントを捉える
        self.master.bind_all("<<OpenFile>>", lambda _: self.open_file())
        self.master.bind_all("<<SaveFile>>", lambda _: self.export_csv_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)

__init__()では"<<SaveFile>>"イベントを検知できるように、self.masterにバインドをし、self.export_csv_data()関数を呼び出している。

テーブルデータの取得では、self.treeview.get_children()関数で表の位置情報の一覧を取得し、self.treeview.item()関数でデータを取り出している。ここで取り出せるのは行のデータ(リスト型)である。

次に出力先となるファイルを取得するために、self.propsから"save_file_name"キーを指定してファイル名を取り出す。

最後に32行目からcsv出力する処理が書いてある。

出力先の指定

第6回 保存用ファイルダイアログ

出力されたファイル

第6回 出力されたCSVファイル

総まとめ

以上、ファイル読み取りGUIを作ってみました。

それっぽいGUIのアプリができたわね!
具体的に何に使うのかしら

わからん、僕はRSSをあんまり活用したことがないから、、

私はネット上から自動でRSSフィードをダウンロードして、気になった記事だけをピックアップして出力できるGUIを作って見ようと思います

ここで作ったGUIはそのままだと機能が少ないけど、それを活かせば便利なアプリになりそうだね

Tkinterの利便性が分かってもらえたら幸いです。