memopy

pythonで作ってみました的なブログ

⑪ コールバック関数の定義【python tkinter sqlite3で家計簿を作る】

⑪ コールバック関数の定義【python tkinter sqlite3で家計簿を作る】

今回は、「表示」ボタンが押下されたときのコールバック関数の定義をする。
この処理は、期間に入力された日付に応じてレコードを絞り込むものである。

前回までに作成したGUI
f:id:memopy:20170604103013p:plain

コールバック関数に必要な処理は

  1. Treeviewのアイテム(item)を全て削除する
  2. 期間に入力された値をSELECT文のWHERE句に組み込む
  3. 再度、Treeviewのアイテムを表示

となる。

Treeviewのアイテムを全て削除する

Treeviewのitemを全て削除するには、deleteメソッドを用いる。
deleteメソッドには、削除するアイテムを指定しなければならない。
この指定には、itemを指定しなければならない。
全てのitemを取得するには、tree.get_childern()を用いる。
tree.get_childern()はリストの値で返ってくるため、for文で全てのitemを削除する。

for i in tree.get_children()
    tree.delete(i)

また、結合(starred expression)の機能を用いて、シンプルに記述することができる。

tree.delete(*tree.get_children())

期間に入力された値をSELECT文のWHERE句に組み込む

SELECT文のWHERE句にBETWEEN演算子を用いて、開始日と終了日を指定する。

sql = """
SELECT acc_date,item_name,amount
FROM acc_data as a,item as i
WHERE a.item_code = i.item_code AND
acc_date BETWEEN '{}' AND '{}'
ORDER BY acc_date
""".format(start,end)

ただし、開始日と終了日が空欄だった場合エラーになるので、デフォルト値を指定する必要がある。

if start == "":
    start = "1900/01/01"
if end == "":
    end = "2100/01/01"
sql = """
SELECT acc_date,item_name,amount
FROM acc_data as a,item as i
WHERE a.item_code = i.item_code AND
acc_date BETWEEN '{}' AND '{}'
ORDER BY acc_date
""".format(start,end)

最後にこのSQLを発行して、Treeviewのアイテムに追加すればよい。
このコールバック関数をまとめると次のようになる。

# 表示ボタンが押されたときの処理
def select_sql(start,end):
    # treeviewのアイテムをすべて削除
    tree.delete(*tree.get_children())
    # 開始日と終了日が空欄だったらデフォルト値の設定
    if start == "":
        start = "1900/01/01"
    if end == "":
        end = "2100/01/01"
    #SELECT文の作成
    sql = """
    SELECT acc_date,item_name,amount
    FROM acc_data as a,item as i
    WHERE a.item_code = i.item_code AND
    acc_date BETWEEN '{}' AND '{}'
    ORDER BY acc_date
    """.format(start,end)
    # ツリービューにアイテムの追加
    i=0
    for r in c.execute(sql):
        # 金額(r[2])を通貨形式に変換
        r = (r[0],r[1],"¥{:,d}".format(r[2]))
        tree.insert("","end",tags=i,values=r)
        if i & 1:
            tree.tag_configure(i,background="#CCFFFF")
        i+=1    

処理をテストする。
f:id:memopy:20170604113554p:plain
それぞれ、期間を入力して、その期間に応じたレコードを表示させることができた!

以上のコールバック関数を組み込んだ表示画面GUIスクリプトは次のようになる。

# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.ttk as ttk
import sqlite3

# 表示ボタンが押されたときの処理
def select_sql(start,end):
    # treeviewのアイテムをすべて削除
    tree.delete(*tree.get_children())
    # 開始日と終了日が空欄だったらデフォルト値の設定
    if start == "":
        start = "1900/01/01"
    if end == "":
        end = "2100/01/01"
    #SELECT文の作成
    sql = """
    SELECT acc_date,item_name,amount
    FROM acc_data as a,item as i
    WHERE a.item_code = i.item_code AND
    acc_date BETWEEN '{}' AND '{}'
    ORDER BY acc_date
    """.format(start,end)
    # ツリービューにアイテムの追加
    i=0
    for r in c.execute(sql):
        # 金額(r[2])を通貨形式に変換
        r = (r[0],r[1],"¥{:,d}".format(r[2]))
        tree.insert("","end",tags=i,values=r)
        if i & 1:
            tree.tag_configure(i,background="#CCFFFF")
        i+=1    
    
