← 기사 목록
日本語https://qiita.com/tags/ai/feed

AIに頼り切って家計簿アプリを追加開発する。:画面作成

추출된 키워드

24
AI·5家計簿アプリ·5C#.net·4windows form·4SQLite3·4OCR·4System.Data.SQLite·3KakeiboApp·3OpenCV·3OpenCvSharp4.runtime.win·3OpenCvSharp4·3Tesseract·3画像前処理·2傾き補正·2ノイズ除去·2二値化·2正規表現解析·2Excel出力·2DataGridView·2OcrService·2DatabaseService·2ExpenseListForm·2ExpenseEntryForm·2MenuForm·2

원문

12,258
AIに頼り切って家計簿アプリを追加開発する。:画面作成

はじめに

前回、家計簿アプリの設計を行いました(AIがやりました)
今回はその設計内容を元に取りあえず動く状態にします。

プロンプト

家計簿アプリの開発を行います。
最低限画面を表示したいため、コードを出力してください。
各画面の機能等は後ほど実装します。

要件は以下です。

・機能要件
 -開発:C#.net(windows form)
 -データ管理:SQLite3
・画面設計
 -メニュー画面
  -支出登録画面
  -支出一覧画面
・機能概要
 -メニュー画面
  -ボタン押下で各画面を表示する。
 -支出登録画面
  -支出情報を登録します。
   レシート等支出情報を画像で入力、OCR機能で各項目に自動入力を行います。
   OCRより入力された情報を人間が目視チェック、修正を行い登録します。
 -支出内保持情報
  -支出情報の一覧を表示します。
・支出情報内保持項目
 -日付
 -金額
 -支払方法
 -備考
 -商品一覧
  -商品名
  -商品区分
  -金額
  -数量
  -税率
  -備考

回答

以下は、まず「画面が起動し遷移できる最低限構成」のサンプルです。
機能本体(SQLite保存、OCR、一覧表示等)は後続実装を前提に、画面と土台クラスのみ作成しています。

構成は以下です。

KakeiboApp
├─ Program.cs
├─ Forms
│   ├─ MenuForm.cs
│   ├─ ExpenseEntryForm.cs
│   └─ ExpenseListForm.cs
│
├─ Models
│   ├─ Expense.cs
│   └─ ExpenseItem.cs
│
├─ Services
│   ├─ DatabaseService.cs
│   └─ OcrService.cs
│
└─ App.config

NuGet

まず以下をインストールしてください。

  • SQLite
  • OCR

推奨:

System.Data.SQLite
Tesseract
OpenCvSharp4
OpenCvSharp4.runtime.win

Program.cs

using System;
using System.Windows.Forms;
using KakeiboApp.Forms;

namespace KakeiboApp
{
    internal static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new MenuForm());
        }
    }
}

Models/Expense.cs

using System;
using System.Collections.Generic;

namespace KakeiboApp.Models
{
    public class Expense
    {
        public int Id { get; set; }

        public DateTime ExpenseDate { get; set; }

        public decimal Amount { get; set; }

        public string PaymentMethod { get; set; }

        public string Note { get; set; }

        public List<ExpenseItem> Items { get; set; }
            = new List<ExpenseItem>();
    }
}

Models/ExpenseItem.cs

namespace KakeiboApp.Models
{
    public class ExpenseItem
    {
        public int Id { get; set; }

        public string ProductName { get; set; }

        public string Category { get; set; }

        public decimal Amount { get; set; }

        public int Quantity { get; set; }

        public decimal TaxRate { get; set; }

        public string Note { get; set; }
    }
}

Services/DatabaseService.cs

using System.Data.SQLite;

namespace KakeiboApp.Services
{
    public class DatabaseService
    {
        private readonly string _connectionString
            = "Data Source=kakeibo.db;Version=3;";

