Pythonの基礎学習を一通り終えたタイミングで、アプリケーション開発に取り掛かりたい人は一定存在すると思います。
- Python製デスクトップアプリを作りたい人
- おすすめのデスクトップアプリ用フレームワークが知りたい人
- Python製デスクトップアプリのexe化や配布方法を知りたい人
上記の悩みを解決しながら、Python製デスクトップ用フレームワークの紹介から実行環境構築まで解説します。
また、ハンズオン形式でサンプルのPython製デスクトップアプリ開発も実施できるようまとめています。
デスクトップアプリとは

- デスクトップアプリの開発用ライブラリが豊富
- 自身の業務に合わせた業務効率化/自動化に利用できる
- exe化によるアプリ配布が可能
- Pythonコードのみで始められるため初心者が取り組みやすい
また、以下はデスクトップアプリの代表的なメリットになります。
- 特定OSに直接実行がしやすい
- オフラインで動作する
- 使用リソースが少なく軽快な動作
- Webアプリよりも開発が用意
既存ツール/アプリでは詳細な業務に手が届いていない機能も多いです。
そのため、自作ツールとしてデスクトップアプリは有効です。
よく自作ツールとして開発されるデスクトップアプリ例は別の章で後述します。
デスクトップアプリとGUIアプリの違い
「デスクトップアプリ」を「GUIアプリ」と表記する場合もありますが、両者は微妙に異なる文脈だと考えます。
「GUI」と「CUI」とは
GUI(Graphical User Interface)は、ウィンドウやボタンなどグラフィカルな要素をマウス等で操作するインターフェースを指します。
一方でCUI(Character User Interface)は、ターミナルをコマンドで操作するインターフェースを指します。
また、CUIをCLI(Command Line Interface)と呼ぶこともあり、グラフィカルな要素がない文字ベースのインターフェースであると認識しておけば問題ありません。
一般的に「GUIアプリ」といえば、画面上のボタンやテキストボックスなどの要素をマウス操作するためデスクトップアプリと同義である場合もあります。
ただ、デスクトップアプリが「場所(ローカルPCかWeb上)」に関連する用語に対し、GUIアプリが「インターフェース(GUIかCUI)」に関連する用語であるといった相違点があります。
そのため、GUIアプリはデスクトップ/モバイル/Webなどマルチプラットフォームで利用されることから、デスクトップアプリより広範なカテゴリに属していると言えます。
Python製のデスクトップアプリ用GUIフレームワーク
Pythonには様々なデスクトップアプリ用のGUIフレームワークが存在します。
以下の要素を考慮しながらGUIフレームワークを選択するのがおすすめです。
- マルチプラットフォーム対応(Windows/Mac/Linux)
- インストールの有無(Tkinterは標準)
- ライセンスを考慮する
- 個人開発する用途に応じたライブラリなのか
また、代表的なPython製デスクトップアプリ用GUIフレームワークをまとめています。
ライブラリ名 | 概要 | URL |
---|---|---|
Tkinter | Pythonの標準ライブラリ | https://docs.python.org/ja/3.13/library/tkinter.html |
PySimpleGUI | 簡単でシンプルなGUIライブラリ | https://www.pysimplegui.com/ |
TkEasyGUI | PySimpleGUI互換のGIUライブラリ | https://github.com/kujirahand/tkeasygui-python/blob/main/README-ja.md |
PyQt/PySide | GUIツールキットQtのPython用ライブラリ | https://wiki.qt.io/PySide_Documentation/ja |
wxPython | 機能が豊富なGUIツールキット | https://docs.wxpython.org/ |
flet | Flutterを活用したPythonマルチプラットフォームライブラリ | https://flet.dev/ |
各ライブラリに関して解説します。
簡易的なアプリの画像とサンプルコードの比較ができるようまとめています。
Tkinter

サンプルコード
import tkinter as tk
import tkinter.messagebox as msg
# ウィンドウを表示する関数
def show_window():
# メインウィンドウ
root = tk.Tk()
root.title("サンプルアプリ")
# サイズ指定
root.geometry("300x200")
# ラベル作成
tk.Label(root, text="サブウィンドウボタンを押下してください").pack()
# ボタン作成
tk.Button(root, text="サブウィンドウ表示", command=click_handler).pack()
# メインループ開始
root.mainloop()
# クリックイベント
def click_handler():
msg.showinfo(title="サンプルアプリ", message="サブウィンドウが表示されました")
if __name__ == "__main__":
show_window()
Tkinterは、Pythonに標準で搭載されているGUIライブラリです。
Tkinterは1990年代初頭から登場した歴史あるライブラリでWindows/Mac/Linuxと多くのOS上で動作します。
PySimpleGUI

