6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

python tkinter,sqlite3でポケモン図鑑

Last updated at Posted at 2018-07-17

##教材元
https://www.udemy.com/the-python-mega-course/learn/v4/content

##tkinterとsqlite3の練習に簡易ポケモン図鑑作った。
frontend.pyとbackend.pyの2つのファイルを作って、frontend.pyでbackedn.pyをimportする。
appとexeファイルにも変換した。
ポケモンのjsonファイル元:
https://github.com/kotofurumiya/pokemon_data

##backend.py
sqlite3でdatabaseを作る。jsonファイルがロードされてない場合は、ロードしてポケモン図鑑のデータを登録する

#backend.py
#trati特性の思考
#多分trait枠2つつくり、trait1,trait2のボックスどちらに入力しても、dbのtrait12どちらかに存在すればTrueにすればいい。
import sqlite3
import json

def connect():
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()#, evolve TEXT, type1 TEXT, type2 TEXT, trait TEXT, hidden TEXT, hp INTEGER, atk INTEGER, def INTEGER, spa INTEGER, spd INTEGER, spe INTEGER)"
    sql_statement="CREATE TABLE IF NOT EXISTS pokemon (id INTEGER PRIMARY KEY, no TEXT, name TEXT, evolve TEXT, type1 TEXT, type2 TEXT)"
    cur.execute(sql_statement)
    conn.commit()
    conn.close()

#ファイルを選択させて、jsonの絶対パスを受け取る。
#そのパスを使ってloadできる?
def load_json():
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()
    sql_statement="INSERT INTO pokemon VALUES (Null,?,?,?,?,?)"
    with open("pokemon_data.json") as data:
        cur.execute("Select * from pokemon")
        raws = cur.fetchall()
        if len(raws)==0:
            for i in json.load(data):
                sql_tuple=(str(i["no"]),i["name"],"True" if i["evolutions"] else "False",i["types"][0],i["types"][1])
                cur.execute(sql_statement, sql_tuple)
    conn.commit()
    conn.close()

def view():
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()
    cur.execute("Select * from pokemon")
    raws = cur.fetchall()
    conn.close()
    return raws
    
def insert(no, name, evolve, type1, type2):
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()
    sql_statement="insert into pokemon values (Null,?,?,?,?,?)"
    cur.execute(sql_statement, (no,name,evolve, type1, type2))
    conn.commit()
    conn.close()

#type1のサーチどうするんだ?
#type1のみならそれだけでいいか
def search(no="", name="", evolve="", type1="", type2=""):
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()
    sql_statement="select * from pokemon where no=? or name=? or evolve=? or (type1=? and type2=?)"
    if type1 or type2:
        #typeが2つため:2回実行
        cur.execute(sql_statement, (no,name,evolve,type1,type2))
        rows = cur.fetchall()
        cur.execute(sql_statement, (no,name,evolve,type2,type1))
        rows += cur.fetchall()
        conn.close()
        return rows
    cur.execute(sql_statement, (no,name,evolve,type1,type2))
    rows = cur.fetchall()
    conn.close()
    return rows

def update(id, no, name, evolve, type1, type2):
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()
    sql_statement="update pokemon set no=?, name=?, evolve=?, type1=?,type2=? where id=?"
    cur.execute(sql_statement, (no,name,evolve, type1, type2, id))
    conn.commit()
    conn.close()
    
def delete(id):
    conn=sqlite3.connect("pokemon.db")
    cur=conn.cursor()
    sql_statement="delete from pokemon where id=?"
    cur.execute(sql_statement, (id,))
    conn.commit()
    conn.close()
    
#importされたときに自動で下記も実行される。
connect()
load_json()

  1. 最初にデータベースにテーブルを作り、カラムにデータを登録する際には指定されたデータ型である必要がある。(ただsqlite3はfrontend側から受け取った値が仮にint型でも、テーブル内部のcolumnがTEXT型だろうと、登録してくれる)
  2. bool値のデータ型がsqlite3にはないっぽい?
  3. cur.fetchall()はリストが返ってくる。リストの中の要素には検索されたrowがtupleで入ってる。
  4. windowが開いたときにデータベースに接続して、windowを閉じるときにデータベースとの接続を切るようにしたほうがコードの量は減るよね。これ。半分くらいに多分なる。

##frontend.py


#frontend.py

from tkinter import *
import backend

#command func
#wrap function。引数を渡せないため、wrapの中で実行させる

#<<ListboxSelect>>を受け取る。eventに入る
def get_selected_row(event):
    global selected_tuple
    try:
        index=list1.curselection()[0]
        selected_tuple=list1.get(index)
        #Entryは0,ENDなのか。Textは"1.0"か
        e1.delete(0,END)
        e2.delete(0,END)
        e3.delete(0,END)
        e4.delete(0,END)
        e5.delete(0,END)
        e1.insert(END, selected_tuple[1])
        e2.insert(END, selected_tuple[2])
        e3.insert(END, selected_tuple[3])
        e4.insert(END, selected_tuple[4])
        e5.insert(END, selected_tuple[5])
    except IndexError:
        pass
    

def view_command():
    list1.delete(0, END)
    for row in view():
        list1.insert(END, row)#(場所, value)
        #rowがendに追加される
        
def search_command():
    list1.delete(0,END)
    #これ空白だとNone渡されちゃうのか?するとちょっと不都合だな…
    for row in search(no.get(),name.get(),evolve.get(),type1.get(),type2.get()):
        list1.insert(END, row)

