3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonによるGUIアプリ テキストエディタの作成

Last updated at Posted at 2024-07-10

fletを利用したGUIアプリケーションの作成

このようなテキストエディタを作成することをゴールとします。
自分で作ってみて、まだまだだと思う点はありますが、プログラミングは面白いし、何か凄いことができた全能感に浸れる感覚を味わってほしいです。

GUIアプリの魅力はそこかなと思います。

画像1.png

前回の記事を参考にして頂けるとスムーズと思います。
前回の続きなので(一応)高校生向けの内容を想定します。

CUIアプリよりも難易度は上がりますが、fletが優秀なので比較的、記述量は少なく書けると思います。

ファイルの書き込み

as はよく使うことになるので、ある程度の段階で触れておいたほうがいいと思います。
「importするモジュールにあだ名をつけて自分の好きなように呼ぶ」という認識でいいかと思います。

ただ、pythonは誰が書いてもわかりやすく可読性の高いコードという設計思想があるので、割とみなさん同じあだ名をつけると思います。

ボタンをクリックして動作する点は前回のhello worldアプリと変わりないですが、ファイルを保存するにあたって、with openの部分が重要になってきます。

pythonでファイルを開くためには開始と終了の処理を行う必要がありますが、withブロック内にプログラムを記述することにより、終了の処理をwith文が自動的に行ってくれます。

ファイルを開くモードは書き込み"w"になります。他にもいろいろなモードが存在するので、実行したい処理によって
色々変えていきます。

pathに代入されているファイル名(ファイルパス)が処理の対象となるファイルになります。プログラムが配置されているカレントディレクトリにファイルが生成されていることにご注意ください。

エンコーディングの指定もここで行っています。utf-8についてもここで少し理解しておく必要がありますが、私はあまり文字コードに明るくないのでここでは説明をしません。

example01.py
# as を使うことでftとして呼び出す
import flet as ft

def main(page):
    
    page.title = "テキストエディタ"
    
    # ファイルの保存を行う関数
    def save_file(e):
        # ファイルの場所や保存名(ファイルパス)を指定
        path = "example.txt"
        # file_nameで設定したファイルパスを受け取り、書き込みモードで実行
        # With文を使うことで開始と終了を自動で処理、as fで以降はfとして扱う
        with open(path, "w", encoding="utf-8") as f:
            # fにtext_areaに記述されているテキストの値を受け取り、書き込む
            f.write(text_box.value)
    
    # 保存ボタンクリック時にfile_save関数を呼び出す
    save_button = ft.ElevatedButton("保存",on_click=save_file)
    
    # テキストフィールドを作成
    text_box = ft.TextField(label="テキストをここに入力",multiline=True,min_lines=20)
    
    page.add(save_button,
             text_box)

ft.app(target=main)

ファイル名の指定

さらにファイル名を指定できるようにします。具体的にはテキストフィールドに入力されているテキストの値をファイル名とし、カレントディレクトリに保存します。

example02.py
import flet as ft

def main(page):
    
    page.title = "テキストエディタ"
    
    def save_file(e):
        # テキストフィールドに入力されている値を取得し、suddix_textの値を加え、ファイル名とする。
        # なお、カレントディレクトリを基準にパスごとファイル名を指定できるが、ここでは特に触れない
        with open(text_file_name.value + text_file_name.suffix_text, "w", encoding="utf-8") as f:
            f.write(text_box.value)
    
    save_button = ft.ElevatedButton("保存",on_click=save_file)
    
    # ファイル名を指定するテキストフィールドを作成
    text_file_name = ft.TextField(label="ファイル名",suffix_text=".txt")
    
    text_box = ft.TextField(label="テキストをここに入力",multiline=True,min_lines=20)
    
    page.add(save_button,
             # テキストフィールドを配置
             text_file_name,
             text_box)

ft.app(target=main)

エラー処理

テストコードを書いてきちんとしたテストを行うまではできないと思いますが、一応エラー処理などをやってみています。tryに実行時の内容を記述し、exceptにエラー時の処理を書いていきます。何らかの原因でコード自体がうまくいかなかった想定外のエラーも、エラーとしてユーザーに見せることができます。

また、ファイル名が空欄だった場合は保存ができないので(処理上はエラーがでない)、ファイル名を受け取った時点で処理する必要があります。

このようにエラー処理は考えることが多いですが、あらかじめやっておくことで安全なプログラムを作成することができます。

example03.py
import flet as ft