# 空のデータベースを作成して接続する
dbname = "database.db"
c = sqlite3.connect(dbname)
c.execute("PRAGMA foreign_keys = 1")

# rootフレームの設定
root = tk.Tk()
root.title("家計簿アプリ")
root.geometry("400x500")

# メニューの設定
frame = tk.Frame(root,bd=2,relief="ridge")
frame.pack(fill="x")
button1 = tk.Button(frame,text="入力")
button1.pack(side="left")
button2 = tk.Button(frame,text="表示")
button2.pack(side="left")
button3 = tk.Button(frame,text="終了")
button3.pack(side="right")

# 入力画面ラベルの設定
label1 = tk.Label(root,text="【表示画面】",font=("",16),height=2)
label1.pack(fill="x")

# 期間選択のラベルエントリーの設定
frame1 = tk.Frame(root,pady=15)
frame1.pack()
label2 = tk.Label(frame1,font=("",14),text="期間 ")
label2.pack(side="left")
entry1 = tk.Entry(frame1,font=("",14),justify="center",width=12)
entry1.pack(side="left")
label3 = tk.Label(frame1,font=("",14),text=" ~ ")
label3.pack(side="left")
entry2 = tk.Entry(frame1,font=("",14),justify="center",width=12)
entry2.pack(side="left")

# 表示ボタンの設定
button4 = tk.Button(root,text="表示",
                    font=("",16),
                    width=10,bg="gray",
                    command=lambda:select_sql(entry1.get(),entry2.get()))
button4.pack()

# ツリービューの作成
tree = ttk.Treeview(root,padding=10)
tree["columns"] = (1,2,3)
tree["show"] = "headings"
tree.column(1,width=100)
tree.column(2,width=75)
tree.column(3,width=100)
tree.heading(1,text="日付")
tree.heading(2,text="内訳")
tree.heading(3,text="金額")

# ツリービューのスタイル変更
style = ttk.Style()
# TreeViewの全部に対して、フォントサイズの変更
style.configure("Treeview",font=("",12))
# TreeViewのHeading部分に対して、フォントサイズの変更と太字の設定
style.configure("Treeview.Heading",font=("",14,"bold"))

# SELECT文の作成
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
"""
# ツリービューにアイテムの追加
i=0
for r in c.execute(sql):
    # 金額(r[2])を通貨形式に変換
    r = (r[0],r[1],"¥{:,d}".format(r[2]))
    tree.insert("","end",tags=i,values=r)
    if i & 1:
        tree.tag_configure(i,background="#CCFFFF")
    i+=1
# ツリービューの配置
tree.pack(fill="x",padx=20,pady=20)

# メインループ
root.mainloop()

今回はここまで!
いよいよ次回が最終回。最後に画面遷移の処理を定義する。

質問や記事の誤りがありましたらコメントお願いします。

2017/6/4追記:sqlite3は、日付型(date型)というデータ型がない。日付として例えばBETWEEN演算子などの処理を認識させるためには、
'YYYY-MM-DD'
'YYYY-MM-DD hh:mm:ss'
の形式で記述しなければならない。
入力形式にYYYY/MM/DDと入力されていたら、replaceメソッドで'/'を'-'に置き換える処理が必要になる。

前の記事
⑩ 複数のウィジェットを配置【python tkinter sqlite3で家計簿を作る】
次の記事
⑫ 画面遷移の処理を定義(最終回)【python tkinter sqlite3で家計簿を作る】