サンプルコード
import PySimpleGUI as sg
# ウィンドウ
win = sg.Window(
title = "サンプルアプリ",
layout = [
[sg.Text("サブウィンドウボタンを押下してください")],
[sg.Button("サブウィンドウ表示")]
]
)
# イベントループ
while True:
event, _ = win.read()
if event == "サブウィンドウ表示":
sg.popup("サブウィンドウを表示しました")
if event == sg.WIN_CLOSED:
break
PySimpleGUIは、手軽にデスクトップアプリを作成するGUIライブラリです。
独自のGUIライブラリではなく、Tkinterなどの既存GUIライブラリをラップしています。
内部でTkinterを利用しているため、動作環境が幅広いのが特徴です。
TkEasyGUI

サンプルコード
import TkEasyGUI as eg
# ウィンドウ表示
win = eg.Window(
title = "サンプルアプリ",
layout = [
[eg.Text("サブウィンドウボタンを押下してください")],
[eg.Button("サブウィンドウ表示")]
]
)
# イベントループ
while win.is_alive():
event, _ = win.read()
if event == "サブウィンドウ表示":
eg.popup("サブウィンドウを表示しました")
win.close()
TkEasyGUIは、PySimpleGUIと互換性のあるGUIライブラリです。
オープンソースで開発されており、商用利用可能で制限の緩いMITライセンスを採用しています。
PyQt/PySide

サンプルコード
import sys
from PySide6 import QtWidgets as qt
def show_window():
# ウィンドウの初期設定
app = qt.QApplication(sys.argv)
win = qt.QWidget()
win.setGeometry(300, 300, 300, 200)
win.setWindowTitle("サンプルアプリ")
# ラベル作成
label = qt.QLabel("サブウィンドウボタンを押下してください", win)
label.setGeometry(10, 10, 200, 20)
# ボタン作成
button = qt.QPushButton("サブウィンドウ表示", win)
button.setGeometry(10, 40, 100, 30)
button.clicked.connect(show_message)
win.show()
app.exec()
# メッセージを表示する関数
def show_message():
qt.QMessageBox.information(None, "サンプルアプリ", "サブウィンドウが表示されました")
if __name__ == "__main__":
show_window()
PyQt/PySideは、Qt(キュート)といったGUIツールキットのPython用ライブラリです。
マルチプラットフォームに対応しており、Windows/Mac/LinuxのOS上で動作します。
もともとC++のために開発されたライブラリであり、幅広く利用され人気です。
wxPython

サンプルコード
import wx
def show_window():
app = wx.App()
frame = wx.Frame(None, wx.ID_ANY, "サンプルアプリ")
panel = wx.Panel(frame, wx.ID_ANY)
# ラベル作成
wx.StaticText(panel, wx.ID_ANY, "サブウィンドウボタンを押下してください", pos=(10, 10))
# ボタン作成
button = wx.Button(panel, wx.ID_ANY, "サンプルアプリ", pos=(10, 40))
button.Bind(wx.EVT_BUTTON, show_message)
frame.Show()
app.MainLoop()
# メッセージを表示する関数
def show_message(e):
wx.MessageBox("サブウィンドウが表示されました")
if __name__ == "__main__":
show_window()
wxPythonは、C++のGUIライブラリである「wxWidgets」のPython用ライブラリです。
コンポーネントの描画をOSに任せているのが特徴で、OSに調和したGUI部品を表示します。
こちらもマルチプラットフォーム対応でWindows/Mac/LinuxのOS上で動作します。
flet

