SQLite3からデータを読み込んで、ウィンドウ内にその内容を表示する

最初に、プログラム全体をご覧ください。。

#!/usr/bin/env python
#coding=utf-8
#SQLite3からデータを読み込んで、ウィンドウ内にその内容を表示する

from Tkinter import *
import sqlite3

class MyApp:
    def __init__(self, parent):

        #フレーム・ウィジット
        self.myContainer1 = Frame(parent)
        self.myContainer1.pack()

        #テキスト・ウィジット
        self.myText = Text(self.myContainer1)
        self.myText.pack()

        #ボタン・ウィジット
        self.myQuitBt = Button(self.myContainer1)
        self.myQuitBt.configure(text = u"終了",
                                relief = "flat",
                                command = self.myContainer1.quit)
        self.myQuitBt.pack(side=RIGHT)

#ルート・ウィジットTkとMyAppの生成
root = Tk()
root.title(u"文字表示 my_tk63_moji.py")
myapp = MyApp(root)

#SQLite の処理
#SQLite3のファイルを取り込み、データを表示する。
conn = sqlite3.connect("meibo.db3") #データベース接続
c = conn.cursor() #カーソル作成
sql = '''
select ipk_id, employee_num, name, birthday, strt_w_day from meibo;
'''
kotae = c.execute(sql)

myapp.myText.insert(INSERT, "社員番号     名   前    生年月日   入社年月日\n")
myapp.myText.insert(INSERT, "--------------------------------------------------\n")
for x in kotae:
    line = "%8s (%15s) %10s %10s" %(x[1],x[2],x[3],x[4])
    myapp.myText.insert(INSERT, line + str("\n"))
myapp.myText.insert(END, "...END")

root.mainloop()
						

上記プログラムは、予めSQLite3で作成したmeibo.db3ファイルの社員名簿を読み込み、 次にTkinterでウィンドウ表示を行い、そこにデータ表示をおこなうというものです。


Tkinter 基礎編


プログラムの上の方にTkinterのフレーム(Frame)を生成するクラス(class) を記述していますので、まずは、Tkinterの解説をすることにしましょう。

Pythonプログラムをコンソール上ではなく、モニター上にウィンドウでデータ表示をさせるには、 Tkinterモジュールが必要になります。ほかにも”wxなんとか”とか"pyQ〜”など色々あるのですが、 ボクはそれらを使えないので、説明はしません。正しくは、説明できません。

さて、pythonでパソコンのモニター上にウィンドウを表示する基本は、下記の通りです。

(例1)

from Tkinter import *

root = Tk()

container1 = Frame(root)
container1.pack()

button1 = Button(container1, text = "Hello")
button1.pack()

root.mainloop()
						

たった10行未満のプログラムでウィンドウを表示させることができます。

最初の行から解説しますと、from Tkinter import *  とtkinterからクラスやらなんやら使うものを全部(*)をインポートします。 このやり方はよくないので import Tkinter as Tk といったやり方でインポートしなさい、 と解説しているページもありますが、どちらでもかまいません。 要は from Tkinter import * で不都合が生じてきたら import Tkinter as Tk  にすれば良いだけのことですから。

次に root = Tk() でルート・ウィジットを作成。ルートとあるぐらいですので、 これは、一番根本のウィンドウ枠、みたいなニュアンスになるかと思います。 英語サイト解説には、

You should only create one root widget for each program, and it must be created before any other widgets.
http://effbot.org/tkinterbook/tkinter-hello-tkinter.htm

なんてふうに書かれています。「プログラムごとにひとつのルート・ウィジットを作成しなさい、 そして、それは他のウィジットを作成する前にしなければならない」ってな内容ですね。 で、ルート・ウィジット(root widget)を作成したあとにフレーム・ ウィジットをルートウィジットに配置します。このフレーム・ウィジットの中に、本来なら、 ボタンやらラベル、テキストボックスなどを放りこんでいくわけです。 イメージとしては下図のような感じです。

関係図

解りやすくしたつもりが、よけい分かり難くなってしまいました。


