wxPythonとwxFormBuilderで家計簿アプリを真似してみる

wxPythonとwxFormBuilderを使用して
⑫ 画面遷移の処理を定義(最終回)【python tkinter sqlite3で家計簿を作る】 – memopy
こちらのアプリを真似してみました。

wxFormBuilderはpythonコードを書き出して、子classでごにょごにょするのが普通なのかもしれませんが、扱いづらいのでXRCでデザインを書き出して使用するようにしました。
参考アプリと違うところは日付はデートピッカー、入力画面はダイアログになっています。
sqliteは先にコマンドで作っています。

バリデーション等、必要ですが、とりあえず動く程度です。

XRCの処理は
wxPython & XRC で Custom Frame を作る方法3種 | 穀風
こちらを参考にしています。

実行例

メイン画面

ダイアログ画面

wxFormBuilder

構成

メイン画面

入力用ダイアログ

メインコード(kakeibo.py)

import wx
import wx.xrc
import wx.grid
import wx.adv
from dbSqliteClass import db_sqlite
 
__res = None
DB_NAME = 'kakeibo.db'
 
def get_resources():
    global __res
    if __res is None:
        __init_resources()
    return __res
 
def __init_resources():
    global __res
    __res = wx.xrc.XmlResource()
    __res.Load('kakeibo/kakeibo.xrc')
 
class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self)
        get_resources().LoadFrame(self, parent, 'MyFrame')
 
        self.Bind(wx.EVT_CLOSE, self.quit_button)
 
        self.btn1 = wx.xrc.XRCCTRL(self, 'btn1')
        self.btn1.Bind(wx.EVT_BUTTON, self.create_button)
        self.btn2 = wx.xrc.XRCCTRL(self, 'btn2')
        self.btn2.Bind(wx.EVT_BUTTON, self.quit_button)
        self.btn3 = wx.xrc.XRCCTRL(self, 'btn3')
        self.kikan1 = wx.xrc.XRCCTRL(self, 'kikan1')
        self.kikan2 = wx.xrc.XRCCTRL(self, 'kikan2')
        self.btn3.Bind(wx.EVT_BUTTON, self.select_sql)
 
        self.gridTable = wx.xrc.XRCCTRL(self, 'gridTable')
        self.gridTable.CreateGrid(5,3)
        # Set column labels.
        self.gridTable.SetColLabelValue(0, '日付')
        self.gridTable.SetColLabelValue(1, '内訳')
        self.gridTable.SetColLabelValue(2, '金額')
        self.tableShow()
 
    def tableShow(self):
        # Set cell values.
        c = db_sqlite(DB_NAME)
        sql = (
            'select acc_date, item_name, amount from'
            ' acc_data as a, item as i where'
            ' a.item_code = i.item_code order by acc_date'
        )
        acc = c.db_execute(sql).fetchall()
        i = 0
        for row in range(len(acc)):
            self.gridTable.SetCellValue(row, 0, str(acc[i]['acc_date']))
            self.gridTable.SetCellValue(row, 1, str(acc[i]['item_name']))
            am = f"{acc[i]['amount']:,}"
            self.gridTable.SetCellValue(row, 2, str(am))
            i += 1
        self.gridTable.AutoSize()
 
    def create_button(self, e):
        adid = AddItemDialog(self)
        adid.ShowModal()
        adid.Destroy()
 
    def quit_button( self, event ):
        self.Destroy()
 
    def select_sql( self, event ):
        kikan1_d = self.kikan1.GetValue()
        kikan1 = kikan1_d.Format("%Y-%m-%d")
        kikan2_d = self.kikan2.GetValue()
        kikan2 = kikan2_d.Format("%Y-%m-%d")
        # Set cell values.
        self.gridTable.ClearGrid()
        c = db_sqlite(DB_NAME)
        sql = (
            'select acc_date, item_name, amount from'
            ' acc_data as a, item as i where'
            ' a.acc_date between ? and ?'
            ' and a.item_code = i.item_code order by acc_date'
        )
        acc = c.db_execute_place(sql, (kikan1, kikan2)).fetchall()
        i = 0
        for row in range(len(acc)):
            self.gridTable.SetCellValue(row, 0, str(acc[i]['acc_date']))
            self.gridTable.SetCellValue(row, 1, str(acc[i]['item_name']))
            am = f"{acc[i]['amount']:,}"
            self.gridTable.SetCellValue(row, 2, str(am))
            i += 1
        self.gridTable.AutoSize()
 
class AddItemDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self)
        get_resources().LoadDialog(self, parent, 'MyDialog')
        self.SetSize((300, 250))
        self.parent = parent
 
        c = db_sqlite(DB_NAME)
 
        self.dbtn1 = wx.xrc.XRCCTRL(self, 'dbtn1')
        self.dbtn1.Bind( wx.EVT_BUTTON, self.quit_button )
        self.dbtn2 = wx.xrc.XRCCTRL(self, 'dbtn2')
        self.dbtn2.Bind( wx.EVT_BUTTON, self.create_sql )
        self.in1 = wx.xrc.XRCCTRL(self, 'in1')
        self.in2 = wx.xrc.XRCCTRL(self, 'in2')
        self.in3 = wx.xrc.XRCCTRL(self, 'in3')
        in2Items = self.createitemname()
        self.in2.SetItems(in2Items)
 
    def quit_button(self, e):
        self.parent.tableShow()
        self.Destroy()
 
    def create_sql( self, event ):
        acc_d = self.in1.GetValue()
        acc_date = acc_d.Format("%Y-%m-%d")
        combo = self.in2.GetValue()
        amount = self.in3.GetValue()
 
        c = db_sqlite(DB_NAME)
        sql = 'select item_code from item where item_name=?'
        item_code = c.db_execute_place(sql, (combo,)).fetchone()
 
        sql = 'insert into acc_data(acc_date, item_code, amount) values (?, ?, ?)'
        c.db_execute_place(sql, (acc_date, item_code['item_code'], amount))
        c.db_commit()
 
    def createitemname(self):
        li = []
        c = db_sqlite(DB_NAME)
        sql = 'select item_name from item'
        items = c.db_execute(sql).fetchall()
        for r in items:
            li.append(r['item_name'])
        return tuple(li)
 
def main():
    app = wx.App()
    ex = MyFrame(None)
    ex.Show()
    app.MainLoop()
 
if __name__ == '__main__':
    main()

dbSqliteClass.py

SQLite3のテーブル作成とPythonによるデータ操作 – Qiita
こちらのコードを使用しています。

# -----
# coding: utf-8
# https://qiita.com/ChibaDai/items/83e03aad49bb21b8bf12
# -----
import sqlite3
 
class db_sqlite():
    def __init__(self, sqlite_db_path):
        self.db_path = sqlite_db_path
        self.conn = None
        self.cursor = None
        self.db_cursor()
 
    def db_connect(self):
        if self.conn is not None:
            self.db_close()
        self.conn = sqlite3.connect(self.db_path)
        self.conn.row_factory = self.dict_factory
 
    def db_cursor(self):
        if self.conn is None:
            self.db_connect()
        if self.cursor is None:
            self.cursor = self.conn.cursor()
 
    def db_commit(self):
        self.conn.commit()
 
    def db_execute(self, exec_sql):
        # Insert a row of data
        # sample sql source
        # c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
        if self.cursor is None:
            self.db_cursor()
        return self.cursor.execute(exec_sql)
 
    def db_execute_place(self, exec_sql, data):
        if self.cursor is None:
            self.db_cursor()
        return self.cursor.execute(exec_sql, data)
 
    def db_execute_many(self, exec_sql, data):
        if self.cursor is None:
            self.db_cursor()
        return self.conn.executemany(exec_sql, data)
 
    def db_close(self):
        if self.conn is not None:
            self.conn.close()
        self.cursor = None
        self.conn = None
 
    def dict_factory(self, cursor, row):
        d = {}
        for idx, col in enumerate(cursor.description):
            d[col[0]] = row[idx]
        return d

XRC(kakeibo.xrc)