サンプルコード
import flet as ft
def main(page: ft.Page):
page.title = "サンプルアプリ"
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
t = ft.Text("サブウィンドウボタンを押下してください")
def handle_close(e):
page.close(dlg_modal)
#page.add(ft.Text(f"Modal dialog closed with action: {e.control.text}"))
dlg_modal = ft.AlertDialog(
modal=True,
title=ft.Text("サブウィンドウ表示"),
content=ft.Text("閉じるボタンを押下してください"),
actions=[
ft.TextButton("閉じる", on_click=handle_close),
],
actions_alignment=ft.MainAxisAlignment.END,
)
page.add(
t,
ft.ElevatedButton("サブウィンドウ表示", on_click=lambda e: page.open(dlg_modal)),
)
ft.app(main)
Fletは、Googleが開発したFlutterを基盤とし、Pythonのシンプルさを組み合わせたGUIアプリ開発優れたフレームワークです。
Fletを使えば、同じコードでWeb/デスクトップ/モバイルアプリといったクロスプラットフォーム対応を実現できます。
Pythonで作れるデスクトップアプリの例
開発者のアイデア次第ではありますが、Pythonで開発するデスクトップアプリといった側面に着目すると様々なアプリを自作できます。
- 基本的なアクセサリ(関数電卓/スケジュール管理系)
- メモ帳や専用テキストエディタ
- データ収集系ツール
- データ分析系ツール
- ファイルマネージャー
- クリップボード履歴管理
- ネットワーク監視ツール
- パスワード管理ツール
- PDF/Excel/Office系のバックオフィス管理ツール
- AI(API経由)連携ツール
自身の業務あるいは趣味など様々なピンポイント作業に取り入れることで作業効率が何倍にも高められると考えます。
特に、既存ツールの機能だとどうしても手が届かない業務内容があり、なおかつ手作業になっている作業工程も数多くあります。
Pythonによるデスクトップアプリは、自身のニーズに沿ったツール開発に最適だと思います。
Pythonによるデスクトップアプリで作業効率化/自動化
筆者自身が実務として経験していますが、以下はまだまだ手動のままになっている業務だと感じます。
- 分析やマーケティングに必要な膨大なCSVファイル作成
- 社内調整で必要な多人数会議予約やZOOM会議室作成とパスワード
- 自動で記述する音声認識の議事録作成ツール
- マジで無駄なメール返信作業
- 提案資料などの情報整理
- 技術マニュアル作成やテンプレート作成
データ収集先URLが固定あるいは選定されていれば、Seleniumを利用したスクレイピングツールが作成できます。
また、社内調整で必要な多人数の予定もGoogleカレンダーと連携し、予約ツールを作成できそうです。
さらに、メールに対する社内外のテンプレートメール管理ツールや、AI連携したメール要約と返信をセットにしたツールも考えられます。
提案資料や技術マニュアルもローカルPC上に保持する各ファイルを読み込ませ、AI(あるいはAI Agent)にプロンプトで指示し資料のひな形を出力するツールも想像できます。
特に、近年ではいくつもAIサービスがあるため、各AIサービスをまとめたツールを作成し、一度で数個のAIに同時出力させ評価が高い出力を採用し、業務に活用する形も目指せそうです。
このように、Pythonとデータサイエンス関連またはAI技術と親和性が非常に高いため、ちょっとしたアイデアで様々なニーズに応えられるデスクトップアプリが開発できます。
Pythonさえ理解すれば、UIの質はともかく機能としては作業効率化/自動化に貢献できるアプリ開発が実現できると考えます。
Pythonによるデスクトップアプリの開発手順
ここでは、ハンズオン形式でPython製デスクトップアプリを開発していきます。
そのために必要な準備として、Pythonによるデスクトップアプリの開発手順を以下に記載します。
- VSCodeのインストール
- Pythonのインストール
- デスクトップアプリ用フレームワークのインストール
- デスクトップアプリ開発
上記の開発手順をそれぞれ解説します。
VSCodeのインストール
Visual Studio Code(VSCode)のインストーラーをダウンロードして、自身のPCにインストールします。
以下のURLからVisual Studio Codeをダウンロードできます。


PCのOSに合ったパッケージの安定版(Stable)を選択してインストーラーをダウンロードします。

ダウンロードしたインストーラーを起動すると、使用許諾画面が表示されるため、「同意する」にチェックし「次へ」をクリックします。

追加タスクの選択画面では、必要なオプションを選んだら「次へ」をクリックします。

インストール準備の完了画面で「インストール」をクリックします。

インストールを終えたら、「完了」をクリックします。
上記の操作にて、Visual Studio Codeのインストールが完了します。
Pythonのインストール
お使いのPCにダウンロードしたインストーラーを起動します。
また、本記事では以下のOSに対するインストール方法を解説します。
- Windows用のPythonインストール
- Mac用のPythonインストール
また、PythonによるローカルPC上での仮想環境を作成したい人は「【Python】ダウンロードとインストール方法から開発環境構築まで解説!」を一読ください。

Pythonのインストール|Windows
Windows用PCにダウンロードしたインストーラーを起動します。
起動すると、ウィンドウにてインストール画面が表示されます。

「Install Now」をクリックすると、インストールが始まります。
インストール中の画面へ切り替わるため、インストール完了まで待機します。

インストールが終了すると、「Setup was Successful」といったインストール完了画面になります。

以上で、プログラミング言語Pythonのインストールは完了です。
Pythonのインストール|Mac
Mac用PCにダウンロードしたインストーラーを起動します。
起動すると、ウィンドウにてインストール画面が表示されます。

「続ける」をクリックすると、「大切な情報」といった項目画面に遷移します。

「続ける」をクリックすると、「使用許諾契約」といった項目画面に遷移します。

「続ける」をクリックすると、画面上部からインストールを続けるために“同意する”かを求められます。
インストールを実行するために、“同意する”をクリックしてください。