フレーム・ウィジットは Frame(親は誰?, オプション) という形で記述します。親というのはTk()です。ルート・ウィジットTk()にFrame()が入って、 親子関係に見えるため子Frameの「親は誰?」と尋ねる引数を一番目にとります。 親はTk()ですので、素直にFrame(root)と指定します(root = Tk())。

そして.pack()メソッドを適応します。 これはFrame自身の大きさなどを調整したりして視覚化するためのメソッドです (ですが、最後にmainloop()メソッドでTkinterイベント・ ループを行うまでウィンドウは現れません)。


大枠ができたので、Frameの中にボタンを配置しましょう。Buttonウィジットを使います。 これもフレーム同様 Button(親は誰?, オプション) という形式で指定していきます。 フレームの中にボタンを配置するので、親はFrameのcontainer1ということになりますね (container1 = Frame(root)なので)。つまり、Button(container1,) でフレームとボタンの親子関係を築きあげたわけです。さらにオプション部分に、text = “Hello”  とすることで、そのボタンに"Hello”という文字を表示させることができます (押せますが、何も起こりません。何も設定していませんので)。 これもフレーム同様、pack()メソッドで視覚生成しておきます。ウィジットを作ったらpack、 作ったらpack、といった感じです。


最後にmainloop()メソッドでループ化します。ループ化しないと画面に何も起こりませんので、 注意です。実際、実行して確認してください。


『クラスで作ってみる』


プログラムが大きくなる場合はクラスに纏め上げるほうがよいとされていますので、 クラス化の練習もしておきましょう。 Tkでウィンドウを表示させる基本は、

root = Tk()

フレームやらボタンやら・・・

root.mainloop()
						

ですので、フレームやらボタンやら...といった箇所をクラス化してまとめ上げます。

(例2)

from Tkinter import *

class MyApp:
    def __init__(self, parent):
        self.container1 = Frame(parent)
        self.container1.pack()

        self.label1 = Label(
                            self.container1,
                            text = "Welcome to the Rogmic World!",
                            bg = "yellow",
                            fg = "red"
                            )
        self.label1.pack()

        self.button1 = Button(
                              self.container1,
                              text = "Hello!",
                              background="red"
                              )
        self.button1.pack(side=LEFT)

root = Tk()

muapp = MyApp(root)

root.mainloop()
						

もしくはFrameクラスを踏襲(継承)するなら、

from Tkinter import *

class MyApp(Frame):
    def __init__(self, parent):
        container1 = Frame(parent)
        container1.pack()

        self.label1 = Label(
                            container1,
                            text = "Welcome to the Rogmic World!",
                            bg = "yellow",
                            fg = "red"
                            )
        self.label1.pack()

        self.button1 = Button(
                              container1,
                              text = "Hello!",
                              background="red"
                              )
        self.button1.pack(side=LEFT)

root = Tk()

muapp = MyApp(root)

root.mainloop()
						

さらに、これでも動きます。

from Tkinter import *

class MyApp: #この後に(Frame)をいれても動く
    def __init__(self, parent):
        container1 = Frame(parent)
        container1.pack()

        label1 = Label(
                            container1,
                            text = "Welcome to the Rogmic World!",
                            bg = "yellow",
                            fg = "red"
                            )
        label1.pack()

        button1 = Button(
                              container1,
                              text = "Hello!",
                              background="red"
                              )
        button1.pack(side=LEFT)

root = Tk()

muapp = MyApp(root)

root.mainloop()
						

要は「どう纏めあげようと動く」のです。ボクは最初、 これに躓きました。あるページでは
container1 = Frame(parent)
と記述してあり、他のページでは、
self.container1 = Frame(parent)
となっている。理屈もクソもありゃしない、のです。理屈がなけりゃ納得できない派の人間からすれば、 これは、まさしくパズル…もしくはメイズ(迷路)。 でもまあ、概念から考えればselfの表記はなくても良いということになるのでしょうが、 付けておかないと、プログラムによっては、後々不便なときもあります (def等によって、スコープが変わる場合、self.なしのオブジェクトを操作することはできません)。


一番それらしいのが、

class MyApp(Frame):
container1 = Frame(parent)
self.label1 = Label
						

といった書き方なのでしょうが、ボクは

class MyApp:
self.container1 = Frame(parent)
self.label1 = Label
						

