【Tkinter開発】第4回 Tkinterでメニューを作成し、ファイルを指定する

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

メニューからファイル読み込みができるように、メニューをセットする。

事前準備

ファイル読み込み用のファイルとして、XMLファイルをワークスペースに配置しておいてください。

とりあえずのダウンロードなら以下のURLにアクセスし、itemdia_all.xmlとしてローカルに保存しておいてください。

https://rss.itmedia.co.jp/rss/2.0/itmedia_all.xml

メニューの配置

メニューの配置

まずはmenu.pyファイルを作成して、そこにメニューに関する処理のクラスを定義する。

import os

import tkinter as tk
from tkinter import filedialog

class Menu(tk.Menu):
    def __init__(self, master: tk.Tk):
        super().__init__(master)

        # メニューバー
        menubar = tk.Menu(self.master)

        menu_file = tk.Menu(menubar, tearoff=0)
        menu_file.add_command(
            label="ファイルを開く", command=lambda: print("ファイルを開く")
        )
        menu_file.add_command(
            label="名前を付けて保存", command=lambda: print("名前を付けて保存")
        )

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

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

tk.Menuクラスでメニューウィジェットを宣言できる。

tkinterのtk.Menuは入れ子になっているので、最初のメニューをmenubarに定義し、その中に入るメニューはmenu_fileに定義する。

メニュの追加はadd_command()関数で追加する。labelにメニューの表示名が入り、commandにボタンを押したときの関数を書く。
今は、コマンドラインにprintするだけである。

最後に、アプリの上側に表示されるメニューに、add_cascade()関数でボタンを押したとき小ウィンドウを出せるようにする。

1, 4行目のインポートは後で使う。

最後にapplication.pyで先ほど定義したMenuクラスを呼び出す。

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)

        Menu(self.master)

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

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

第4回 メニューの追加を確認

メニューを追加すると、急にデスクトップアプリ感が出てくるわね笑

今は押しても何もしてくれないんだけどね

ファイルを指定する

ファイルを開くためには、ファイルパスを入力する必要がある。
しかし、ファイルパスの文字列を直接入力するのは利用者にとって手間となるため、GUIによって直感的にファイルを選択したい。
そのためにファイルダイアログを利用する。

ファイルダイアログを開く

まずは、open_filedialog()関数をMenuメソッドとして作成し、"ファイルを開く"ボタンを押したときに実行するようにする。

class Menu(tk.Menu):
    def __init__(self, master: tk.Tk):
        super().__init__(master)

        # メニューバー
        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=lambda: print("名前を付けて保存")
        )

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

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

“名前をつけて保存"ボタンは保存するときに実装する。

open_filedialog()関数を定義する。

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

    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
        )

        print(file_name)

filedialog.askopenfilename()関数でファイルダイアログが開かれる。

ファイルフィルタは拡張子でフィルタをかけ、ファイルダイアログ上で特定のファイルのみを表示するものである。
今回はデフォルトにxmlファイルのみを表示させるようにする。

また、initialdirで最初に開かれるフォルダパスを指定している。今回はmain.pyがあるフォルダを最初のフォルダとしている。

第4回 ファイルダイアログの表示 (.xml)

こんな感じで開かれる。itmedia_all.xmlを始めとした拡張子がxmlのファイルを置いておけば、それらが表示される。

“開く"の上の指定を変えるとすべてのファイルが表示される。

第4回 ファイルダイアログ (すべて)

今のところこんな感じです。

こういう直感的なファイル指定こそ、GUIの利点よね

ここまで実装しても、まだファイルを指定するところまでで、ファイルを開く処理は別途実装します

ちなみに、ファイルダイアログは「開く」ボタンを押すとファイルパスが得られるが、「キャンセル」ボタンを押すと空文字""になる。
あとで、ファイルを開いたのかキャンセルをしたのかを判定する必要がある。

ファイルダイアログには、複数ファイル指定やフォルダ指定などいくつか種類があるので、いろいろ試してみてね


次回はようやくファイルを開いて読み込みます
今はMenuクラスとTablesクラスで別けて実装しているので、クラス間でデータのやり取りする少し複雑な処理が登場します