次に、「インストール先」の選択になりますが、基本的に変更する必要はありません。

「続ける」をクリックすると、「インストールの種類」といった項目画面に遷移します。

ここで、インストール実行するためにPCのパスワード入力が求められるため、入力後にインストールをクリックしてください。

インストールが開始されたら、各ファイルがインストールされるまで待機します。

以上で、MacにおけるPythonのインストールは完了です。
デスクトップアプリ用フレームワークのインストール
ここでは、上述で紹介した各ライブラリのインストールコマンドを記載します。
また、Tkinterに関してはPythonに同梱されたライブラリであるため省略しています。
PySimpleGUI|インストール
python -m pip install -U PySimpleGUI
TkEasyGUI|インストール
python -m pip install -U TkEasyGUI
PyQt/PySide|インストール
python -m pip install -U PySide6
wxPython|インストール
python -m pip install -U wxPython
自身が利用したデスクトップ用ライブラリをインストールしてください。
以下から、例としてPySimpleGUIを利用したデスクトップアプリ開発を記載します。
Python製デスクトップアプリ作ってみた
ここでは、以下のディレクトリ構成でデスクトップ用のメモ帳アプリを開発します。
memo
└─ memo.py
- memo:開発用ディレクトリ名
- memo.py:メモ帳アプリのPython実行ファイル
個々人で任意のディレクトリ/ファイル名で構いません。
PySimpleGUIによるメモ機能実装
PySimpleGUIによるメモ機能を実装していきます。
# メモ帳アプリ
import os
import PySimpleGUI as sg
# 保存ファイル指定
SCRIPT_DIR = os.path.dirname(__file__)
SAVE_FILE = os.path.join(SCRIPT_DIR, "memo.txt")
# レイアウト定義
layout = [
[sg.Multiline(size=(60, 20), key="text")],
[sg.Button("保存"), sg.Button("開く")],
]
window = sg.Window("MEMO", layout=layout)
# イベントループ
while True:
# イベントと入力値の取得
event, values = window.read()
# 保存ボタン押下時
if event == "保存":
# 指定ファイルに保存
with open(SAVE_FILE, "w", encoding="utf-8") as f:
f.write(values["text"])
sg.popup("保存しました")
# 開くボタン押下時
if event == "開く":
# 保存先ファイルの存在確認
if not os.path.exists(SAVE_FILE):
sg.popup("ファイルがありません")
continue
# 保存されたファイル読み込み
with open(SAVE_FILE, "r", encoding="utf-8") as f:
text = f.read()
# 読み込みデータをテキストボックスへ反映
window["text"].update(text)
# 閉じるボタン押下時
if event == sg.WINDOW_CLOSED:
break
# 終了処理
window.close
- 各モジュールのインポート
- 保存ファイル指定
- 画面に対するレイアウト定義
- while文によるイベントループ
- 各イベント処理
import os
import PySimpleGUI as sg
osモジュールは、直接ローカルPC上のファイルを扱うためにインポートしています。
また、デスクトップアプリで利用するためPySimpleGUIもインポートします。
SCRIPT_DIR = os.path.dirname(__file__)
SAVE_FILE = os.path.join(SCRIPT_DIR, "memo.txt")
osモジュールのメソッドを利用し、ディレクトリ指定します。
また、ディレクトリに対してmemo.txtをファイルとして保存できるようSAVE_FILEを定義します。
layout = [
[sg.Multiline(size=(60, 20), key="text")],
[sg.Button("保存"), sg.Button("開く")],
]
window = sg.Window("MEMO", layout=layout)
レイアウト定義は、PySimpleGUIのMulitilineメソッドを利用し、テキスト入力ボックス(スクロール可)を生成します。
また、保存用ボタンと開くボタンも生成しておきます。
変数windowにて、レイアウトを格納しておきます。
while True:
# イベントと入力値の取得
event, values = window.read()
# 保存ボタン押下時
if event == "保存":
# 指定ファイルに保存
with open(SAVE_FILE, "w", encoding="utf-8") as f:
f.write(values["text"])
sg.popup("保存しました")
# 開くボタン押下時
if event == "開く":
# 保存先ファイルの存在確認
if not os.path.exists(SAVE_FILE):
sg.popup("ファイルがありません")
continue
# 保存されたファイル読み込み
with open(SAVE_FILE, "r", encoding="utf-8") as f:
text = f.read()
# 読み込みデータをテキストボックスへ反映
window["text"].update(text)
# 閉じるボタン押下時
if event == sg.WINDOW_CLOSED:
break
# 終了処理
window.close
while文によるイベントループを開始させます。
変数eventとvaluesを定義し、イベント内容と入力値の取得を.read()メソッドで実行します。
if文ではそれぞれイベント処理に合わせ、保存/開く/終了を実装しています。
終了イベントが発生した場合、window.closeでアプリを終了させます。
メモ帳アプリの動作確認
任意のディレクトリにて、以下のコマンドを実行します。
python memo.py