wxFormBuilderで書き出したもの。

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
	<object class="wxFrame" name="MyFrame">
		<style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style>
		<size>350,350</size>
		<title>家計簿アプリ</title>
		<centered>1</centered>
		<aui_managed>0</aui_managed>
		<object class="wxPanel" name="mainPanel">
			<style>wxTAB_TRAVERSAL</style>
			<object class="wxBoxSizer">
				<orient>wxVERTICAL</orient>
				<object class="sizeritem">
					<option>0</option>
					<flag>wxEXPAND</flag>
					<border>5</border>
					<object class="wxGridSizer">
						<rows>0</rows>
						<cols>2</cols>
						<vgap>0</vgap>
						<hgap>0</hgap>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxButton" name="btn1">
								<label>入力</label>
								<default>0</default>
								<markup>0</markup>
								<bitmap />
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL|wxALIGN_RIGHT</flag>
							<border>5</border>
							<object class="wxButton" name="btn2">
								<label>終了</label>
								<default>0</default>
								<markup>0</markup>
								<bitmap />
							</object>
						</object>
					</object>
				</object>
				<object class="sizeritem">
					<option>0</option>
					<flag>wxEXPAND</flag>
					<border>5</border>
					<object class="wxBoxSizer">
						<orient>wxVERTICAL</orient>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxEXPAND | wxALL</flag>
							<border>5</border>
							<object class="wxStaticLine" name="line1">
								<style>wxLI_HORIZONTAL</style>
							</object>
						</object>
					</object>
				</object>
				<object class="sizeritem">
					<option>0</option>
					<flag>wxEXPAND</flag>
					<border>5</border>
					<object class="wxBoxSizer">
						<orient>wxVERTICAL</orient>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL|wxALIGN_CENTER_HORIZONTAL</flag>
							<border>5</border>
							<object class="wxStaticText" name="st1">
								<font>
									<size>15</size>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>【表示画面】</label>
								<wrap>-1</wrap>
							</object>
						</object>
					</object>
				</object>
				<object class="sizeritem">
					<option>0</option>
					<flag>wxALIGN_CENTER_HORIZONTAL</flag>
					<border>5</border>
					<object class="wxBoxSizer">
						<orient>wxHORIZONTAL</orient>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
							<border>5</border>
							<object class="wxStaticText" name="st2">
								<label>期間</label>
								<wrap>-1</wrap>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxDatePickerCtrl" name="kikan1">
								<style>wxDP_DROPDOWN</style>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
							<border>5</border>
							<object class="wxStaticText" name="st3">
								<label>~</label>
								<wrap>-1</wrap>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxDatePickerCtrl" name="kikan2">
								<style>wxDP_DROPDOWN</style>
							</object>
						</object>
					</object>
				</object>
				<object class="sizeritem">
					<option>0</option>
					<flag>wxEXPAND</flag>
					<border>5</border>
					<object class="wxBoxSizer">
						<orient>wxVERTICAL</orient>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL|wxALIGN_CENTER_HORIZONTAL</flag>
							<border>5</border>
							<object class="wxButton" name="btn3">
								<label>表示</label>
								<default>0</default>
								<markup>0</markup>
								<bitmap />
							</object>
						</object>
					</object>
				</object>
				<object class="sizeritem">
					<option>0</option>
					<flag>wxEXPAND</flag>
					<border>5</border>
					<object class="wxBoxSizer">
						<orient>wxVERTICAL</orient>
						<object class="sizeritem">
							<option>1</option>
							<flag>wxALL|wxEXPAND</flag>
							<border>5</border>
							<object class="wxGrid" name="gridTable" />
						</object>
					</object>
				</object>
			</object>
		</object>
	</object>
	<object class="wxDialog" name="MyDialog">
		<style>wxDEFAULT_DIALOG_STYLE</style>
		<size>300,250</size>
		<title>入力画面</title>
		<centered>1</centered>
		<object class="wxBoxSizer">
			<orient>wxVERTICAL</orient>
			<object class="sizeritem">
				<option>1</option>
				<flag>wxEXPAND</flag>
				<border>5</border>
				<object class="wxPanel" name="mainPanel">
					<style>wxTAB_TRAVERSAL</style>
					<object class="wxBoxSizer">
						<orient>wxVERTICAL</orient>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxEXPAND</flag>
							<border>5</border>
							<object class="wxBoxSizer">
								<orient>wxVERTICAL</orient>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL|wxALIGN_RIGHT</flag>
									<border>5</border>
									<object class="wxButton" name="dbtn1">
										<label>終了</label>
										<default>0</default>
										<markup>0</markup>
										<bitmap />
									</object>
								</object>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxEXPAND</flag>
							<border>5</border>
							<object class="wxBoxSizer">
								<orient>wxVERTICAL</orient>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL|wxALIGN_CENTER_HORIZONTAL</flag>
									<border>5</border>
									<object class="wxStaticText" name="st1">
										<font>
											<size>14</size>
											<style>normal</style>
											<weight>normal</weight>
											<underlined>0</underlined>
										</font>
										<label>【入力画面】</label>
										<wrap>-1</wrap>
									</object>
								</object>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALIGN_CENTER_HORIZONTAL</flag>
							<border>5</border>
							<object class="wxFlexGridSizer">
								<rows>0</rows>
								<cols>2</cols>
								<vgap>0</vgap>
								<hgap>0</hgap>
								<growablecols></growablecols>
								<growablerows></growablerows>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
									<border>5</border>
									<object class="wxStaticText" name="st2">
										<label>日付</label>
										<wrap>-1</wrap>
									</object>
								</object>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL</flag>
									<border>5</border>
									<object class="wxDatePickerCtrl" name="in1">
										<style>wxDP_DROPDOWN</style>
									</object>
								</object>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
									<border>5</border>
									<object class="wxStaticText" name="st3">
										<label>内訳</label>
										<wrap>-1</wrap>
									</object>
								</object>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL</flag>
									<border>5</border>
									<object class="wxComboBox" name="in2">
										<size>150,-1</size>
										<value></value>
										<content />
									</object>
								</object>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
									<border>5</border>
									<object class="wxStaticText" name="st4">
										<label>金額</label>
										<wrap>-1</wrap>
									</object>
								</object>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL</flag>
									<border>5</border>
									<object class="wxTextCtrl" name="in3">
										<size>150,-1</size>
										<value></value>
									</object>
								</object>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALIGN_CENTER_HORIZONTAL</flag>
							<border>5</border>
							<object class="wxBoxSizer">
								<orient>wxVERTICAL</orient>
								<object class="sizeritem">
									<option>0</option>
									<flag>wxALL</flag>
									<border>5</border>
									<object class="wxButton" name="dbtn2">
										<label>登録</label>
										<default>0</default>
										<markup>0</markup>
										<bitmap />
									</object>
								</object>
							</object>
						</object>
					</object>
				</object>
			</object>
		</object>
	</object>
</resource>

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)