def add_command():
    insert(no.get(),name.get(),evolve.get(),type1.get(),type2.get())
    list1.delete(0, END)
    list1.insert(END, (no.get(),name.get(),evolve.get(),type1.get(),type2.get()))
    
#リストボックス内部の選択したもののidを取得する
def delete_command():
    delete(selected_tuple[0])
    view_command()
    
def update_command():
    update(selected_tuple[0],no.get(),name.get(),evolve.get(),type1.get(),type2.get())
    search_command()
    
def reset_command():
    e1.delete(0,END)
    e2.delete(0,END)
    e3.delete(0,END)
    e4.delete(0,END)
    e5.delete(0,END)
    
    
#window
window = Tk()

#title
window.wm_title("POKeMON Guide 861s")

#l = Label(window, text="")
l1 = Label(window, text="No")
l2 = Label(window, text="Name")
l3 = Label(window, text="Evolve")
l4 = Label(window, text="Type1")
l5 = Label(window, text="Type2")
#grid(row=0,column=0)
l1.grid(row=0,column=0)
l2.grid(row=1,column=0)
l3.grid(row=2,column=0)
l4.grid(row=3,column=0)
l5.grid(row=4,column=0)
#Entry
no=StringVar()
name=StringVar()
evolve=StringVar()
type1=StringVar()
type2=StringVar()
#Entry(window, textvariable=, width=10)
e1=Entry(window, textvariable=no, width=10)
e2=Entry(window, textvariable=name, width=10)
e3=Entry(window, textvariable=evolve, width=10)
e4=Entry(window, textvariable=type1, width=10)
e5=Entry(window, textvariable=type2, width=10)
#gird
e1.grid(row=0,column=1)
e2.grid(row=1,column=1)
e3.grid(row=2,column=1)
e4.grid(row=3,column=1)
e5.grid(row=4,column=1)
#listbox of output
list1 = Listbox(window, height=10, width=50)
list1.grid(row=0, column=2, rowspan=5)
#scrollbar
sb1 = Scrollbar(window)
sb1.grid(row=0, column=3, rowspan=5)
#scrollbar connect with listbox, y軸スクロール
list1.configure(yscrollcommand=sb1.set)
#y軸view will change
sb1.configure(command=list1.yview)

#event, bind(event, func)で→func(event)の形で実行される
list1.bind("<<ListboxSelect>>", get_selected_row)

#Button(window, text="", width=12, command=None)
b1=Button(window, text="View all", width=12, command=view_command)
b2=Button(window, text="Search entry", width=12, command=search_command)
b3=Button(window, text="Add entry", width=12, command=add_command)
b4=Button(window, text="Update", width=12, command=update_command)
b5=Button(window, text="Delete", width=12, command=delete_command)
b6=Button(window, text="Reset entry", width=12, command=reset_command)
b7=Button(window, text="Close", width=12, command=exit)
#grid(row=, column=3)
b1.grid(row=0, column=4)
b2.grid(row=1, column=4)
b3.grid(row=2, column=4)
b4.grid(row=3, column=4)
b5.grid(row=4, column=4)
b6.grid(row=5, column=4)
b7.grid(row=6, column=4)

window.mainloop()

#皮つくった
#sqlite3文つくった
#ボタン用funcを作る
#リストボックスへの選択イベントを関数と紐付け

'''
        print(list1.curselection())
        print(list1.curselection()[0])
        print(list1.get)
        print(selected_tuple)
(5,)#
5
<bound method Listbox.get of <tkinter.Listbox object .!listbox>>
(6, '5', 'リザード', 'True', 'ほのお', '')
'''
  1. list1.configure(yscrollcommand=sb1.set)はListboxのインスタンスlist1とScrollBarのインスタンスsb1を紐づけた。
  2. sb1.configure(command=list1.yview)でスクロールバーを動かすと、listboxのy軸(縦方向)の見るものが変わる。configureは全てのウィジェット(Text,Listbox,Entryなど)に共通で存在。動きを紐つけることができる
  3. Buttonを押したときの動作はcommand=funcのfuncの部分に関数名を指定する。しかし、この際引数を指定できない。そのため引数な必要な場合、実際に実行した関数を別の関数で包んであげる(wrap_functionと呼ばれるっぽい)。この包んだ別の関数をButtonのcommand=に渡してやればいい。
  4. bind(event, func)func(event)の形式で実行することができる。<<ListboxSelect>>はリストボックス内部で選択されたときのイベント。get_selected_row()の引数にこのイベントが渡される。参考元:https://python.keicode.com/advanced/tkinter-widget-listbox.php
  5. get_selected_row()は自分で定義したもの。渡されたeventを元に、
    list1.curselection()でリストボックス内部で選択されたrowのindex部分を含むtupleを返す。tupleなのでlist1.curselection()[0]で選択列のlistbox内部のindexを得られる。
  6. list1.get(index)で対象のrowに存在する値をtupleで受け取れる。list1はListBoxのインスタンス。list1.get()のみだとListbox内部の値全てが返ってくる。
  7. jupyterlabでこのコードを実行すると、window.destroyが働かない。.pyにしてterminalで動かせば普通に動く。

##おまけ スタンドアローン化する

pip install pyinstaller
pyinstaller --onefile --windowed frontend.py 

pyinstallerがない場合pip install pyinstallerする。
backend.pyとfrontend.pyを同じディレクトリにおく。同ディレクトリ上で上記コードを実行すれば、appを作れる。以外の簡単なんやね。exeファイルも作れる。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?