PythonでMySQLに接続(GUI-Tkinter編)

 PythonでMySQLに接続してデータを取得し、グラフィカルベース(GUI)でデータを表示する。GUIの作成には、Pythonの標準GUI作成ツールであるTkinterを使用する。
 使用したMySQLサーバーとリモートPCの諸元は次の通りである。 

MySQLサーバーリモートPC
PC:HP630 (Celeron T3500, 2.1GHz/2コア)
OS:Ubuntu MATE 18.04.6 64ビット
メモリー:6GB
システム媒体:SSD 64GB
IPアドレス:192 . 168 . (abc) . (xyz)
MySQL Community Server 5.7.41

PC:Mate ML-D (Intel Core i5 2400S 2.5GHz)
OS:Windows10 64ビット
メモリー:12GB
システム媒体:HDD 250GB
Python 3.9.13 64-bit
Anaconda3 2022.10 (Python 3.9.13 64-bit)
PyCharm Community Edition 2022.3.2

MySQLサーバー側の設定

 MySQL側の設定については、「PythonでMySQLに接続(CUI)」を参照のこと。

リモートPC側の設定(mysql-connector-pythonのインストール)

 PythonからMySQLに接続するためには、接続モジュールのインストールが必要となる。このサイトでは、MySQL開発元のOracle社が公式ライブラリとして公開している’mysql-connector-python’を使う。
 リモートPC側の設定については、「PythonでMySQLに接続(CUI)」を参照のこと。

実行結果のスナップショット

 先に実行結果のスナップショットを示す。
 画面のレイアウトは、以前紹介した「JavaからのDBアクセス(SWTによる表示)」と同様である。

◎ 指定した年号の一部の文字列に対して、3種類の検索条件を指定できる。

◎ 検索したい年号(元号)の一部に“義”を指定して、“義”を「任意の位置に含む」年号を検索する。
 そのような年号は存在しないので、データがない旨のダイアログが表示される。

◎ 大河ドラマ・アンコールで「篤姫」が再放送されている。幕末のキーワード:嘉永の“嘉”を
 「先頭に含む」年号を検索する。該当する年号のレコードが表形式で表示され、最下行の
 ステータスバーには、「該当するレコードが11件あった」ことが表示される。

ソースプログラム

 GUIプログラムの作成にはTkinterモジュールを使用する。TkinterはPythonの標準モジュールなのでインストールの必要はない。importすることですぐに利用が可能である。
 ソースプログラムと簡単な説明を以下に示す。なお、プログラムはIDLE、PyCharmのどちらのIDEからでも実行可能である。(各IDEの環境設定については、「PythonでMySQLに接続(CUI)」を参照のこと。)

import mysql.connector
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox

col_list = [''] * 5

def msg_box(msg):
    if msg == 'no_key':
        tk.messagebox.showinfo(title='インフォメーション',
                    message='検索する文字(列)や位置の\n'
                            + '指定を行ってください!!')
    elif msg == 'no_data':
        tk.messagebox.showinfo(title='インフォメーション',
                    message='該当するデータがありません!!')
    elif msg == 'err_msg':
        tk.messagebox.showerror(title='接続エラー',
                    message='データベースに接続できません!!\n'
                            + 'ネットワーク周りを確認してください')

def db_search():
    tree.delete(*tree.get_children())
    stat_text.set('')
    if not entry.get() or combobox.get() == '選択してください':
        msg_box('no_key')
    else:
        if combobox.get() == 'を先頭に含む':
            sqlKeyWord = "'" + entry.get() + "%'"
        elif combobox.get() == 'を任意の位置に含む':
            sqlKeyWord = "'%" + entry.get() + "%'"
        elif combobox.get() == 'を末尾に含む':
            sqlKeyWord = "'%" + entry.get() + "'"

        try:
            conn = mysql.connector.connect(
                host='192.168.*.***',
                user='myuser',
                password='********',
                database='gengo'
            )

            cursor = conn.cursor()

            # データを取得
            sql = 'select * from main where nengo like ' + sqlKeyWord  # SQL文を指定

            cursor.execute(sql)
            rows = cursor.fetchall()

            if not cursor.rowcount:
                # コネクションを閉じる
                cursor.close()
                conn.close()

                msg_box('no_data')
                return

            stat_text.set('該当するレコードは' + str(cursor.rowcount) + '件でした')

            # 要素の切り出しと表への埋め込み
            for row in range(cursor.rowcount):
                for col in range(5):
                    col_list[col] = rows[row][col + 1]
                    if col == 4:
                        tree.insert(parent='',
                                    index='end',
                                    values=(col_list))

            # コネクションを閉じる
            cursor.close()
            conn.close()

        except:
            msg_box('err_msg')
            return