Python製デスクトップアプリをおしゃれにするには
もともとPythonは、バックエンド言語の一つとして開発が進められています。
そのため、デザイン性や高度なUI実装に不向きなプログラミング言語です。
フロントエンド領域とバックエンド領域で主な役割とニーズが異なるため、デザイン重視のアプリ開発はPythonのみだと難しいです。

- デザインや高度なUIの流行を実現
- 最新デバイスへの対応
- 各ブラウザのアップデート対応
一方で、バックエンド領域で主に求められるニーズは何になるか考えてみましょう。

- 誰がどこにいても繋がれるアクセス対応
- 24時間365日の連続稼働
- ネットセキュリティに対する対応
大まかではありますが、フロントエンドとバックエンドで求められる要件が異なることが分かると思います。
そのため、モダンなUIによるデスクトップアプリを開発したい場合におすすめなのがfletになります。
Fletを使用すると、開発者はPythonでリアルタイムのWeb/モバイル/デスクトップアプリを同一コードで簡単に構築できます。
Flutterを基盤としており、出来上がったモダンなUIコンポーネントを活用するため、フロントエンドの経験は必要ありません。
おすすめのPython製デスクトップアプリの作り方
デザインではなく機能実装に専念したい人は、「flet」によるデスクトップアプリ開発がおすすめです。
また、フロントエンド経験がなくても実践できるのが特徴で、ある程度のモダンなUIは簡単に実現できるようコンポーネントが用意されています。
- fletによるデスクトップ用ToDoアプリ開発
上記のデスクトップアプリ開発を実施していきます。
fletによるデスクトップ用ToDoアプリ開発

上記は、fletによるデスクトップ用ToDoアプリの完成画像になります。
以下の手順に沿って、ToDoアプリ開発を進めていきます。
- fletのインストール
- ページコントロールとイベント処理
- 再利用可能なUIコンポーネント作成
- リストの表示/編集/削除
- リストのフィルタリングとフッター作成
- ToDoアプリのexe化
Pythonのインストールや任意のディレクトリは、すでに準備できている想定で進めています。
fletのインストール
まず任意のディレクトリにcd(カレントディレクトリ)コマンドで移動したら、fletをインストールします。
pip install flet
次に、fletによる「Hello World!」を表示する簡単なアプリを作成します。
import flet as ft
def main(page: ft.Page):
page.add(ft.Text(value="Hello, world!"))
ft.app(main)
fletの特徴として、fletが用意する数多くのUIコンポーネントが存在します。
UIコンポーネントは、コントロールといった概念で構成されています。
各コントロールには、親子になるコントロールがあり、親コントロール(parent control)の中に子コントロール(child control)を含める形になります。
ここでは、fletにおけるコントロールの概念は割愛します。
一度実行した画面が以下になります。

ページコントロールとイベント処理
ここでは、シンプルなテキストフィールドとフローティングボタンを設置します。
import flet as ft
def main(page: ft.Page):
def add_clicked(e):
page.add(ft.Checkbox(label=new_task.value))
new_task.value = ""
page.update()
new_task = ft.TextField(hint_text="ToDoを入力してください")
page.add(new_task, ft.FloatingActionButton(icon=ft.Icons.ADD, on_click=add_clicked))
ft.app(main)
アプリ実行画面が以下になります。

page.add()の箇所を見ると分かりますが、子コントロールを追加しながらページを拡張していることが分かります。
また、page.update()することで再レンダリングが実行され、SPA(シングルページアプリケーション)がPythonでもfletを通して実現できます。
ページ全体の見栄えをよくするため、上部中央に位置できるよう調整しています。

