エンジニアになりたい30代の学習備忘録

学習したプログラミングやマークアップについての備忘録

Pythonによる誕生日リストAppのソースコード

誕生日リストAppのソースコードですが、GitHubに公開はしたものの、なーんかソースコード載せるだけではダメな気がするし見てもらいにくい気もするし、せっかく前回Markdown記法が使った記事も書けたのでこっちでも公開してみます。

外部ライブラリは使ってないので画像さえ用意してもらえれば大丈夫なはず。

モジュール遷移図

f:id:horiuchi_koro:20210219130520p:plain

リセットボタン二つとキャンセルボタンについてはクリックイベントで済んでいるので「widget_create.py」に記述されています。

各モジュールのソースコード

長くなるので折り畳んでいます。

TOP画面

birthday_app.py

from tkinter import *
from tkinter import ttk
from image_file import ImageFile
from widget_create import WidgetCreate

class Application(ImageFile):
    def __init__(self, master, style=None):
        super().__init__()                
        #TOP画面を作成
        master.title('誕生日リスト')
        master.geometry('610x300+250+100')
        self.main_frame = Frame(master)
        self.main_frame.pack(pady=10)
        #画像
        self.image_file = ImageFile()
        self.cake_image = self.image_file.cake_img()
        self.image_label = Label(self.main_frame, image=self.cake_image)
        self.image_label.grid(column=0, rowspan=2, row=0, padx=20, pady=15, ipady=5)
        #Widgetの作成
        my_widget = WidgetCreate(belong=self.main_frame, style='MyWidgets4.TFrame')
        my_widget.button_create(master=root, my_style='ButtonStyle.TButton')

if __name__ == '__main__':
    root = Tk()
    s = ttk.Style()
    s.configure('MyWidgets.TFrame',background='systemTransparent', relief=FLAT)
    s.configure('MyWidgets4.TFrame',background='systemTransparent', relief=SUNKEN)
    s.configure('MyWidgets2.TLabel', background='systemTransparent', relief=FLAT)
    s.configure('ButtonStyle.TButton', foreground='#FF0000')

    app = Application(master=root, style='MyWidgets4.TFrame')

    root.mainloop()

ウィジェットの作成

widget_create.py

from tkinter import *
from tkinter import ttk
from image_file import ImageFile
from all_data import NewWindow
from select_db import OpenData
from sq_db import DbInto