# ★バグ対応用の関数 - Python 3.7.3以降で不具合
def fixed_map(option):
    return [elm for elm in style.map('Treeview.Heading', query_opt=option) if
            elm[:2] != ('!disabled', '!selected')]

# メインウィンドウの設定
root = tk.Tk()
root.resizable(False, False)
root.title('元号DBの検索')
root.geometry('395x400')

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

# menubarを親としてファイルメニュを作成・表示
set_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='ファイル', menu=set_menu)
# ファイルメニュにプルダウンメニュを追加
set_menu.add_command(label='終了', command = root.destroy)
### menubarの終了 ###

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

# StringVarのインスタンスを格納するウィジェット変数stat_text - ステータスバーに表示
stat_text = tk.StringVar(frame)
stat_text.set('')

# 各種ウィジェットの作成
label = ttk.Label(frame, text='検索したい年号(元号)の一部を指定してください')
entry = ttk.Entry(frame, width=18)
combobox = ttk.Combobox(frame, state='readonly', values=[
                                                        'を先頭に含む',
                                                        'を任意の位置に含む',
                                                        'を末尾に含む'])
combobox.set('選択してください')
button = ttk.Button(frame, text='検索', command=db_search)
# 列の識別名を指定
column = ('年号', 'よみ', '開始年', '終了年', '時代')
# Treeviewの生成
tree = ttk.Treeview(frame, padding=(5, 5, 5, 5), height=14, columns=column)
# 見出しのスタイルを指定
style = ttk.Style()
style.configure("Treeview.Heading", foreground="blue")
# ★バグ対応を処理 - Python 3.7.3以降で不具合
style.map('Treeview.Heading', foreground=fixed_map('foreground'))
# 列の設定
tree["columns"] = (1, 2, 3, 4, 5)
tree["show"] = "headings"
tree.column(1, anchor='e', width=60)
tree.column(2,anchor='e', width=110)
tree.column(3, anchor='e', width=55)
tree.column(4, anchor='e', width=55)
tree.column(5, anchor='e', width=60)
# 列の見出し設定
tree.heading(1, text='年号', anchor='e')
tree.heading(2, text='よみ', anchor='e')
tree.heading(3,text='開始年', anchor='e')
tree.heading(4, text='終了年', anchor='e')
tree.heading(5, text='時代', anchor='e')
# スクロールバーの追加
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=tree.yview)
tree["yscrollcommand"] = scrollbar.set
# Labelをステータスバーに使用
statusbar = ttk.Label(frame,
                        textvariable=stat_text,
                        font=('',12,'bold'),
                        foreground='DarkOrange',
                        relief=tk.GROOVE,
                        anchor='w')

# 各種ウィジェットの設置
label.grid(row=0, column=0, padx=10, stick=tk.W)
entry.grid(row=1, column=0, padx=12, sticky=tk.W)
combobox.grid(row=1, column=0, padx=(132, 0), sticky=tk.W)
button.grid(row=1, column=0, padx=(285,0), sticky=tk.W)
tree.grid(row=2, column=0, padx=(10,0), pady=(3,0), sticky=tk.W)
scrollbar.grid(row=2, column=1, padx=0, sticky=(tk.NS))
statusbar.grid(row=3, column=0, padx=(10,0), pady=(0,0), sticky=tk.EW)

root.mainloop()
  • 26~32行目
     sql文の検索条件部(like以下)の文字列を作成する。
  • 45行目
     select部と検索条件部をつないでsql文を作成する。
  • 61~67行目
     検索結果は、recordとfieldの二次元のリスト’rows[row][col]’に返ってくるので、1recordずつ各fieldを切り出してTreeviewウイジェットのinsert文で表に埋め込む。
     ここで、先頭の’id’ fieldは表示させないので、リストの第二要素から切り出す。(rows[row][col + 1])
  • 78~80、125行目
     Python3.7.3(正確には、TkinterモジュールのVersion8.6.9以降。)にはTkinterモジュールのTreeviewウイジェットの色表示に不具合があるので、これを回避する。
  • 144行目
     Tkinterにはステータスバーの機能がないので、Labelウイジェットで代用する。

参考URL

・ OFFICE54 Pythonの基本
・ CTC教育サービス ゼロから歩くPythonの道
・ SyachikuLOG PythonのTkinterのTree内の表データの文字色/背景色を指定②【バグへの対応】

inserted by FC2 system