        public void Initialize()
        {
            using (var connection = new SQLiteConnection(_connectionString))
            {
                connection.Open();

                string expenseTable = @"
CREATE TABLE IF NOT EXISTS Expenses (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    ExpenseDate TEXT,
    Amount REAL,
    PaymentMethod TEXT,
    Note TEXT
);";

                string itemTable = @"
CREATE TABLE IF NOT EXISTS ExpenseItems (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    ExpenseId INTEGER,
    ProductName TEXT,
    Category TEXT,
    Amount REAL,
    Quantity INTEGER,
    TaxRate REAL,
    Note TEXT
);";

                using (var command = new SQLiteCommand(expenseTable, connection))
                {
                    command.ExecuteNonQuery();
                }

                using (var command = new SQLiteCommand(itemTable, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

Services/OcrService.cs

namespace KakeiboApp.Services
{
    public class OcrService
    {
        public void ExecuteOcr(string imagePath)
        {
            // TODO:
            // Tesseract OCR実装予定

            // 例:
            // ・店舗名取得
            // ・商品一覧取得
            // ・金額取得
            // ・日付取得
        }
    }
}

Forms/MenuForm.cs

using System;
using System.Windows.Forms;

namespace KakeiboApp.Forms
{
    public class MenuForm : Form
    {
        private Button btnExpenseEntry;
        private Button btnExpenseList;

        public MenuForm()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            Text = "家計簿アプリ - メニュー";
            Width = 400;
            Height = 250;
            StartPosition = FormStartPosition.CenterScreen;

            btnExpenseEntry = new Button();
            btnExpenseEntry.Text = "支出登録";
            btnExpenseEntry.Width = 200;
            btnExpenseEntry.Height = 40;
            btnExpenseEntry.Top = 40;
            btnExpenseEntry.Left = 80;
            btnExpenseEntry.Click += BtnExpenseEntry_Click;

            btnExpenseList = new Button();
            btnExpenseList.Text = "支出一覧";
            btnExpenseList.Width = 200;
            btnExpenseList.Height = 40;
            btnExpenseList.Top = 100;
            btnExpenseList.Left = 80;
            btnExpenseList.Click += BtnExpenseList_Click;

            Controls.Add(btnExpenseEntry);
            Controls.Add(btnExpenseList);
        }

        private void BtnExpenseEntry_Click(object sender, EventArgs e)
        {
            var form = new ExpenseEntryForm();
            form.ShowDialog();
        }

        private void BtnExpenseList_Click(object sender, EventArgs e)
        {
            var form = new ExpenseListForm();
            form.ShowDialog();
        }
    }
}

Forms/ExpenseEntryForm.cs

using System;
using System.Windows.Forms;

namespace KakeiboApp.Forms
{
    public class ExpenseEntryForm : Form
    {
        private TextBox txtDate;
        private TextBox txtAmount;
        private TextBox txtPaymentMethod;
        private TextBox txtNote;

        private DataGridView dgvItems;

        private Button btnSelectImage;
        private Button btnOcr;
        private Button btnSave;

        public ExpenseEntryForm()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            Text = "支出登録";
            Width = 1000;
            Height = 700;
            StartPosition = FormStartPosition.CenterScreen;

            Label lblDate = new Label();
            lblDate.Text = "日付";
            lblDate.Top = 20;
            lblDate.Left = 20;

            txtDate = new TextBox();
            txtDate.Top = 40;
            txtDate.Left = 20;
            txtDate.Width = 200;

            Label lblAmount = new Label();
            lblAmount.Text = "金額";
            lblAmount.Top = 80;
            lblAmount.Left = 20;

            txtAmount = new TextBox();
            txtAmount.Top = 100;
            txtAmount.Left = 20;
            txtAmount.Width = 200;

            Label lblPayment = new Label();
            lblPayment.Text = "支払方法";
            lblPayment.Top = 140;
            lblPayment.Left = 20;

            txtPaymentMethod = new TextBox();
            txtPaymentMethod.Top = 160;
            txtPaymentMethod.Left = 20;
            txtPaymentMethod.Width = 200;

            Label lblNote = new Label();
            lblNote.Text = "備考";
            lblNote.Top = 200;
            lblNote.Left = 20;

            txtNote = new TextBox();
            txtNote.Top = 220;
            txtNote.Left = 20;
            txtNote.Width = 300;

            dgvItems = new DataGridView();
            dgvItems.Top = 280;
            dgvItems.Left = 20;
            dgvItems.Width = 920;
            dgvItems.Height = 300;

            dgvItems.ColumnCount = 6;

            dgvItems.Columns[0].Name = "商品名";
            dgvItems.Columns[1].Name = "商品区分";
            dgvItems.Columns[2].Name = "金額";
            dgvItems.Columns[3].Name = "数量";
            dgvItems.Columns[4].Name = "税率";
            dgvItems.Columns[5].Name = "備考";

            btnSelectImage = new Button();
            btnSelectImage.Text = "画像選択";
            btnSelectImage.Top = 600;
            btnSelectImage.Left = 20;

            btnSelectImage.Click += BtnSelectImage_Click;

            btnOcr = new Button();
            btnOcr.Text = "OCR実行";
            btnOcr.Top = 600;
            btnOcr.Left = 140;

            btnOcr.Click += BtnOcr_Click;

            btnSave = new Button();
            btnSave.Text = "登録";
            btnSave.Top = 600;
            btnSave.Left = 260;

            btnSave.Click += BtnSave_Click;

            Controls.Add(lblDate);
            Controls.Add(txtDate);

            Controls.Add(lblAmount);
            Controls.Add(txtAmount);

            Controls.Add(lblPayment);
            Controls.Add(txtPaymentMethod);

            Controls.Add(lblNote);
            Controls.Add(txtNote);

            Controls.Add(dgvItems);

            Controls.Add(btnSelectImage);
            Controls.Add(btnOcr);
            Controls.Add(btnSave);
        }

        private void BtnSelectImage_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();

            dialog.Filter = "画像ファイル|*.png;*.jpg;*.jpeg";

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                MessageBox.Show($"選択画像: {dialog.FileName}");
            }
        }

        private void BtnOcr_Click(object sender, EventArgs e)
        {
            MessageBox.Show("OCR実行(未実装)");
        }

        private void BtnSave_Click(object sender, EventArgs e)
        {
            MessageBox.Show("登録処理(未実装)");
        }
    }
}

Forms/ExpenseListForm.cs

using System.Windows.Forms;

namespace KakeiboApp.Forms
{
    public class ExpenseListForm : Form
    {
        private DataGridView dgvExpenses;

