【Python tkinter】データベースsqlite3をGUIアプリと連携させる

2020.05.05 /

【Python tkinter】データベースsqlite3をGUIアプリと連携させる

PythonTkinterでGUIアプリケーションを作成するとき、データベースと連携させる場合があります。

データベースと連携させることにより、様々な機能を含んだアプリケーションを作成できます。

今回は数あるDBRMSの中から、sqlite3を使ったアプリケーション構築方法を解説していきます。

以下リンク(tkinterチュートリアル)で作成しているデスクトップアプリケーションとsqlite3を連携させます。

sqlite3とは

sqlite3はPythonの標準モジュールです。
そのためモジュールをインポートして、データベースに接続するだけで使えるようになります。

sqlite3で使用するデータベースは、その名の通りSQLiteです。
SQLiteはオープンソースのRDBMSです。

特徴としてWebサーバーを介せずに使用できるため、サーバープロセスを起動させることなく利用が可能です。

アプリケーションに組み込んで使用されるという、RDBMSとしては珍しい特徴を持っています。

sqlite3のデータベースを操作するためにはSQLという言語を使用します。
このSQLを使ってデータベースの追加やデータの取得などの操作を行います。

またsqlite3はMySQLなどのRDBMSと比べると機能面は乏しいですが、スピードが非常に速いので、大量のデータを扱うことが得意です。

IT用語の確認

RDBMS(Relational DataBase Management System)とは、リレーショナルデータベース(RDB)を管理するためのソフトウェアです。簡単に言うと、データベースを楽に操作するためのソフトです。

大規模なデータベースにはSQLiteは向いていないので、そういった場合はMySQLを使うのがお勧めです。
SQLiteは小規模のデータベースシステムで使うようにしましょう。

またSQLiteはファイル形式のため、セキュリティ機能は一切ないです。
データを守るためのセキュリティ対策は自身で行う必要があります。

sqlite3のデータ型

sqlite3のデータ型は以下のようになります。

データ型 説明
INTEGER 整数型
REAL 小数型
TEXT 文字列型
NULL NULL

追加するsqlite3のソースコード

「OUTLOOKから添付ファイルを保存するアプリ」に下記sqlite3を使用するためのコードを追加します。

import sqlite3

# データベースに接続または作成
dbname = "outlook.db"
connection = sqlite3.connect(dbname)
# カーソルの取得
c = connection.cursor()
# SQL文の実行
c.execute('CREATE TABLE IF NOT EXISTS default_folder (folder varchar(1024))')
c.execute('SELECT * FROM default_folder')
# 新しくDBを作成した場合、テーブルを作成する
if not c.fetchone():
    folder = 'C:/'
    c.execute("""
            INSERT INTO default_folder values ('{}');
    """.format(folder))
# データベースから値・文字列を取得
c.execute('SELECT * FROM default_folder')
default_folder = c.fetchone()
# データベースに変更を保存
connection.commit()
# データベースを閉じる
connection.close()

上記コードについて詳しく説明していきます。

sqlite3の使用手順

sqlite3を使用するためには、下記の流れを守る必要があります。

sqlite3手順

1: sqlite3をインポート

2: データベースに接続または作成

3: カーソルの取得

4: SQL文の実行

5: コミット

6: データベースとの接続切断

sqlite3の使用方法

sqlite3モジュールのインポート

import sqlite3

sqlite3はPythonの標準モジュールなので、使うためにインストールなどは必要なく、プログラムの頭にsqlite3をインポートするだけで使用できます。

sqlite3の接続・作成

# データベースに接続または作成
dbname = "outlook.db"
connection = sqlite3.connect(dbname)

データベースに接続またはデータベースの作成には,sqlite3.connect()メソッドを使用しConnectionオブジェクトを作成します。

Connectionオブジェクト作成時に、引数で渡されたデータベースがなければ自動で新しいデータベースを作成し、存在すれば接続を行います。