と書くことが多いです(こちらがおすすめです。 理由は何回かTkinterプログラムを組んでいると分かってきます)。  class MyApp(object):とすれば新クラスの記入でpython3以降でも使えると思います。 試していないので、判りません。


例2には例1では触れていなかった内容が少し出てきているので、触れておきましょう。
Buttonウィジットの生成の際に 

Button(
       self.container1,
       text = "Hello!",
       background="red"
       )
						

と記述していますが、これは
Button(self.container1, text = "Hello!", background="red") 
と同じで、見やすくカッコ良く(?)しているだけのことです。横に長くなるのがイヤなんです。 そのかわり、縦に長くなりますが…。


内容的には 親は self.container1 で、text = "Hello!"  としてボタンに"Hello”という文字を表示させ、background="red" でボタンのバックグラウンドは赤色にせよ、といったオプションを設定しています。尚、 backgroundはbgと書いても構いません。


以上で、Tkinter基礎編は終了です。次回からはSQLite3の説明です。


SQLite3 基礎編


さてSQLなのですが、これはデータベースでして、詳しい解説はGoogleなどで”SQLとは” といったキーワードで検索すれば、いやというほどワンサカでてくるので、ここではしません。 そのかわり、基本操作について少々解説した後にPythonでの操作方法へと展開していきましょう。 ですがこの投げやり的な書き方ではあまりにも不丁寧。 少しだけボクなりにデータベースを説明しましょうか。長い話を短くして説明すれば、 データベースとは「要は色々なデータを詰め込める便利な入れ物。 しかも必要や状況に応じて収納物を様々な形にアレンジして閲覧できる機能まで持ちあわせていますので、 奥さん、これは買いですよ」的なグッズ、といったところでしょうか。


まず、データベースのファイル作りからです。と、その前に、起動方法ですね。 起動とファイル作成を一緒に行います。コンソール画面から、

sqlite3 meibo.db3

と打ち込めば、SQLite3が起動し、meibo.db3というファイルがすでにあればそれに接続、 なければ作成してくれます。拡張子の.db3ですが、別に.sqlite3でも.sqdbでもなんでもよく、 自分が覚えやすく、期間がある程度たったあとからでも何のファイルかわかるような拡張子にしておけばよいでしょう。


ポイント:
sqlite3 ファイル名

◎◎▲@◇●-desktop:~/workspace/sql_test/src$ sqlite3 meibo.db3
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>
						

コンソール画面で起動させるとこんな感じです↑


で、データベースですので、データを保持するテーブルが必要です。テーブル作成には、

create table meibo(ipk_id integer primary key, employee_num numeric, name text, birthday text, strt_w_day text);

でmeiboという名前のテーブルが出来上がります。


ポイント:
create table テーブル名(カラム名1 カラムの型、カラム名2 カラムの型、…);


ここでは、meiboというテーブルをつくり、その中身(カラム)はipk_id, employee_num, name, birthday です、と定義しています。 さらにそのカラム内容をカラム名の次に半角スペース後に記載することで定義しているわけです。 カラム名 カラムの型=employee_num numeric つまり、カラム employee_numはnumeric型です、 と定義しているのです(一応、従業員Noの意味でemployee_numとしました)。 SQL構文は始まりから ; までをひとつの文とみなしますので、 最後に ; を付けるのを忘れないようにしましょう。


イメージ的には下記のようなテーブルが作成されます。

  ipk_id   employee_num   Name   Birthday start_w_day
         
         