        public ExpenseListForm()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            Text = "支出一覧";
            Width = 1000;
            Height = 700;
            StartPosition = FormStartPosition.CenterScreen;

            dgvExpenses = new DataGridView();

            dgvExpenses.Dock = DockStyle.Fill;
            dgvExpenses.ColumnCount = 5;

            dgvExpenses.Columns[0].Name = "日付";
            dgvExpenses.Columns[1].Name = "金額";
            dgvExpenses.Columns[2].Name = "支払方法";
            dgvExpenses.Columns[3].Name = "備考";
            dgvExpenses.Columns[4].Name = "商品数";

            Controls.Add(dgvExpenses);
        }
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

次に実装すべき機能

次段階では以下順序がおすすめです。

  • SQLite初期化
  • 支出登録保存
  • 支出一覧取得
  • OCR実装
  • OCR→DataGridView自動反映
  • Excel出力
  • 商品カテゴリマスタ
  • 検索機能
  • 月別集計
  • グラフ表示

OCR実装時のおすすめ構成

OCRは以下構成が扱いやすいです。

画像
 ↓
OpenCvSharp
  ・傾き補正
  ・ノイズ除去
  ・二値化
 ↓
Tesseract OCR
 ↓
正規表現解析
 ↓
Expenseへ反映

レシートOCRは「画像前処理」が精度にかなり影響します。
単純OCRより、OpenCV併用を推奨します。

起動確認

最後に

次回はOCR取り込みを作成していこうと思います。