適当のごった煮

Pythonと境界標とQGISを中心にいろいろと

tkinterでsqlite3のDBビューワーを作る

スポンサードリンク

必要な機能を考える

Pythonのsqlite3を使えば手軽にDBを作成できますが、PythonからDBの内容を確認するためは、接続、カーソル取得、SELECT文投入、結果確認という手順を踏まねばならず、やや手間に感じます。

SQLiteコマンドラインを使用して直接ファイルを開けば、ドットコマンドというSQLite特有の便利な機能が使えますが、Pythonとは別にSQLiteをインストールしなければなりません。

なんとかPythonだけで簡単にDBファイルを確認できるようにするため、tkinterを使ってGUIのDB確認ソフトを作成することにしました。必要な機能は以下の通りです。

  • DBファイル(*.db)を指定するとテーブル一覧が表示される
  • テーブルを選択すると、テーブル情報を表示する
  • 最新5件を表示する簡易ボタンをつける
  • SELECT文を書いて実行するボタンをつける

目次

テストDBに投入したデータはテストデータ・ジェネレータを利用させていただきました。

起動して「開く」ボタンでファイルを選択するとテーブル一覧を表示。

f:id:tekito-gottani:20160929195150j:plain

リストからテーブルを選択して「選択」ボタンを押すとテーブル定義が表示される。

「最新5件」でデータを表示。

f:id:tekito-gottani:20160929195301j:plain

SELECT文を投入(テストデータは合計500件投入)。

f:id:tekito-gottani:20160929195702j:plain

スクリプトソース

およそ100行です。SELECT文のたびにDBに接続、切断していたり、改善すべきところはかなりあると思いますが、個人で使う分にはこのくらいで十分なので、多少の不具合は目をつぶっています。

入力されたSQL文はノーチェックで通しているので、あくまでDBファイル確認用の個人ツールという扱いです。

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

import tkinter as tk
import tkinter.filedialog as filedialog
import sqlite3

class App(tk.Frame):
    def __init__(self, master = None):
        tk.Frame.__init__(self, master)
        self.pack()

        # ラベル
        self.lbl1 = tk.Label(self, text='対象DB')
        self.lbl1.grid(row=0, column=0)
        self.lbl2 = tk.Label(self, text='テーブルリスト')
        self.lbl2.grid(row=1, column=0)
        self.lbl3 = tk.Label(self, text='対象テーブル')
        self.lbl3.grid(row=2, column=0)
        self.lbl4 = tk.Label(self, text='SELECT文')
        self.lbl4.grid(row=3, column=0)
        
        # ファイル選択用ボタンと選択されたファイル名表示エントリー
        self.btn1 = tk.Button(self, text='開く', command = self.btn_open_file)
        self.btn1.grid(row=0, column=2)
        self.ent1 = tk.Entry(self, width=50)
        self.ent1.grid(row=0, column=1)
        
        # テーブル名表示用リストボックスとテーブル選択ボタン
        self.lstb = tk.Listbox(self, height=7, width=50)
        self.lstb.grid(row=1, column=1)
        self.btn2 = tk.Button(self, text='選択', command = self.btn_select_tbl)
        self.btn2.grid(row=1, column=2)

        # 選択テーブル表示エントリーとselect文投入用エントリー
        self.ent2 = tk.Entry(self, width=50)
        self.ent2.grid(row=2, column=1)
        self.ent3 = tk.Entry(self, width=50)
        self.ent3.grid(row=3, column=1)

        # select文実行、最新10件ボタンと結果表示用テキストエリア
        self.btn3 = tk.Button(self, text='実行', command = self.btn_select_exe)
        self.btn3.grid(row=3, column=2)
        self.btn3 = tk.Button(self, text='最新5件', command = self.btn_latest5)
        self.btn3.grid(row=3, column=3)
        self.text = tk.Text(self)
        self.text.grid(row=4, column=0, columnspan=4)

    def btn_open_file(self):
        # .dbファイル選択
        f_name = filedialog.askopenfilename()
        if f_name[-3:] != '.db':
            print('.dbファイルを選んでください。')
        else:
            # 既存エントリーを削除して選択したファイル名を表示
            self.ent1.delete(0, 50)
            self.ent1.insert(0, f_name)

            # dbファイル内のテーブルを取得してリストボックスに表示
            con = sqlite3.connect(f_name)
            cur = con.cursor()
            cur.execute("SELECT tbl_name FROM sqlite_master \
                        WHERE type = 'table'")
            result = cur.fetchall()
            con.close()
            
            self.lstb.delete(0, self.lstb.size()) # リストボックスをカラにする
            for tbl_name in result:
                self.lstb.insert(0, list(tbl_name))

    # テーブル選択
    def btn_select_tbl(self):
        # 既存エントリー削除
        self.ent2.delete(0, 50) # 50文字を超えるテーブル名は削除されない
        self.text.delete('1.0', '100.100') # 同上

        # テーブル名選択、表示
        tbl_selected = self.lstb.get(self.lstb.curselection())
        self.ent2.insert(0, tbl_selected)
        
        # テーブル定義表示
        con = sqlite3.connect(self.ent1.get())
        cur = con.cursor()
        cur.execute("SELECT sql FROM sqlite_master \
                    WHERE type = 'table' AND tbl_name = (?)", tbl_selected)
        result = str(cur.fetchall()[0]) # cur.fetchallの返り値はタプルのリスト
        self.text.insert('1.0', result.replace(',', ',\n'))
        con.close()

        # 既存エントリーを削除して、select文の候補表示
        self.ent3.delete(0, 50)
        select = 'SELECT * FROM ' + tbl_selected[0]
        self.ent3.insert(0, select)
        
    # select文実行
    def btn_select_exe(self):
        con = sqlite3.connect(self.ent1.get())
        cur = con.cursor()
        cur.execute(self.ent3.get())
        result = cur.fetchall()
        
        result_text = str(len(result)) + '個selectされました.\n'
        self.text.delete('1.0', '100.100')
        self.text.insert('1.0', result_text)
        i=1
        for line in result:
            num = str(i+1) + '.0'
            line = str(i)+' : '+str(line).replace("('", "").replace("',)", "\n")
            self.text.insert(num, line)
            i += 1
        con.close()

    # 最新5件表示
    def btn_latest5(self):
        tbl_selected = self.lstb.get(self.lstb.curselection())
        con = sqlite3.connect(self.ent1.get())
        cur = con.cursor()
        select = 'select * from ' + tbl_selected[0] + ' order by ROWID desc limit 5'
        cur.execute(select)
        result = cur.fetchall()

        # 既存を削除して結果表示
        self.text.delete('1.0', '100.100')
        for line in result:
            line = str(line) + '\n\n'
            self.text.insert('1.0', line)
        con.close()

app = App()
app.mainloop()