class WidgetCreate(ImageFile):
    def __init__(self, belong=None, style=None):
        super().__init__()
        #Frame
        self.sub_frame = ttk.Frame(belong, style=style)
        self.sub_frame.grid(column=1, row=0, ipadx=15, ipady=10)
        self.name_frame = Frame(self.sub_frame)
        self.name_frame.pack(padx=10, pady=20, anchor=W)
        self.birth_frame = Frame(self.sub_frame)
        self.birth_frame.pack(padx=5, anchor=W)
        self.birth_frames = [
          Frame(self.birth_frame) for i in range(3)
        ]
        for item in self.birth_frames:
            item.pack(side=LEFT, padx=5)
        self.i_button_frame = Frame(belong)
        self.i_button_frame.grid(column=0, columnspan=2, row=3)
        self.r_button_frame = Frame(self.sub_frame)
        self.r_button_frame.pack()
        #Label
        self.info_label = Label(self.name_frame, text='★必要事項を入力してください★\n')
        self.info_label.pack(padx=5, anchor=W)
        self.name_lists = ['姓', '名']
        self.date_text = ['西暦', '月', '日']
        self.name_labels = [
          Label(self.name_frame, text=label_item) for label_item in self.name_lists
        ]
        #date_labelをそれぞれのFrameに入れて作成
        self.date_labels = [
          Label(frame_item, text=label_item) for frame_item, label_item in zip(self.birth_frames, self.date_text)
        ]
        #Entryを作成
        self.family_var = StringVar()
        self.first_var = StringVar()
        self.familyname_entry = ttk.Entry(self.name_frame, textvariable=self.family_var, width=10)
        self.firstname_entry = ttk.Entry(self.name_frame, textvariable=self.first_var, width=10)
        self.entry_list = [self.familyname_entry, self.firstname_entry]
        #姓名LabelとEntryをpack
        
        for label, entry in zip(self.name_labels, self.entry_list):
            label.pack(anchor = W, side = LEFT)
            entry.pack(anchor = W, side = LEFT)
        #Comboboxを作成
        self.date_list = [
          [value for value in range(1920, 2021)],
          [value for value in range(1, 13)],
          [value for value in range(1, 32)]
        ]
        self.combo_list = [
          ttk.Combobox(self.birth_frames[i], state='readonly', values=self.date_list[i], width = 5, justify = RIGHT) for i in range(3)
        ]
        #生年月日LabelとComboboxをpack
        for label, combo in zip(self.date_labels, self.combo_list):
            label.pack(anchor = W, side = LEFT)
            combo.pack(anchor = W, side = LEFT)
        
    #----ボタン関連の作成----
    def button_create(self, master, my_style=None):
        #登録ボタンイベント
        def i_button_click():
            #入力値を取得
            full_name = [name.get() for name in self.entry_list]
            birth_list = [var.get() for var in self.combo_list]
            result = DbInto(name_data=full_name, birth_data=birth_list)
            #入力内容のリセット
            for item in self.entry_list:
                item.delete(0,END)
            for item in self.combo_list:
                item.selection_clear()
                item.set('')

        #検索ボタンイベント
        def c_button_click():
            #画像を用意
            self.search_image = ImageFile()
            self.s_image = self.search_image.search_img()
            #入力値を取得
            full_name = [name.get() for name in self.entry_list]
            birth_list = [var.get() for var in self.combo_list]
            #生年月日条件(birth_list)をint型に変換
            for index, item in enumerate(birth_list):
                if item != '':
                    birth_list[index] = int(item)
            sum_item = full_name + birth_list
            #条件設定の有無で分岐
            if any(sum_item) is False:
                all_search = NewWindow(image=self.s_image)
            else:
                condition_data = OpenData(conditions=sum_item, image=self.s_image)
        #名前リセット
        def name_reset():
            for item in self.entry_list:
                item.delete(0,END)
        #生年月日リセット
        def date_reset():
            for item in self.combo_list:
                item.selection_clear()
                item.set('')
        #ボタンを作成
        self.i_button = ttk.Button(self.i_button_frame, text='新規登録', width=9, command=i_button_click)
        self.i_button.pack(side=LEFT)
        self.c_button = ttk.Button(self.i_button_frame, text='データ検索', width=9, command=c_button_click)
        self.c_button.pack(side=LEFT, padx=20)
        self.d_button = ttk.Button(self.i_button_frame, text='× キャンセル ×', style=my_style, width=11, command=master.destroy)
        self.d_button.pack(side=LEFT, padx=10)
        #余白調整のLabel
        self.space_label = Label(self.r_button_frame, text='')
        self.space_label.pack()
        #リセットボタン
        self.nr_button = ttk.Button(self.r_button_frame, text='Name Reset', command=name_reset)
        self.nr_button.pack(side=LEFT)
        self.dr_button = ttk.Button(self.r_button_frame, text='Date Reset', command=date_reset)
        self.dr_button.pack(padx=15, side=LEFT)

画像ファイルの準備

image_file.py

from tkinter import *
from tkinter import ttk

class ImageFile():
    def __init__(self):
        self.f = 'image_birthday/calender_woman.png'
        self.f2 = 'image_birthday/sweets_cake_pavlova.png'
        self.f3 = 'image_birthday/search_mushimegane2-2.png'
        
    def calender_img(self):
        self.calender_image = PhotoImage(file=self.f)
        return self.calender_image
        
    def cake_img(self):
        self.cake_image = PhotoImage(file=self.f2)
        return self.cake_image

    def search_img(self):
        self.search_image = PhotoImage(file=self.f3)
        return self.search_image

if __name__ == '__main__':
    ImageFile()

登録機能

sq_db.py

from tkinter import *
from tkinter import messagebox as mb
import sqlite3
import datetime as dt

class DbInto():
    def __init__(self, name_data, birth_data):
        full_name = name_data
        birth_list = birth_data
        if '' in full_name:
            mb.showwarning('空欄エラー', '姓名に空欄があります')
        else:
            try:
                birth_list = [int(i) for i in birth_data]
            except ValueError:
                mb.showerror('空欄エラー', '生年月日を入力してください')
            else:
                try:
                    birthday = dt.date(birth_list[0], birth_list[1], birth_list[2])
                except ValueError:
                    mb.showwarning('入力値エラー', f'{birth_list[1]}月は{birth_list[2]}日までありません')
                else:
                    birthday_info = (
                      '{}{}さんの生年月日を{}年{}月{}日で登録します。'.format(full_name[0], full_name[1], birth_list[0], birth_list[1], birth_list[2])
                      )
                    res = mb.askquestion('結果', birthday_info)
                    if res == 'yes':
                        into_data = [
                          full_name[0], full_name[1], birth_list[0], birth_list[1], birth_list[2]
                        ]
                        self.data_into_db(data=into_data)
                        mb.showinfo('完了', '★登録しました★')

    def data_into_db(self, data):
        #Connectionオブジェクトを作る。
        conn = sqlite3.connect('birthday.db')
        c = conn.cursor()
        #----データの追加----
        c.execute("INSERT INTO birthday VALUES(?, ?, ?, ?, ?)", data)
        #----追加終了----
        #保存する。
        conn.commit()
        #データベースをクローズする。
        conn.close()

