⑪ コールバック関数の定義【python tkinter sqlite3で家計簿を作る】
⑪ コールバック関数の定義【python tkinter sqlite3で家計簿を作る】
今回は、「表示」ボタンが押下されたときのコールバック関数の定義をする。
この処理は、期間に入力された日付に応じてレコードを絞り込むものである。
前回までに作成したGUI
コールバック関数に必要な処理は
- Treeviewのアイテム(item)を全て削除する
- 期間に入力された値をSELECT文のWHERE句に組み込む
- 再度、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
処理をテストする。
それぞれ、期間を入力して、その期間に応じたレコードを表示させることができた!
以上のコールバック関数を組み込んだ表示画面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で家計簿を作る】
⑩ 複数のウィジェットを配置【python tkinter sqlite3で家計簿を作る】
⑩ 複数のウィジェットを配置【python tkinter sqlite3で家計簿を作る】
表示画面設計図
前回までに作成したGUI
今までの機能を用いて、表示画面のGUIを作成した。
また、TreeviewのHeading部のフォントを変更する方法については別ページにまとめた。
ttk Treeview Headingのfontを変更する方法 - memopy
# -*- coding: utf-8 -*- import tkinter as tk import tkinter.ttk as ttk import sqlite3 # 空のデータベースを作成して接続する 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()
表示画面のGUIが完成した!
次は、表示画面のGUIに処理を実装する。
質問や記事の誤りがありましたらコメントお願いします。
前の記事
⑨ 表(テーブル)の作成【python tkinter sqlite3で家計簿を作る】
次の記事
⑪ コールバック関数の定義【python tkinter sqlite3で家計簿を作る】
ttk Treeview Headingのfontを変更する方法
ttk Treeview Headingのfontを変更する方法
ツリービュー ヘッダーのフォントを変更するのに苦労したのでまとめる。
styleクラスを使用すると、ウィジェットの全てのクラスに対して一括でオプションを設定できる。
import tkinter as ttk style = ttk.Style() # configure(オプションを一括で適用するウィジェットのクラス名,オプション・・・) # ※今回は、フォントサイズのみ14に変更する例 style.configure("Treeview.Heading",font=("",14))
ウィジェットのクラス名指定の例
フォントサイズを14に変更するクラスの範囲を指定する例
# 全てのウィジェット style.configure(".",font=("",14)) # Treeviewの全部 style.configure("Treeview",font=("",14)) # TreeviewのHeading部分のみ style.configure("Treeview.Heading",font=("",14))
import tkinter as tk import tkinter.ttk as ttk # ルート画面の作成 root = tk.Tk() # スタイルの設定 style = ttk.Style() # TreeViewの全部に対して、フォントサイズの変更 style.configure("Treeview",font=("",12)) # TreeViewのHeading部分に対して、フォントサイズの変更と太字の設定 style.configure("Treeview.Heading",font=("",14,"bold")) # ツリービュー(表)の作成 tree = ttk.Treeview(root) # 各列に対してインデックスを付ける tree["columns"]=(1,2,3) # ツリービューの非表示 tree["show"]="headings" # 各列に対する設定( tree.column(1, width=100) tree.column(2, width=100) tree.column(3, width=100) # 各列のヘッダーの設定 tree.heading(1, text="日付") tree.heading(2, text="内訳") tree.heading(3, text="金額") # レコードの挿入 tree.insert("" ,"end",values=("2017/5/1","食費",3500)) tree.pack() root.mainloop()
⑨ 表(テーブル)の作成【python tkinter sqlite3で家計簿を作る】
⑨ 表(テーブル)の作成【python tkinter sqlite3で家計簿を作る】
前回までに登録画面のGUI実装が完了した。
今回は表示画面のGUI作成を行う。始めに、表(テーブル)の作成だ。
表(テーブル)の作成は、ttkモジュールのツリービュー(Treeview)ウィジェットを用いる。
表示画面設計図
ツリービュー Treeview
はじめに簡単なサンプルスクリプトを作成して、ウィジェットの機能を確認する。
# モジュールのインポート import tkinter as tk import tkinter.ttk as ttk # ルートフレームの作成 root = tk.Tk() # ツリービューの作成 tree = ttk.Treeview(root) # 列インデックスの作成 tree["columns"] = (1,2,3) # 表スタイルの設定(headingsはツリー形式ではない、通常の表形式) tree["show"] = "headings" # 各列の設定(インデックス,オプション(今回は幅を指定)) tree.column(1,width=75) tree.column(2,width=75) tree.column(3,width=100) # 各列のヘッダー設定(インデックス,テキスト) tree.heading(1,text="日付") tree.heading(2,text="内訳") tree.heading(3,text="金額") # レコードの作成 # 1番目の引数-配置場所(ツリー形式にしない表設定ではブランクとする) # 2番目の引数-end:表の配置順序を最下部に配置 # (行インデックス番号を指定することもできる) # 3番目の引数-values:レコードの値をタプルで指定する tree.insert("","end",values=("2017/5/1","食費",3500)) tree.insert("","end",values=("2017/5/10","光熱費",7800)) tree.insert("","end",values=("2017/5/10","住宅費",64000)) # ツリービューの配置 tree.pack() root.mainloop()
表が作成された。
表の要素に追加するためには、insertメソッドのvaluesオプションに要素をタプル型で渡してあげればよい。
データベースに登録している明細テーブル(acc_data)をSELECT文で抽出し、for文で追加するロジックを立てることになる。
始めにデータベースに登録しているレコードをSELECT文で取得してみる。
# -*- coding:cp932 -*- import sqlite3 c = sqlite3.connect("database.db") # 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 """ # SQLを発行し、各レコードをタプルで表示 for r in c.execute(sql): print(r)
データベースのacc_dataテーブルの各レコードがタプル型で抽出できることを確認した。
次に、先ほどのツリービューを、データベースから取得したレコードを要素として追加するロジックに変更する。
# モジュールのインポート import tkinter as tk import tkinter.ttk as ttk import sqlite3 # データベースの接続 c = sqlite3.connect("database.db") # 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 """ # ルートフレームの作成 root = tk.Tk() # ツリービューの作成 tree = ttk.Treeview(root) # 列インデックスの作成 tree["columns"] = (1,2,3) # 表スタイルの設定(headingsはツリー形式ではない、通常の表形式) tree["show"] = "headings" # 各列の設定(インデックス,オプション(今回は幅を指定)) tree.column(1,width=75) tree.column(2,width=75) tree.column(3,width=100) # 各列のヘッダー設定(インデックス,テキスト) tree.heading(1,text="日付") tree.heading(2,text="内訳") tree.heading(3,text="金額") # SELECT文で取得した各レコードを繰り返し取得 for r in c.execute(sql): # ツリービューの要素に追加 tree.insert("","end",values=r) # ツリービューの配置 tree.pack() root.mainloop()
データベースのテーブルをツリービューとして表示させた!
~おまけ~
Treeviewは罫線がなくて表が見づらい。各レコードを2色に設定すると見やすくなる。
上記のコード、for文の中を次のように変更した。
# SELECT文で取得した各レコードを繰り返し取得 i=0 for r in c.execute(sql): # ツリービューの要素に追加(tagを追加することにより、要素を識別できる) tree.insert("","end",tags=i,values=r) # tagが奇数か偶数か判定 if i & 1: # tagが奇数(レコードは偶数)の場合のみ、背景色の設定 tree.tag_configure(i,background="#CCFFFF") i+=1
今回はここまで!
質問や記事の誤りがありましたらコメントください。
関連記事
ttk Treeview Headingのfontを変更する方法 - memopy
前の記事
⑧ ドロップダウンリストの作成【python tkinter sqlite3で家計簿を作る】
次の記事
⑩ 複数のウィジェットを配置【python tkinter sqlite3で家計簿を作る】
⑧ ドロップダウンリストの作成【python tkinter sqlite3で家計簿を作る】
⑧ ドロップダウンリストの作成【python tkinter sqlite3で家計簿を作る】
今回は、ttkモジュールを使って、ドロップダウンリスト(コンボボックス)の作成をしてみる。
前回までに作成したGUI
「内訳」の値は内訳テーブル(item)に登録された値を用いるため、直接入力よりドロップダウンリストのほうがユーザーフレンドリーである。
また、明細テーブル(acc_data)への登録は、'食費'などのitem_nameではなく、数値のitem_codeを登録するため、ドロップダウンリストで選択した値に対応するitem_codeの値を求めなければならない。
ドロップダウンリストはTkinterの拡張モジュールであるTkinter.ttkモジュールを用いる。
ちなみに、ttkにおけるウィジェット名は、コンボボックスである。
コンボボックス Combobox
始めに簡単なサンプルスクリプトを記載する。
# モジュールのインポート import tkinter as tk import tkinter.ttk as ttk # ルートフレームの作成 root = tk.Tk() # コンボボックスの作成(rootに配置,リストの値を編集不可(readonly)に設定) combo = ttk.Combobox(root, state='readonly') # リストの値を設定 combo["values"] = ("食費","住宅費","光熱費") # デフォルトの値を食費(index=0)に設定 combo.current(0) # コンボボックスの配置 combo.pack() # ボタンの作成(コールバックコマンドには、コンボボックスの値を取得しprintする処理を定義) button = tk.Button(text="表示",command=lambda:print(combo.get())) # ボタンの配置 button.pack() root.mainloop()
コンボボックスが作成され、「表示」を押下すると、インタプリタコンソールにコンボボックスの値を出力したのが確認できる。
Combboxのオプション(抜粋)
オプション名 | 入力型式 | 説明 |
---|---|---|
justify | center,left(デフォルト),right | テキストの文字寄せ |
postcommand | コールバック関数 | リストが表示される直前の処理 |
state | normal(デフォルト) readonly disable |
リストの状態 normal:リストの編集可能 readonly:リストの編集不可 disable:非活性(選択不可) |
values | tuple | リストの要素 |
width | integer | コンボボックスの幅 |
Combboxのメソッド(抜粋)
# コンボボックスに値をセットする combo.set("string") # コンボボックスの値を読み取る combo.get() # コンボボックスの初期値を設定する combo.current(0) #index = 0に設定
⑦ GUIの実装 sqlite3への登録【python tkinter sqlite3で家計簿を作る】
⑦ GUIの実装 sqlite3への登録【python tkinter sqlite3で家計簿を作る】
今回から遂に、GUIの実装(クリックしたときの処理を定義)を行う。
最終的には、入力画面に各項目を入力し、「登録」を押下するとsqlite3のDBに登録(SQLを発行)するという処理を実装する。
前回までに作成したGUI
Buttonウィジェットのクリック時の処理を定義する
import tkinter as tk # ボタンがクリックされた時の処理をコールバック関数として定義 def callback(): print("Hello World") root = tk.Tk() # ボタンを作成(rootに配置、テキストの指定、ボタンがクリックされた時に呼び出す関数(コールバック関数)を指定) button = tk.Button(root,text="OK",command=callback) button.pack() root.mainloop()
OKボタンをクリックすると、インタプリタコンソールにHello Worldの文字が表示されたことがわかる。
引数があるコールバック関数を定義する場合
Buttonのcommandのコールバック関数に引数を用いる場合はラムダ形式で記述しなければならない。
import tkinter as tk # 引数があるコールバック関数を定義 def callback(name): print("Hello World " + name) root = tk.Tk() # ボタンを作成(commandをラムダ形式で記述) button = tk.Button(root,text="OK",command=lambda:callback("memopy")) button.pack() root.mainloop()
Entryウィジェットのテキストを読み取る
Entryウィジェットに入力されたテキストを読み取るためには、getメソッドを使う。
import tkinter as tk # GETボタンが押された時のコールバック関数 def text_get(): # Entryウィジェットのテキストを読み取るgetメソッド text = entry.get() print(text) root = tk.Tk() # Entryウィジェットの作成 entry = tk.Entry(root,width=10) entry.pack() # Buttonウィジェットの作成 button = tk.Button(root,text="GET",command=text_get) button.pack()
Entry内のテキストを読み取り、インタプリタコンソールに出力した。
⑥ LabelとEntryの配置【python tkinter sqlite3で家計簿を作る】
⑥ LabelとEntryの配置【python tkinter sqlite3で家計簿を作る】
今回はLabelとEntryウィジェットの配置をする。
設計図
前回までに作成したGUI
ラベル Label
# ラベルの作成(rootに配置,テキストの指定,フォントの指定) label1 = tk.Label(root,text="【入力画面】",font=16) label1.pack(fill="x")
ラベルが配置された。
Labelのオプション(抜粋)
オプション名 | 入力型式 | 説明 |
---|---|---|
anchor | center(デフォルト),n,ne,e,se,s,sw,w,nw | ラベル内における文字の配置場所 中心(center)からの方位角 |
background bg |
色 | 背景色 |
borderwidth bd |
integer | ラベルの枠幅 |
font | font_object | 文字のフォント |
foreground fg |
string | 文字色 |
height | integer | ラベルの縦の長さ |
justify | center(デフォルト),left,right | 複数行のテキストの文字寄せ |
relief | flat(デフォルト),sunken,raised,groove,ridge | ラベル枠の形状 ラベル枠の形状を有効にするためには、必ずborderwidthの値を1以上に設定しなければならない |
text | string | ラベルに表示させる文字 |
underline | boolean | 下線を引く場合はTrue,付けない場合はFalse(デフォルト) |
width | integer | ボタンの横の長さ |
wraplength | integer | ラベルを複数行に表示した場合における行間 |
エントリー Entry
# エントリーの作成(rootに配置,フォントの指定,文字の中央揃え,Entry横幅の指定) entry1 = tk.Entry(root,font=("",12),justify="center",width=15) entry1.pack()
1行の入力欄が作成された!
Entryのオプション(抜粋)
オプション名 | 入力型式 | 説明 |
---|---|---|
background bg |
色 | 背景色 |
borderwidth bd |
integer | エントリーの枠幅 |
font | font_object | 文字のフォント |
foreground fg |
string | 文字色 |
justify | center,left(デフォルト),right | テキストの文字寄せ |
relief | flat,sunken(デフォルト),raised,groove,ridge | エントリー枠の形状 |
width | integer | エントリーの横の長さ |