今日のPython「家計簿アプリ1:クラスとJSONに挑戦」

2024年11月12日火曜日

Python Python_家計簿アプリ編 公開

t f B! P L

 


「はじめに」

今回は、クラスとJSONに挑戦してみたいと思います。

クラスは、オブジェクト指向のプログラムで
プログラムのコード量を減らしたり
プログラムの変更が容易にできたりする
という利点があり、

JSONは、フロントエンドとバックエンドのデータのやり取りや、言語間を超えるデータのやり取りを得意としていて、汎用性があるそうです。

今後、プログラムの合理化やWEBアプリの作成等にも挑戦していきたいと考えると、これらの知識が必要ではないか、と思い、今回のPython学習の対象にしました。


これまで「ToDoアプリ」に、機能を追加することで少しずつ、Python学習を進めてきました。
この「ToDoアプリ」にクラスを導入しようか、とも考えましたが、プログラムも随分長くなり、オブジェクト指向のプログラムでコードを書き直すのは大変そうです。
そこで、新たに短いプログラムでこれらに挑戦することにしました。

なお、「ToDoアプリ」の見直しも行っていく予定です。これまでの経緯はPython_ToDoアプリ編として、こちらからご覧いただけます(→更新履歴

「家計簿アプリ」で挑戦


クラスとJSONを使って、新たに作成を始めるアプリは、どのようなものがよいか?

データベースを使って「ToDoアプリ」とは、違うものと考えたところ、
「家計簿アプリ」を思いつきました。

日々の収支をデータベースとして使用し、さらに、収支計算をするという新たな課題にも挑戦できます。

ということで、新たなアプリ作成します。

では、始めます。

プログラムの作成


まずは、基本機能のみの物を作りました。

CUI画面で、収支の入力をして、そのデータを、JSON形式で保存、という機能のみです。

プログラムを機能別に3つに分けて、3つのファイルにしています。

(1)メインプログラム
   ファイル名KDmain.py
(2)収支データ管理用プログラム
   ファイル名KDpay.py
(3)入力画面管理用プログラム
   ファイル名KDui.py

(2)(3)でクラスを定義して
(1)で(2)(3)をimportして使います。

では、各プログラムの概要を説明します。
 

(1)メインプログラム「KDmain.py」の概要

メインプログラムを関数として

def main():

最終行のif文で呼び出します。

if __name__ == '__main__':
    main()
 


プログラム開始直後に、データ保存用のJSONファイルが存在するか確認し、

無ければ、新たにファイルを作成します。

 

    # 既存ファイルを確認
    if not os.path.exists(file_path):
        # 無ければ作成
        with open( file_path, mode='w', encoding="UTF-8" ) as open_json:
            initial_list = [{
                            "month"   :  0    , #"月"
                            "day"     :  0    , #"日"
                            "content" : "内訳",
                            "amount"  :  0      #"金額"
            } ]
            json.dump( initial_list, open_json, indent=4, ensure_ascii=False)


(2)のプログラムで定義したPayManagerクラスを呼び出してオブジェクトを作成します。

  # ペイ(収支)データ管理用クラスオブジェクト作成    
    pay_manager = PayManager()


JSONファイルを呼び出して、読み込んだデータを先程のPayManagerオブジェクトに追加します。

    # ファイルの読み込み
    with open( file_path, mode='r', encoding="UTF-8") as open_json:
        json_load_list = json.load(open_json)

    # 読み込んだファイルをペイデータ管理用クラスオブジェクトに追加
    for pay_data in json_load_list:
        pay_manager.add_pay(pay_data)


そして、(3)のプログラムで定義したクラスからUIManagerオブジェクトを作成し、

PayManagerオブジェクトを引き継ぎます。

    # 入力画面管理用クラスオブジェクト作成    
    ui_manager = UIManager(pay_manager)

ここでCUI画面の待ち受け状態になります。

プログラム実行中は、データの保存は行わず、

プログラムの終了時に、PayManagerオブジェクトから、情報を受け取って、JSONファイルとして保存します。

    # ファイル書き込み
    with open(file_path, mode='w',encoding="UTF-8") as open_json:
        updated_data_list=[]
        for pay in pay_manager.pay_data_list:
            updated_data_list.append({
                                "month":   pay.month,              
                                "day":     pay.day,
                                "content": pay.content,
                                "amount":  pay.amount
            })
        json.dump(updated_data_list, open_json ,indent=4,ensure_ascii=False)


以上が「(1)メインプログラム」の内容です。


(2)収支データ管理用プログラム「KDpay.py」の概要

クラスを2つ定義しています。

まず、個別の収支データを定義するクラスです。4つの情報を持たせています。

class Pay:
    def __init__(self, pay_data):
        # ペイ(収支)データの初期化
        self.month   = pay_data["month"]      
        self.day     = pay_data["day"]
        self.content = pay_data["content"]
        self.amount  = pay_data["amount"]


次に、個別の収支データをリストにして管理するクラスです。リストを作成して個別データの追加と削除が出来るようにしています。削除はリストのpop()メソッドで指定位置の要素を削除するようにしています。

class PayManager:
    def __init__(self):
        # ペイデータリストの初期化
        self.pay_data_list = []

    def add_pay(self, pay_data):
        # 新しいペイデータをペイデータリストに追加
        new_pay_data = Pay(pay_data)
        self.pay_data_list.append(new_pay_data)

    def delete_pay(self, pay_data):
        # ペイデータをペイデータリストから削除
        self.pay_data_list.pop(pay_data)


以上が「(2)収支データ管理用プログラム」の内容です。


(3)入力画面管理用プログラム「KDui.py」の概要

クラスを1つ定義しています。オブジェクトが出来ると、入力画面を呼び出して、

class UIManager:
    def __init__(self, pay_manager):
        # UIの初期化
        self.pay_manager = pay_manager
        # 入力画面呼び出し
        self.command_input()


CUI画面で入力の待ち受け状態になります。入力があると、入力に対応する処理をするための関数を呼び出します。この部分は、CUI版のToDoアプリから流用しています。

    def command_input(self):
        # 入力画面の表示
        while True:
            command = input("(1表示、2追加、3更新、4削除、0終了)どの操作を行いますか?番号: ")
            if command == "0": #'終了'
                break
            elif command == "1": #'表示'
                self.show_pays()
            elif command == "2": #'追加'
                self.add_pay()
            elif command == "3": #'更新'
                self.update_pay()
            elif command == "4": #'削除'
                self.delete_pay()
            else:
                print("0~4の番号を入力してください。")

リストを画面に表示するための関数です。PayManagerオブジェクトからリストを読み込んで画面に表示します。

    def show_pays(self):
        # リストを表示
        cnt = 0
        for pay_data in self.pay_manager.pay_data_list:
            print(f"ID:{cnt}{vars(self.pay_manager.pay_data_list[cnt])}")
            cnt += 1  


データを追加するための関数です。入力されたデータをPayManagerオブジェクトに追加します。

  def add_pay(self):
        # レコード(辞書)を追加
        try:
            month   = int(input("月を入力(数値)してください: "))      
            day     = int(input("日を入力(数値)してください: "))
            content =     input("内訳を入力してください: ")
            amount  = int(input("金額を入力(数値)してください: "))
        except ValueError:
            print("エラー:数値以外が入力されました")
            return self.add_pay()

        new_pay_dict = {
            "month"  : month,
            "day"    : day,
            "content": content,
            "amount" : amount
        }
        self.pay_manager.add_pay(new_pay_dict)


データを削除するための関数です。ID番号を指定して、PayManagerオブジェクトから削除します。入力した数値がリストのpop()メソッドの指定位置になり、その要素を削除します。

    def delete_pay(self):
        #レコード(辞書)を削除
        delete_column = input("削除する行のID番号を入力してください: ")
        number = int(delete_column)
        self.pay_manager.delete_pay(number)


データを更新するための関数です。


    def update_pay(self):
        #レコード(辞書)を更新
        self.delete_pay()
        self.add_pay()


以上が「(3)入力画面管理用プログラム」の内容です。




プログラム実行の結果

実際にプログラムを実行すると、下のような画面が表示されます。







「最後に」


以上、「家計簿アプリ」の作成をしてみました。


まだまだ、見た目も悪く、開発途中感満載ですが、一応基本動作が出来るようになりました。

今回の主な目的は、クラスとJSONの理解です。
その意味でいうと一定の成果が出て満足しています。

これからはこれをベースに試行錯誤しながら、段階的に、機能を追加していきたいと思います。


なお、今回CUIで作成したのは、GoogleColabでも開発が進められる、という利点があるからです。
スマホがあれば、出先や移動の途中で時間が出来たときに、いつでもプログラムとテスト動作の確認が出来ます。
アプリの見た目よりクラスとJSONの理解を優先させて、時間を有効に使うという点では良かったと思います。



最後にソースコードを載せておきます。


実行形式のファイルにする(exe化)場合は、こちらを参考にしてください(→過去記事



ソースコード「KDmain.py」



######################################################
# ファイル名:KDmain.py
# バージョン:Ver1.0
# 機能:アプリの基幹制御
# 作成日:2024/10/12
# 更新日:2024/10/12
######################################################

import os
import json
from KDpay import PayManager
from KDui import UIManager

file_path = 'KDjson.json'

def main():
    # 既存ファイルを確認
    if not os.path.exists(file_path):
        # 無ければ作成
        with open( file_path, mode='w', encoding="UTF-8" ) as open_json:
            initial_list = [{
                            "month"   :  0    , #"月"
                            "day"     :  0    , #"日"
                            "content" : "内訳",
                            "amount"  :  0      #"金額"
            } ]
            json.dump( initial_list, open_json, indent=4, ensure_ascii=False)

    # ペイ(収支)データ管理用クラスオブジェクト作成    
    pay_manager = PayManager()
   
    # ファイルの読み込み
    with open( file_path, mode='r', encoding="UTF-8") as open_json:
        json_load_list = json.load(open_json)

    # 読み込んだファイルをペイ(収支)データ管理用クラスオブジェクトに追加
    for pay_data in json_load_list:
        pay_manager.add_pay(pay_data)

    # 入力画面管理用クラスオブジェクト作成    
    ui_manager = UIManager(pay_manager)

    # ファイル書き込み
    with open(file_path, mode='w',encoding="UTF-8") as open_json:
        updated_data_list=[]
        for pay in pay_manager.pay_data_list:
            updated_data_list.append({
                                "month":   pay.month,              
                                "day":     pay.day,
                                "content": pay.content,
                                "amount":  pay.amount
            })
        json.dump(updated_data_list, open_json ,indent=4,ensure_ascii=False)

# プログラム開始宣言
if __name__ == '__main__':
    main()
 

 


 

 

 

ソースコード「KDpay.py」

 

######################################################
# ファイル名:KDpay.py
# バージョン:Ver1.0
# 機能:レコードを管理
# 作成日:2024/10/12
# 更新日:2024/10/12
######################################################

class Pay:
    def __init__(self, pay_data):
        # ペイ(収支)データの初期化
        self.month   = pay_data["month"]      
        self.day     = pay_data["day"]
        self.content = pay_data["content"]
        self.amount  = pay_data["amount"]

class PayManager:
    def __init__(self):
        # ペイデータリストの初期化
        self.pay_data_list = []

    def add_pay(self, pay_data):
        # 新しいペイデータをペイデータリストに追加
        new_pay_data = Pay(pay_data)
        self.pay_data_list.append(new_pay_data)

    def delete_pay(self, pay_data):
        # ペイデータをペイデータリストから削除
        self.pay_data_list.pop(pay_data)






ソースコード「KDui.py」


######################################################
# ファイル名:KDui.py
# バージョン:Ver1.0
# 機能:CUIで入力画面を制御
# 作成日:2024/10/12
# 更新日:2024/10/12
######################################################

class UIManager:
    def __init__(self, pay_manager):
        # UIの初期化
        self.pay_manager = pay_manager
        # 入力画面呼び出し
        self.command_input()

    def command_input(self):
        # 入力画面の表示
        while True:
            command = input("(1表示、2追加、3更新、4削除、0終了)どの操作を行いますか?番号: ")
            if command == "0": #'終了'
                break
            elif command == "1": #'表示'
                self.show_pays()
            elif command == "2": #'追加'
                self.add_pay()
            elif command == "3": #'更新'
                self.update_pay()
            elif command == "4": #'削除'
                self.delete_pay()
            else:
                print("0~4の番号を入力してください。")

    def show_pays(self):
        # リストを表示
        cnt = 0
        for pay_data in self.pay_manager.pay_data_list:
            print(f"ID:{cnt}{vars(self.pay_manager.pay_data_list[cnt])}")
            cnt += 1  
           
    def add_pay(self):
        # レコード(辞書)を追加
        try:
            month   = int(input("月を入力(数値)してください: "))      
            day     = int(input("日を入力(数値)してください: "))
            content =     input("内訳を入力してください: ")
            amount  = int(input("金額を入力(数値)してください: "))
        except ValueError:
            print("エラー:数値以外が入力されました")
            return self.add_pay()

        new_pay_dict = {
            "month"  : month,
            "day"    : day,
            "content": content,
            "amount" : amount
        }
        self.pay_manager.add_pay(new_pay_dict)

    def update_pay(self):
        #レコード(辞書)を更新
        self.delete_pay()
        self.add_pay()
       
    def delete_pay(self):
        #レコード(辞書)を削除
        delete_column = input("削除する行のID番号を入力してください: ")
        number = int(delete_column)
        self.pay_manager.delete_pay(number)











このブログを検索

アーカイブ

カテゴリー

QooQ