import flet as ft
def main(page: ft.Page):
def add_clicked(e):
tasks_view.controls.append(ft.Checkbox(label=new_task.value))
new_task.value = ""
view.update()
new_task = ft.TextField(hint_text="ToDoを入力してください", expand=True)
tasks_view = ft.Column()
view=ft.Column(
width=600,
controls=[
ft.Row(
controls=[
new_task,
ft.FloatingActionButton(icon=ft.Icons.ADD, on_click=add_clicked),
],
),
tasks_view,
],
)
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.add(view)
ft.app(main)
アプリ全体をページの上部中央に配置し、幅を600ピクセルにしています。
TextFieldとFloatingButtonの「+」ボタンは水平に揃え、アプリの幅全体を占めるようにします。
再利用可能なUIコンポーネント作成
関数内のコード記述を続けることも可能ですが、再利用可能なUIコンポーネントにまとめるのがよいです。
ここでは、クラス化によってコードの可読性を高めることを意識しています。
import flet as ft
class TodoApp(ft.Column):
def __init__(self):
super().__init__()
self.new_task = ft.TextField(hint_text="ToDoを入力してください", expand=True)
self.tasks_view = ft.Column()
self.width = 600
self.controls = [
ft.Row(
controls=[
self.new_task,
ft.FloatingActionButton(
icon=ft.Icons.ADD, on_click=self.add_clicked
),
],
),
self.tasks_view,
]
def add_clicked(self, e):
self.tasks_view.controls.append(ft.Checkbox(label=self.new_task.value))
self.new_task.value = ""
self.update()
def main(page: ft.Page):
page.title = "Flet ToDo App"
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.update()
todo = TodoApp()
page.add(todo)
ft.app(main)
リストの表示/編集/削除
タスク項目がチェックボックスとして表示される簡易なToDoアプリが作成されました。
タスク名(チェックボックスのラベル)の横に「編集」と「削除」ボタンを追加し、機能とUIを改良します。
また、「編集」ボタン押下時はタスク名を編集モードに切り替えています。

各タスク項目はdisplay_viewによる「チェックボックス」「編集ボタン」「削除ボタン」のある行と、edit_viewによる「テキストフィールド」「保存ボタン」を実装します。
また、display_viewとedit_viewを新たなTaskクラスで実装します。
class Task(ft.Column):
def __init__(self, task_name, task_delete):
super().__init__()
self.task_name = task_name
self.task_delete = task_delete
self.display_task = ft.Checkbox(value=False, label=self.task_name)
self.edit_name = ft.TextField(expand=1)
self.display_view = ft.Row(
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.display_task,
ft.Row(
spacing=0,
controls=[
ft.IconButton(
icon=ft.Icons.CREATE_OUTLINED,
tooltip="Edit To-Do",
on_click=self.edit_clicked,
),
ft.IconButton(
ft.Icons.DELETE_OUTLINE,
tooltip="Delete To-Do",
on_click=self.delete_clicked,
),
],
),
],
)
self.edit_view = ft.Row(
visible=False,
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.edit_name,
ft.IconButton(
icon=ft.Icons.DONE_OUTLINE_OUTLINED,
icon_color=ft.Colors.GREEN,
tooltip="Update To-Do",
on_click=self.save_clicked,
),
],
)
self.controls = [self.display_view, self.edit_view]
def edit_clicked(self, e):
self.edit_name.value = self.display_task.label
self.display_view.visible = False
self.edit_view.visible = True
self.update()
def save_clicked(self, e):
self.display_task.label = self.edit_name.value
self.display_view.visible = True
self.edit_view.visible = False
self.update()
def delete_clicked(self, e):
self.task_delete(self)
また、「追加」ボタンがクリックされたときにTaskクラスのインスタンスを保持するようにToDoAppクラスを変更しました。
class TodoApp(ft.Column):
def __init__(self):
super().__init__()
self.new_task = ft.TextField(hint_text="ToDoを入力してください", expand=True)
self.tasks = ft.Column()
self.width = 600
self.controls = [
ft.Row(
controls=[
self.new_task,
ft.FloatingActionButton(
icon=ft.Icons.ADD, on_click=self.add_clicked
),
],
),
self.tasks,
]
def add_clicked(self, e):
task = Task(self.new_task.value, self.task_delete)
self.tasks.controls.append(task)
self.new_task.value = ""
self.update()
def task_delete(self, task):
self.tasks.controls.remove(task)
self.update()
全体コード
import flet as ft
class Task(ft.Column):
def __init__(self, task_name, task_delete):
super().__init__()
self.task_name = task_name
self.task_delete = task_delete
self.display_task = ft.Checkbox(value=False, label=self.task_name)
self.edit_name = ft.TextField(expand=1)
self.display_view = ft.Row(
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.display_task,
ft.Row(
spacing=0,
controls=[
ft.IconButton(
icon=ft.Icons.CREATE_OUTLINED,
tooltip="Edit To-Do",
on_click=self.edit_clicked,
),
ft.IconButton(
ft.Icons.DELETE_OUTLINE,
tooltip="Delete To-Do",
on_click=self.delete_clicked,
),
],
),
],
)
self.edit_view = ft.Row(
visible=False,
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.edit_name,
ft.IconButton(
icon=ft.Icons.DONE_OUTLINE_OUTLINED,
icon_color=ft.Colors.GREEN,
tooltip="Update To-Do",
on_click=self.save_clicked,
),
],
)
self.controls = [self.display_view, self.edit_view]
def edit_clicked(self, e):
self.edit_name.value = self.display_task.label
self.display_view.visible = False
self.edit_view.visible = True
self.update()
def save_clicked(self, e):
self.display_task.label = self.edit_name.value
self.display_view.visible = True
self.edit_view.visible = False
self.update()
def delete_clicked(self, e):
self.task_delete(self)
class TodoApp(ft.Column):
def __init__(self):
super().__init__()
self.new_task = ft.TextField(hint_text="ToDoを入力してください", expand=True)
self.tasks = ft.Column()
self.width = 600
self.controls = [
ft.Row(
controls=[
self.new_task,
ft.FloatingActionButton(
icon=ft.Icons.ADD, on_click=self.add_clicked
),
],
),
self.tasks,
]
def add_clicked(self, e):
task = Task(self.new_task.value, self.task_delete)
self.tasks.controls.append(task)
self.new_task.value = ""
self.update()
def task_delete(self, task):
self.tasks.controls.remove(task)
self.update()
def main(page: ft.Page):
page.title = "Flet ToDo App"
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.update()
todo = TodoApp()
page.add(todo)
ft.app(main)
ここまでの実装が正常に動作するか確認しましょう。