また引数に":memory:"を使うとRAM上にデータベースを作ることができます。

カーソルの取得

c = connection.cursor()

ConnectionオブジェクトからCursorオブジェクトを作成します。

これによりSQLを使ってデータベースに対して様々な命令を行えるようになります。

SQL文の実行

c.execute('CREATE TABLE IF NOT EXISTS default_folder (folder varchar(1024))')
c.execute('SELECT * FROM default_folder')

CursorオブジェクトのexecuteメソッドでSQL文を実行できます。

上記コードの1行目では、データベース内に対象テーブルがなかった場合、CREATE文でテーブルを作成しています。

つまり初めてこのプログラムを起動し、データベースが作成されたときのみテーブルが作成されることを意味します。

初回起動時にテーブルにデータを追加する

if not c.fetchone():
    folder = 'C:/'
    c.execute("""
            INSERT INTO default_folder values ('{}');
    """.format(folder))

初回起動時(テーブルが作成されたとき)はテーブルになにもデータが入っていないので、データを挿入しています。

if not c.fetchone()により、事前にselect文で取得していたデータの有無を確認しています。

データがなければ、Insert文でCドライブのパスをデータベースに挿入しています。

データベースから値・文字列を取得

c.execute('SELECT * FROM default_folder')
default_folder = c.fetchone()

SELECT文で取得した値をfetchoneメソッドで変数に格納しています。

データベースから取得する値が複数の場合は、fetchallメソッドで取得します。

fetchoneメソッドやfetchallメソッドを実行し、レコードが何もない場合はNoneが返されます。

データベースに変更を保存

connection.commit()

データベースの変更を行ったら必ずコミットcommit()を行う必要があります。

コミットをせずにデータベースを閉じると、変更は保存されないので注意してください。

データベースを閉じる

connection.close()

データベースの作業を行いましたら、最後に必ずデータベースコネクションをcloseメソッドを使って閉じます。

あとはデータベースから取得した値を使いたい場所に入れ込みます。

entry_folder.insert(0, default_folder[0])

これによりアプリ起動時にデータベースから取得した値を初期値としてフォルダー名に入力されます。

Tkinter:GUI画面に初期値をDBから入力

まとめ

今回までで作成したアプリケーションの全コードを以下に記します。

# -*- coding: utf-8 -*-
"""
Created on Thu Apr  2 17:27:21 2020

@author: admin
"""


import win32com.client
import datetime
import os
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog
from tkinter import messagebox
import os.path
import sqlite3

# データベースに接続または作成
dbname = "outlook.db"
connection = sqlite3.connect(dbname)
# カーソルの取得
c = connection.cursor()
# SQL文の実行
c.execute('CREATE TABLE IF NOT EXISTS default_folder (folder varchar(1024))')
c.execute('SELECT * FROM default_folder')
# 新しくDBを作成した場合、テーブルを作成する
if not c.fetchone():
    folder = 'C:/'
    c.execute("""
            INSERT INTO default_folder values ('{}');
    """.format(folder))
# データベースから値・文字列を取得
c.execute('SELECT * FROM default_folder')
default_folder = c.fetchone()
# データベースに変更を保存
connection.commit()
# データベースを閉じる
connection.close()

# 保存先フォルダーを指定し、entry_folderにセットする
def ask_folder():
    fld = filedialog.askdirectory()
    entry_folder.delete(0, tk.END)
    entry_folder.insert(tk.END, fld)