def main(page):
    
    page.title = "テキストエディタ"
    
    def save_file(e):
        # tryの内容が実行できなければException(エラー)がraiseされますが、
        # 別途if notの部分が実行された場合も明示的にエラーを発生させてます。
        try:
            # ここではファイル名に何かが入力されているかどうかの判定しており、strip()で空白(スペース)を削除します。
            # notと書くことでデータが空である場合にExceptionをraiseします。
            if not text_file_name.value.strip():
                raise Exception
                
            with open(text_file_name.value + text_file_name.suffix_text, "w", encoding="utf-8") as f:
                f.write(text_box.value)
                # 保存後の処理として、メッセージを表示します。
                text_file_name.helper_text = text_file_name.value + text_file_name.suffix_text + "を保存しました"
                page.update()
                
        # エラー発生時の処理、Exceptionはpythonが出力するエラーの内容となります。
        except Exception as e:
            text_file_name.helper_text = "エラーが発生しました。" + str(e)
            page.update()
            
    
    save_button = ft.ElevatedButton("保存",on_click=save_file)
    
    # 後ほどhelper_textの値にエラー文などを表示させる。
    text_file_name = ft.TextField(label="ファイル名",suffix_text=".txt",helper_text="")
    
    text_box = ft.TextField(label="テキストをここに入力",multiline=True,min_lines=20)
    
    page.add(save_button,
             text_file_name,
             text_box)

ft.app(target=main)

OS標準のファイルダイアログを利用する

エラー処理を含めるとコードが煩雑になるので一旦忘れます(暴挙)。

ここでは、OS標準のファイルダイアログを扱う方法についてやっていきます。

WindowsやMacではファイルを開く時にOSの機能であるファイルダイアログが開きます。これをアプリケーション側から呼び出し、ファイルパス等の情報を受け取ります。

ボタンをクリックするとファイルダイアログを呼び出し、その戻り値にであるファイルパスやファイルデータに対して、処理を行います。つまり、呼び出し側と受け取り側の2つの関数が必要になるということです。
ファイルピッカーは変数に代入しておき、呼び出し時には呼び出し用のメソッドpick_files()を適用しています。

通常はラムダ文などを使って簡潔に書く(公式ドキュメントではそうなってますね^^)と思いますが、ラムダ文の書き方についての前提知識が必要になりますのではまずは関数型で記述していきます。

とはいえ、公式ドキュメントが動くものを提供しているので、基本的はそのまま使うのがおすすめです。

example04.py
# WindowsやMac等のOS搭載のファイルダイアログを呼び出し、その返り値をパスやファイルデータとして受け取ります。
import flet as ft

def main(page):
    # 選択したファイルを読み取っていきます。
    def pick_files_result(e):
        # if e ではダイアログから受け取ったデータ自体があるかないかをif文で判断しています。
        if e:
            # e.files[0]が最初のデータになります。単体のファイルを選択しているので、e.files[1]は存在しません。
            # .path でファイルパスをtext_file_name.valueに代入し、画面に表示させます。
            text_file_name.value = e.files[0].path
            page.update()
        
        # if e.files[0].pathではファイルパスのデータがあるかないかを判断しています。
        if e.files[0].path:
            # データがあれば、with openの読み込みモード"r"でファイルを開き、fとします。
            with open(e.files[0].path,"r", encoding="utf-8") as f:
                # text_box.valueの中に読み込んだデータを代入します。
                text_box.value = f.read()
            page.update()
    
    # ボタンから呼び出されたpick_files_dialogに対して、pick_files()というメソッド(処理を)行うことで、
    # ダイアログを呼び出します。ダイアログを呼び出すだけですが、ファイルを選択する部分はOS側でやりますので、
    # 呼び出すだけの動作が必要になるわけです。
    def open_pick_files_dialog(e):
        pick_files_dialog.pick_files()

    # fletからファイルピッカー(ダイアログを呼び出すための部品)を呼び出してpick_files_dialogに格納します。
    # 「on_result=」のオプションで、ファイルピッカーが呼び出されたときの処理をpick_files_resultに指定します。
    pick_files_dialog = ft.FilePicker(on_result=pick_files_result)
    
    # 「開く」ボタンの作成します。ボタンをクリックした際の処理を、open_pick_files_dialogに指定します。
    pick_button = ft.ElevatedButton("開く",on_click=open_pick_files_dialog)
    
    text_file_name = ft.Text()
    
    text_box = ft.TextField(label="テキストをここに入力",multiline=True,min_lines=20)

    page.overlay.append(pick_files_dialog)

    page.add(
        pick_button,
        text_file_name,
        text_box
    )

ft.app(target=main)

まとめ

ここまでで本稿は終了となりますが、このあと「開く」機能と「保存」機能を統合したり、様々なエンコードに対応するなど、より本格的なものにしていくためには色々な部分をみていく必要があります。

大規模なアプリケーションを作るのは大変ですが、比較的お手軽にGUIが実現できるfletでプログラミングの楽しい部分に触れると、一気に世界が変わって見えますね。

何かの足しになれば幸いです。

3
3
0

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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?