「編集」と「削除」が実施できることが確認できました。
リストのフィルタリングとフッター作成
タスクの「作成」「編集」「削除」といった機能的な実装ができたので、タスクをステータス別でフィルタリングしていきます。
# ...省略
class TodoApp(ft.Column):
def __init__(self):
super().__init__()
self.new_task = ft.TextField(hint_text="ToDoを入力してください", expand=True)
self.tasks = ft.Column()
self.filter = ft.Tabs(
selected_index=0,
on_change=self.tabs_changed,
tabs=[ft.Tab(text="ALL"), ft.Tab(text="実行中"), ft.Tab(text="完了")],
)
# ....省略
タスクのステータスに応じてリストを表示するため、「ALL」「実行中」「完了」の3つにタブを分けています。
また、ここでは同じリストを保持しステータスに応じたタスク表示のみを実行する方法になります。
before_update()は全てのタスクを反復処理し、タスクのステータスに応じてプロパティを更新します。
# ...省略
def before_update(self):
status = self.filter.tabs[self.filter.selected_index].text
for task in self.tasks.controls:
task.visible = (
status == "ALL"
or (status == "実行中" and task.completed == False)
or (status == "完了" and task.completed)
)
# ....省略
フィルタリングはタブをクリックする、あるいはステータス変更時に実行されます。
そのため、タブ選択時とタスクのステータス変更時の関数をそれぞれ追加します。
class TodoApp(ft.Column):
# ...省略
def tabs_changed(self, e):
self.update()
def task_status_change(self, e):
self.update()
def add_clicked(self, e):
#
task = Task(self.new_task.value, self.task_status_change, self.task_delete)
# ...省略
class Task(ft.Column):
def __init__(self, task_name, task_status_change, task_delete):
super().__init__()
self.completed = False
self.task_name = task_name
self.task_status_change = task_status_change
self.task_delete = task_delete
self.display_task = ft.Checkbox(
value=False, label=self.task_name, on_change=self.status_changed
)
def status_changed(self, e):
self.completed = self.display_task.value
self.task_status_change()
# ...省略
また、フッターによるカウント数の表示と一部を追加/調整し、以下の全体コードになります。
本アプリの全体コード
import flet as ft
class Task(ft.Column):
def __init__(self, task_name, task_status_change, task_delete):
super().__init__()
self.completed = False
self.task_name = task_name
self.task_status_change = task_status_change
self.task_delete = task_delete
self.display_task = ft.Checkbox(
value=False, label=self.task_name, on_change=self.status_changed
)
self.edit_name = ft.TextField(expand=1)
self.display_view = ft.Row(
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.display_task,
ft.Row(
spacing=0,
controls=[
ft.IconButton(
icon=ft.icons.CREATE_OUTLINED,
tooltip="Edit To-Do",
on_click=self.edit_clicked,
),
ft.IconButton(
ft.icons.DELETE_OUTLINE,
tooltip="Delete To-Do",
on_click=self.delete_clicked,
),
],
),
],
)
self.edit_view = ft.Row(
visible=False,
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.edit_name,
ft.IconButton(
icon=ft.icons.DONE_OUTLINE_OUTLINED,
icon_color=ft.colors.GREEN,
tooltip="Update To-Do",
on_click=self.save_clicked,
),
],
)
self.controls = [self.display_view, self.edit_view]
def edit_clicked(self, e):
self.edit_name.value = self.display_task.label
self.display_view.visible = False
self.edit_view.visible = True
self.update()
def save_clicked(self, e):
self.display_task.label = self.edit_name.value
self.display_view.visible = True
self.edit_view.visible = False
self.update()
def status_changed(self, e):
self.completed = self.display_task.value
self.task_status_change(self)
def delete_clicked(self, e):
self.task_delete(self)
class TodoApp(ft.Column):
def __init__(self):
super().__init__()
self.new_task = ft.TextField(
hint_text="ToDoを入力してください", on_submit=self.add_clicked, expand=True
)
self.tasks = ft.Column()
self.filter = ft.Tabs(
scrollable=False,
selected_index=0,
on_change=self.tabs_changed,
tabs=[ft.Tab(text="ALL"), ft.Tab(text="実行中"), ft.Tab(text="完了")],
)
self.items_left = ft.Text("0 items left")
self.width = 600
self.controls = [
ft.Row(
[ft.Text(value="Todos", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM)],
alignment=ft.MainAxisAlignment.CENTER,
),
ft.Row(
controls=[
self.new_task,
ft.FloatingActionButton(
icon=ft.icons.ADD, on_click=self.add_clicked
),
],
),
ft.Column(
spacing=25,
controls=[
self.filter,
self.tasks,
ft.Row(
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
self.items_left,
ft.OutlinedButton(
text="Clear completed", on_click=self.clear_clicked
),
],
),
],
),
]
def add_clicked(self, e):
if self.new_task.value:
task = Task(self.new_task.value, self.task_status_change, self.task_delete)
self.tasks.controls.append(task)
self.new_task.value = ""
self.new_task.focus()
self.update()
def task_status_change(self, task):
self.update()
def task_delete(self, task):
self.tasks.controls.remove(task)
self.update()
def tabs_changed(self, e):
self.update()
def clear_clicked(self, e):
for task in self.tasks.controls[:]:
if task.completed:
self.task_delete(task)
def before_update(self):
status = self.filter.tabs[self.filter.selected_index].text
count = 0
for task in self.tasks.controls:
task.visible = (
status == "ALL"
or (status == "実行中" and task.completed == False)
or (status == "完了" and task.completed)
)
if not task.completed:
count += 1
self.items_left.value = f"{count} active item(s) left"
def main(page: ft.Page):
page.title = "ToDo App"
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.scroll = ft.ScrollMode.ADAPTIVE
page.add(TodoApp())
ft.app(main)
実行画面が以下になります。