def click_execute():
    # Outlookからメール情報を取得
    outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
    if not entry_outlook.get():
        # 受信フォルダのメールを取得
        folders = outlook.GetDefaultFolder(6)
    else:
        # 指定されたメールフォルダのメールを取得
        folders = outlook.GetDefaultFolder(6).Folders[entry_outlook.get()]
    # メールフォルダ内のメールにアクセス
    mails=folders.Items

    #添付ファイルの保管用フォルダ作成
    newfolder_path = entry_folder.get() + '/Outlook_test'
    # 作成フォルダが存在するかの確認
    if os.path.isdir(newfolder_path) == False:
        # フォルダがなければ作成する
        os.makedirs(newfolder_path)

    # 絞り込み日程の取得
    startdate = datetime.datetime.strptime(entry_start.get(), '%Y/%m/%d')
    enddate = datetime.datetime.strptime(entry_end.get(), '%Y/%m/%d')


    #メール情報を1件1件取得し、添付ファイルがあれば保存する
    for mail in mails:
        RT=mail.ReceivedTime
        period = datetime.datetime(RT.year ,RT.month, RT.day, RT.hour, RT.minute, RT.second)
        if startdate <= period <= enddate:
            if mail.Attachments.Count > 0:
                myDate = RT.strftime("%Y/%m/%d")
                myDate = myDate.replace("/","-")
                datefolder_path = newfolder_path + '\\' + myDate
                if os.path.isdir(datefolder_path) == False:
                    os.makedirs(datefolder_path)
                for myAttachment in mail.Attachments:
                    folder_path = datefolder_path + '\\' + myAttachment.FileName
                    if os.path.isfile(folder_path) == False:
                        myAttachment.SaveAsFile(folder_path)
    messagebox.showinfo("完了","添付ファイルをダウンロードしました")
# メインウィンドウの設定
root = tk.Tk()
root.title("OUTLOOK操作")
root.geometry("600x300")

### menubarの作成と設置 ###
# menubarの大元(コンテナ)の作成と設置
menubar = tk.Menu(root)
root.config(menu=menubar)

# menubarを親として設定メニューを作成と表示
setting_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='設定', menu=setting_menu)

# menubarを親としてヘルプメニューを作成と表示
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='ヘルプ', menu=help_menu)

# 設定メニューにプルダウンメニューを追加
setting_menu.add_command(label='環境設定')
setting_menu.add_command(label='終了')

# ヘルプメニューにプルダウンメニューを追加
help_menu.add_command(label='FAQ')

### menubarの終了 ###

# メインフレームの作成と設置
frame = ttk.Frame(root)
frame.grid(column=0, row=0, sticky=tk.NSEW, padx=5, pady=10)

# 各種ウィジェットの作成
label_start = ttk.Label(frame, text="集計開始日(yyyy/mm/dd):")
entry_start = ttk.Entry(frame)
label_end = ttk.Label(frame, text="集計終了日(yyyy/mm/dd):")
entry_end = ttk.Entry(frame)
label_folder = ttk.Label(frame, text="出力先フォルダ:")
entry_folder = ttk.Entry(frame, width=50)
button_folder = ttk.Button(frame, text="参照", command=ask_folder)
label_outlook = ttk.Label(frame, text="OUTLOOK受信フォルダ名:")
entry_outlook = ttk.Entry(frame)
button_execute = ttk.Button(frame, text="実行", command=click_execute)

# 各種ウィジェットの設置
label_start.grid(row=0, column=0, padx=10, pady=2)
entry_start.grid(row=0, column=1, sticky=tk.W)
label_end.grid(row=1, column=0, padx=10, pady=2)
entry_end.grid(row=1, column=1, sticky=tk.W)
label_folder.grid(row=2, column=0, padx=10,pady=2, sticky=tk.E)
entry_folder.grid(row=2, column=1,sticky=tk.W)
button_folder.grid(row=2, column=2, sticky=tk.E)
label_outlook.grid(row=3, column=0, padx=10, pady=2, sticky=tk.E)
entry_outlook.grid(row=3, column=1, sticky=tk.W)
button_execute.grid(row=4, column=1, pady=10)

# 初期値の入力
dt_now = datetime.datetime.now()
dt_now = dt_now.strftime("%Y/%m/%d")
entry_end.insert(tk.END, dt_now)
entry_folder.insert(0, default_folder[0])

root.mainloop()