データ表示のための新しいウィンドウ作成

toplevel_create.py

from tkinter import *
from tkinter import ttk

class TopLevelCreate():
    def __init__(self, image=None, title=None):
        #Toplevel
        self.my_win = Toplevel()
        self.my_win.title(title)
        self.my_win.geometry('400x400+300+120')
        self.my_win.resizable(0,1)
        #Flame
        self.image_frame = ttk.Frame(self.my_win, padding=15)
        self.image_frame.pack()
        #画像を用意
        self.search_label = Label(self.image_frame, image=image)
        self.search_label.pack()
        #メッセージ
        self.info_label = Label(self.image_frame, text='★データが10件以上の場合はスクロールして下さい★', pady=5, padx=5)
        self.info_label.pack()

全データ表示機能

all_data.py

from tkinter import *
from tkinter import ttk
import sqlite3
from toplevel_create import TopLevelCreate

#--tkinter部品作成--
class NewWindow(TopLevelCreate):
    def __init__(self, image=None):
        super().__init__(title='全登録データ', image=image)
        #画像を用意
        #--sqlite3の操作--
        conn = sqlite3.connect('birthday.db')
        c = conn.cursor()

        c.execute('SELECT * FROM birthday')
        r_tuple = tuple(c.fetchall())

        conn.close
        #----------------
        data_count = len(r_tuple)
        count_info = f'全データ:{data_count}件'
        c_info_label = Label(self.image_frame, text=count_info)
        c_info_label.pack(anchor=W)
        #----データを表示----
        data_frame = ttk.Frame(self.my_win, relief=SOLID, padding=15)
        data_frame.pack()

        final_data = []
        for index, item in enumerate(r_tuple):
            final_data.append(
              ' [{}]  {}{}:{}年{}月{}日生まれ'.format(
                    index+1, r_tuple[index][0], r_tuple[index][1], r_tuple[index][2], r_tuple[index][3], r_tuple[index][4])
            )

        #データ表示用Listbox&Scrollbar
        v = StringVar(value=final_data)
        list_box = Listbox(data_frame, listvariable=v, width=30)
        list_box.grid(row=0, column=0)
        scroll = ttk.Scrollbar(data_frame, orient=VERTICAL, command=list_box.yview)
        list_box['yscrollcommand'] = scroll.set
        scroll.grid(row=0, column=1, sticky=(N, S))

指定条件データ表示機能

select_db.py

from tkinter import *
from tkinter import ttk
from toplevel_create import TopLevelCreate
import sqlite3

#====検索機能モジュール====
class OpenData(TopLevelCreate):
    def __init__(self, conditions=None, image=None):
        super().__init__(title='指定検索データ', image=image)
        #条件データを受け取る
        full_conditions = conditions
        #条件の入っている列番号(インデックス)
        conditions_index = [index for index,item in enumerate(full_conditions) if item != '']
        #条件から空白を除外
        full_conditions = [elem for elem in full_conditions if elem != '']  
        #----sqlite3操作----
        conn = sqlite3.connect('birthday.db')
        c = conn.cursor()

        c.execute("SELECT * FROM birthday")
        all_data = c.fetchall()
        selection_data = []
        for item in all_data:
            select_result = True
            for key, value in enumerate(conditions_index):
                if item[value] != full_conditions[key]:
                    select_result = False
            if select_result == True:
                selection_data.append(item)
        conn.close
        #---------------------
        data_count = len(selection_data)
        count_info = f'条件一致データ:{data_count}件'
        c_info_label = Label(self.image_frame, text=count_info)
        c_info_label.pack(anchor=W)
        #----データを表示----
        data_frame = ttk.Frame(self.my_win, relief=SOLID, padding=15)
        data_frame.pack()
        final_data = []
        for index, item in enumerate(selection_data):
            final_data.append(
              ' [{}]  {}{}:{}年{}月{}日生まれ'.format(
                    index+1, selection_data[index][0], selection_data[index][1], selection_data[index][2], selection_data[index][3], selection_data[index][4])
            )
        #データ表示用Listbox&Scrollbar
        v = StringVar(value=final_data)
        list_box = Listbox(data_frame, listvariable=v, width=30)
        list_box.grid(row=0, column=0)
        scroll = ttk.Scrollbar(data_frame, orient=VERTICAL, command=list_box.yview)
        list_box['yscrollcommand'] = scroll.set
        scroll.grid(row=0, column=1, sticky=(N, S))

以上です。現時点での私の技量での最良ですが、何かご指摘がありましたらお願い致します。