ToDoアプリのexe化
最後に、開発したToDoアプリをexe化します。
fletコマンドでexe化を実現するためには、別パッケージのPyInstallerをインストールする必要があります。
pip install pyinstaller
PyInstallerをインストールすることで、fletコマンドからパッケージ化できるようになります。
flet pack <your_program.py> --name <bundle_name>
今回のアプリの場合は、以下で実行しています。
flet pack -n todo -D main.py
また、flet pack -hにて各オプションを確認できます。
最後は、作成されたtodo.exeファイルを起動することでアプリを無事実行できました。
Python製デスクトップアプリのexe化
実行ファイル形式であるexe化は、いくつか方法があります。
ツール名 | 概要 |
---|---|
PyInstaller | Windows/Mac/Linuxに対応した変換ツールで単一ファイルに固められる |
cx_Freeze | Windows/Mac/Linuxに対応した変換ツールで設定ファイルに詳細内容を指定できる |
Py2exe | Windows用の実行ファイルに変換するツール |
Py2app | MacOS用の実行ファイルに変換するツール |
以下は、PyInstllerによる実行ファイル作成を実施します。
PyInstallerによるexeファイル作成
ここでは、PyInstallerによるexeファイル作成を実施します。
以下のコマンドで任意のディレクトリにPyInstallerをインストールします。
python -m pip install PyInstaller
また、exe化したいファイルがあれば以下のコマンドで実行できます。
pyinstaller --onefile --noconsole memo.py
適宜、必要なオプションを利用すると管理しやすくなります。
オプション | 説明 |
---|---|
–onedirまたは-D | 出力を一つのディレクトリにまとめる |
–onefileまたは-F | 実行ファイルを一つにまとめる |
–noconsoleまたは-w | ターミナルを非表示にする |
–clean | 前回作成したキャッシュファイルを削除してから再作成 |
Python製デスクトップアプリの配布方法
デスクトップアプリ開発が完遂できた人は、自分以外に利用してもらう場合にアプリの配布方法を考える必要があります。
代表的な配布方法として、以下の方法があります。
- exeファイル形式で配布する
- プログラムをそのまま配布する
- 仮想環境を丸ごと配布する
exe化した実行ファイル形式の配布方法が最も一般的かなと思います。
ただし、プログラムをそのまま配布する場合や仮想環境(例:Docker)を配布する場合は、設定ファイルや必要なパッケージ群を整理/調整する必要があります。
requirements.txtやイメージファイル/コンテナファイルの設定を確認しておきましょう。
コメント