データ型について少々説明しますと、SQLite3にはNULL, INTEGER, REAL, TEXT, BLOB という型が用意されていまして、ホームページ(http://www.tuyudaku.net/sqlite/datatype.html) では次のように解説しています。

1.0 Storage Classes and Datatypes

Each value stored in an SQLite database (or manipulated by the database engine) has one of the following storage classes:

NULL
The value is a NULL value.
INTEGER
The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.
REAL
The value is a floating point value, stored as an 8-byte IEEE floating point number.
TEXT
The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
BLOB
The value is a blob of data, stored exactly as it was input.

平たく言えば、Null、数字のINTEGER、小数のREAL、テキスト文字、BLOBといった型がありますよ、 ということです。BLOBというのはピクチャなどのバイナリデータです。


データ・タイプを決めてカラムを作りましたので、次は各カラムにデータの入力(挿入)を行います。

insert into meibo(employee_num, name, birthday, strt_w_day) values(1, "Python Light", "1955-08-10", "2010-6-20");

直訳的な解説をしますと「meiboテーブル( )の各カラムに挿入せよ!値は( )の内容だ」 といった感じでしょうか。


ポイント:
insert into テーブル名(カラム1, カラム2) values(カラム1の値, カラム2の値);


上の構文で次々とデータを入れていきます。ここらへんは実際、SQLite を起動してテスト入力したほうが理解しやすいと思います。で、ある程度、入力が完了したら、 その中身を見たくなるというのが、人情。そんな時のコマンドが、

select * from meibo;

になります。* は全部という意味です。「meiboテーブルからの全部のデータをセレクトしてくれ」 と要求しているわけです。その他に、例えばプライマリー・インテジャー・キー3 に対応するデータを参照したい場合は、

select * from meibo where ipk_id = 3;

といった指定で値を参照することができます。 「meiboテーブルからipkid=3 のデータを全部セレクトしなさい」という意味ですね。 カラムの値を見たい場合は、select name from maibo ってな具合。 「meiboテーブルから、nameカラムのデータをセレクトしなさい」

sqlite> .explain
sqlite> select * from meibo;
ipk_  employee_num   name  birt  strt
----  -------------  ----  ----  ----
1     1              Python Light  1955-08-10  2010-6-20
2     34             Thomas Joel  2211-07-10  2235-08-08
3     3              Terumasa Hino  2189-08-03  2209-03-15
4     12             Bill Evans  2200-04-01  2220-12-25
5     116            Hiroki Go  1980-02-24  1999-08-08
sqlite>
						
sqlite>  select * from meibo where ipk_id=3;
ipk_  employee_num   name  birt  strt
----  -------------  ----  ----  ----
3     3              Terumasa Hino  2189-08-03  2209-03-15
sqlite>
						
sqlite> select name from meibo;
name
----
Python Light
Thomas Joel
Terumasa Hino
Bill Evans
Hiroki Go
						

このようにコンソール上で確認するのが基本ですが、実はFirefoxブラウザの add onにSQLite Managerというものがあり、中身の確認でしたら、 それを使ったほうが手っ取り早いです。確認だけではなく新規作成からクエリーの実行と、 ほぼ何でもできてしまいます。


ですが、GUIベースソフトを使用してしまうとSQL文の勉強になりませんし、今回、 解説したコマンドはプログラムを組むときに、少し形を変えて利用できるものですので説明したわけです、 はい。


SQLiteの基本は以上です。次回からはPythonで、SQLiteを操作していきます。


PythonでSQLiteを操作する


さて、いよいよ今回からPythonでウィンドウを表示してその中にSQLite のデータを出力するプログラムを作成していきます。最初の方に説明したTkinter のことなどかなり忘れてしまっていることでしょうから、 おさらいをしながら少しづつプログラムを組んでいきましょう。


まずはプログラムに使用するモジュールのインポートからです。

From Tkinter import *
import SQLite3
						

Tkinterの基本は、

root = Tk()
root.mainloop()
						

でした。この root = Tk() と root.mainloop()  との間に細々としたものを置いていけば良いのです。で、間に置くのがmyapp=MyApp()になります。 つまり、

root = Tk()
myapp=MyApp(root)
root.mainloop()
						

さらに、MyAppの中にあるテキスト・ウィジット(self.myText)の中にSQLのデータを表示させる。 それをメインループするので、

root = Tk()
myapp=MyApp(root)
	テキスト・ウィジットに関連付けるSQLのの制御文
root.mainloop()
						

といった形を作ります。それではMyAppクラスを作成しましょう。

class MyApp:
    def __init__(self, parent):

        #フレーム・ウィジット
        self.myContainer1 = Frame(parent)
        self.myContainer1.pack()

        #テキスト・ウィジット
        self.myText = Text(self.myContainer1)
        self.myText.pack()

        #ボタン・ウィジット
        self.myQuitBt = Button(self.myContainer1)
        self.myQuitBt.configure(text = u"終了",
                                relief = "flat",
                                command = self.myContainer1.quit)
        self.myQuitBt.pack(side=RIGHT)
						

MyAppクラスは親を必要としますので、__init__ は(self, parent)となります。 Frameが親を受け取る際の引数がparentとなるわけです。よって、ここから

  self.myContainer1 = Frame(parent)

が導きだされます。 Frameの親はparentということです。


そして、すぐにパック。このフレームの中にテキスト・ウィジットとボタン・ウィジットが入りますので、

  self.myText = Text(self.myContainer1) self.myQuitBt = Button(self.myContainer1)

と記述していきます。テキスト・ウィジットというのは、テキストを数行表示するためのウィジットです
(The Text widget is used to display text in multiple lines.※1)。


ボタンに関してはconfigureで詳細設定をします。text=u”終了”でボタンに終了という文字を表示させ、 reliefでボタンの形状を指定、commandで、ボタンが押されて離されたらウィンドウを閉じる、 という設定をします。そして、これらをパック。パック内容は side = RIGHT で右側に配置するということです。 ボタンの形状を指定するreliefには、他に SUNKEN、RAISED、GROOVE,RIDGE などがあります。


ウィンドウを作ったので、つぎはSQLの処理を記述していきます。

#SQLite の処理
#SQLite3のファイルを取り込み、データを表示する。
conn = sqlite3.connect("meibo.db3") #データベース接続
c = conn.cursor() #カーソル作成
sql = '''
select ipk_id, employee_num, name, birthday, strt_w_day from meibo;
'''
kotae = c.execute(sql)

myapp.myText.insert(INSERT, "社員番号     名   前    生年月日   入社年月日\n")
myapp.myText.insert(INSERT, "--------------------------------------------------\n")
for x in kotae:
    line = "%8s (%15s) %10s %10s" %(x[1],x[2],x[3],x[4])
    myapp.myText.insert(INSERT, line + str("\n"))
myapp.myText.insert(END, "...END")
						

SQLの基本処理は、

  • SQLに接続
  • SQLを操作するカーソル作成
  • カーソルでSQLを操作
  • コミット
  • SQLを閉じる

といった感じになります。これらの操作を可能にするのsqlite3モジュールです。 このモジュールを使用するために import sqlite3 とモジュールのインポートを最初に行います。


接続はsqlite3モジュールのconnect()でデータベースとのコネクションをまず作成します。 このコネクション(オブジェクト)を操作するためにcursorが必要になります。 命令はカーソルを通して行われることになります。カーソルというのはコンソール画面でピコピコと点滅している  | ですね。 | の後にコマンドを入力して処理を実行させる。といったイメージそのものの処理です。


まとめると下記のとおりです。

conn = sqlite3.connect(“ データベース名 ”)  データベースへの接続
csr = conn.crsor() データベース操作のためのカーソル作成
csr.execute() カーソルを通して命令を実行

例えば、SQLの select * from ▲●▲テーブル; という文を実行したい場合は、


csr.execute(“elect * from ▲●▲テーブル;”)


となります。


それから、この実行内容を保持するために変数 kotae に渡し、挿入メソッド(insert() ) でテキスト・ウィジット上にそれを表示させるといった具合になります。 先にMyAppをクラス化しているので、myapp.myText.insert(INSERT, "  ") といった渡し方ができるわけです。


For 文の処理の説明をしましょう。select ipk_id, employee_num, name, birthday, strt_w_day from meibo;で入力されているデータを全行取り出すことになります。 このデータはリストに入ったタプルで帰ってきます。次のような感じです。


[(4, murakami,1985/12/03, 2005/03/20),(12, tanaka, 1980/3/3, 2000/05/05)]

個々のタプルデータを取り出すためにインデックス処理をしています。 他の方法としては、

for x in kotae:
	for data in x:
		myapp.myText.insert(INSERT, data + str("\n"))
						

といったデータの取り出しも可能です。取り出したデータをinsertして一連の表示処理は完了。 最後にmainloop()で、プログラム完成です。

※ 今回はデータを参照するだけなので使用していませんが、 作成やデータを入力などといった一連の処理を実行した後にはcommit()する必要があります。


※1 http://www.tutorialspoint.com/python/python_gui_programming.htm