「日経ソフトウェア2022年11月号 特集3 Python デスクトップアプリを作ろう」をやってみた その3

日経ソフトウェア2022年11月号 特集3 Python デスクトップアプリを作ろう」を実行してみました。

 

サンプルプログラム

サンプルプログラムは日経ソフトウェアのWebからダウンロードできます。

以下URLから「本誌バックナンバーを見る」→「2022年11月号」→「「特集3 Pythonでデスクトップアプリを作ろう後編」(t32211.zip)

 

 

Part3 「蔵書管理アプリ」を作る

データベースを利用した「蔵書管理アプリ」を作ります。

 

TreeViewウィジェットの使い方

TreeViewウィジェットの使い方を学びます。

内容は以下の通りです。

・ttk.TreeView関数でTreeViewウィジェットを作成する

・bind関数で行が選択されたときに実行する関数を設定する

・heading関数で表の見出しを設定する

・column関数で列を設定する

・insert関数で行にデータを追加する

 

プログラム例です。

view_select関数はイベントが発生したらslct_itemsの[0]列名のデータをメッセージボックスで表示する関数です。

list_view.selection()で行のすべての情報を取得し、slct_items変数に代入しています。

list_view.bind('<<TreeviewSelect>>', view_selectは、 その行が選択されたとき(eventが発生したとき)に、view_selectを実行されます。

disp関数でウインドウにリストを全て表示します。

 

import tkinter as tk
from tkinter import ttk
from tkinter import messagebox

def view_select(event):
  slct_items = list_view.selection()
  messagebox.showinfo('メッセージ', slct_items[0])

# ウインドウの作成
root = tk.Tk()
root.geometry('400x150')
root.title('Treeviewのテスト')
root.resizable(0, 0)

column = ('Title', 'Author', 'Publisher')

# Treeviewの作成
list_view = ttk.Treeview(root, columns=column, height=5)
list_view.bind('<<TreeviewSelect>>', view_select)

# 表の見出し
list_view.heading('#0', text='')
list_view.heading('Title', text='タイトル', anchor='center')
list_view.heading('Author', text='著者', anchor='center')
list_view.heading('Publisher', text='出版社', anchor='center')

# 列の設定
list_view.column('#0', width=0, stretch='no')
list_view.column('Title', anchor='w', width=200)
list_view.column('Author', anchor='w', width=80)
list_view.column('Publisher', anchor='w', width=80)

# Treeviewにデータを追加
def disp():
  for i in range(5):
    list_view.insert(parent='', index='end', iid=i,
    values = (str(i), str(i), str(i)))

disp()

list_view.pack()

root.mainloop()

 

実行結果

Treeviewselect

Menuウィジェットの使い方を学びます。

内容は以下の通りです。

・ウィンドウにMenu関数でメニューバーを作る

・メニューバーにMenu関数とadd_cascade関数でメニューを作る

・メニューにadd_command関数で項目を作り、その項目が選択されたときに実行する関数を設定する

 

プログラム例です。

import tkinter as tk
from tkinter import messagebox

def save():
  messagebox.showinfo('メッセージ', 'save')

def delete():
  messagebox.showinfo('メッセージ', 'delete')

# ウインドウの作成
root = tk.Tk()
root.geometry('300x150')
root.title('Menuのテスト')

# メニューバーを作成
menubar = tk.Menu()
root.config(menu=menubar)
# 操作メニューを作成
menu_command = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='操作', menu=menu_command)
# 操作メニューに「新規登録」「削除」メニューを追加する
menu_command.add_command(label='新規登録', command=save)
menu_command.add_separator()
menu_command.add_command(label='削除', command=delete)

root.mainloop()

 

実行結果

操作から新規登録を選択するとメッセージボックスが起動しました。

Menu Test

 

 

データベースを作成する

データベースを作成します。

データベースの名前は「collection.db」です。

CREATE TABLEでbooksテーブルを作成します。

 

CREATE TABLE books(
               id INTEGER PRIMARY KEY AUTOINCREMENT,
               isbn TEXT,
               title TEXT NOT NULL,
               author TEXT,
               publisher TEXT,
               release TEXT,
               price INTEGER,
               image TEXT)

 

プログラム例です。

import sqlite3

conn = sqlite3.connect('collection.db')
cur = conn.cursor()
cur.execute("""CREATE TABLE books(
               id INTEGER PRIMARY KEY AUTOINCREMENT,
               isbn TEXT,
               title TEXT NOT NULL,
               author TEXT,
               publisher TEXT,
               release TEXT,
               price INTEGER,
               image TEXT)""")

# 入力データ
books = [['0123-A1234','ビジネスPython超入門', \
          '中島 省吾', '日経BP','2019年6月10日',2640,'img_001.png'],
         ['B07HMN68L9','5日間で学ぶPython AIプログラミング編', \
          '中島 省吾','日経BP','2018年9月25日',880,'img_002.png']]

sql= """INSERT INTO books(isbn,
                        title,
                        author,
                        publisher,
                        release,price,
                        image) VALUES (?, ?, ?, ?, ?, ?, ?)"""

for i in range(len(books)):
    cur.execute(sql, books[i])
conn.commit()

for row in cur.execute("SELECT * FROM books"):
  print(row)

conn.close()

 

実行結果

collection.dbファイルが実行フォルダに作成されました。

 

蔵書管理アプリの完成

 

プログラム例です。

import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import sqlite3

books_list =
in_entry =   # 登録情報入力用のEntry
sub_win = None
s = ('ISBN', 'タイトル', '著者', '出版社', '発売日',
     '価格(円)', '画像ファイル')

# 書籍情報をデータベースから取得する関数
def get_books():
  conn = sqlite3.connect('collection.db')
  cur = conn.cursor()
  sql = 'SELECT * FROM books'
  cur.execute(sql)
  books_list = cur.fetchall()
  conn.close()
  return books_list

# 一覧の書籍を選択すると呼び出される関数
def view_select(event):
  global img
  global d_frame
  slct_items = list_view.selection()
  s = ('ISBN', 'タイトル', '著者', '出版社', '発売日',
       '価格(円)')
  for i in range(len(s)):
    item_view.item(i, values=(s[i],
      books_list[int(slct_items[0])][i+1]))
  try:
    img  = \
      tk.PhotoImage(file=books_list[int(slct_items[0])][7])
  except tk.TclError:
    img = ''
  panel = tk.Label(d_frame, image=img)
  panel.grid(row=0, column=1)
   
# 登録ボタンをクリックすると呼び出される関数
def register():
  global sub_win
  global in_entry
  conn = sqlite3.connect('collection.db')
  cur = conn.cursor()
  sql = """INSERT INTO books(isbn,
                             title,
                             author,
                             publisher,
                             release,
                             price,
                             image)
                             VALUES(?, ?, ?, ?, ?, ?, ?)"""
  ele =
  for i in range(len(in_entry)):
    ele.append(in_entry[i].get())
  cur.execute(sql, ele)
  conn.commit()
  conn.close()
  messagebox.showinfo('メッセージ', '新規登録しました。')
  sub_win.destroy()
  disp()

# 新規登録メニューがクリックされると呼び出される関数
def save():
  global sub_win
  global in_entry
 
  # 書籍情報入力用ウインドウの作成
  sub_win = tk.Toplevel()
  sub_win.geometry("250x250")
  in_label =
  in_entry.clear()
  for i in range(len(s)):
    in_label.append(tk.Label(sub_win, text=s[i]))
    in_entry.append(tk.Entry(sub_win, width=20))
  register_button = tk.Button(sub_win, text='登録',
                              command=register)
  for i in range(3):
    sub_win.columnconfigure(i, weight=1)
  for i in range(9):
    sub_win.rowconfigure(i, weight=1)
  for i in range(7):
    in_label[i].grid(column=0, row=i)
    in_entry[i].grid(column=1, row=i, columnspan=2)
  register_button.grid(column=1, row=8, rowspan=2,
                       sticky=tk.N)

# 削除メニューがクリックされると呼び出される関数
def delete():
  conn = sqlite3.connect('collection.db')
  cur = conn.cursor()
  sql = 'DELETE FROM books WHERE id = ?'
  slct_items = list_view.selection()
  cur.execute(sql, (books_list[int(slct_items[0])][0],))
  conn.commit()
  conn.close()
  messagebox.showinfo('メッセージ',
                      'データを削除しました。')
  disp()

# ウインドウの作成
root = tk.Tk()
root.geometry('400x300')
root.title('蔵書管理アプリ')
root.resizable(0, 0)
root.grid_rowconfigure(0, weight=1)
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

# メニューバーを作成
menubar = tk.Menu()
root.config(menu=menubar)
# 操作メニューを作成
menu_command = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='操作', menu=menu_command)
# 操作メニューに「新規登録」「削除」メニューを追加する
menu_command.add_command(label='新規登録', command=save)
menu_command.add_separator()
menu_command.add_command(label='削除', command=delete)

# 一覧用のフレーム
c_frame = tk.Frame(root)
column = ('Title', 'Author', 'Publisher')

# Treeviewの作成
list_view = ttk.Treeview(c_frame,columns=column,height=5)
list_view.bind('<<TreeviewSelect>>', view_select)

# 表の見出し
list_view.heading('#0', text='')
list_view.heading('Title',text='タイトル',anchor='center')
list_view.heading('Author', text='著者', anchor='center')
list_view.heading('Publisher',
                  text='出版社', anchor='center')

# 列の設定
list_view.column('#0', width=0, stretch='no')
list_view.column('Title', anchor='w', width=200)
list_view.column('Author', anchor='w', width=80)
list_view.column('Publisher', anchor='w', width=80)

# 一覧にデータを追加
def disp():
  list_view.delete(*list_view.get_children()) # 初期化
  global books_list
  books_list = get_books()
  for i in range(len(books_list)):
    list_view.insert(parent='', index='end', iid=i ,
    values = (books_list[i][2], books_list[i][3],
              books_list[i][4]))
  list_view.grid(row=0, column=0, sticky=tk.NSEW)
  list_view.selection_set(i) # 最後のアイテムを選択
  list_view.see(i) # 最後のアイテムに移動
  # スクロールバーの作成
  ybar = tk.Scrollbar(c_frame, orient=tk.VERTICAL,
                      width=16, command=list_view.yview)
  list_view.configure(yscrollcommand=ybar.set)
  ybar.grid(row=0, column=1, sticky=tk.NSEW)

# 一覧の作成
disp()

# 詳細部分の作成
d_frame = tk.Frame(root)
d_frame.grid_rowconfigure(0, weight=1)
d_frame.grid_columnconfigure(0, weight=1)
d_frame.grid_columnconfigure(1, weight=1)
column = ('Item', 'Contents')
item_view = ttk.Treeview(d_frame, show='tree',
                         columns=column, height = 6)
item_view.column('#0', width=0, stretch='no')
item_view.column('Item', anchor='w', width=80)
item_view.column('Contents', anchor='w', width=140)

# 詳細の表示
slct_items = list_view.selection()
for i in range(len(s)):
  item_view.insert(parent='', index='end', iid=i,
                   values=(s[i],
                   books_list[int(slct_items[0])][i+1]))
item_view.grid(row=0, column=0)
try:
  img = \
    tk.PhotoImage(file=books_list[int(slct_items[0])][7])
except tk.TclError:
  img = ''
panel = tk.Label(d_frame, image=img)
panel.grid(row=0, column=1, sticky=tk.NSEW)
c_frame.grid(row=0, column=0)
d_frame.grid(row=1, column=0,sticky=tk.NSEW)

root.mainloop()


 

実行結果

起動するとすでに登録してある2冊の情報が表示されます。

 

蔵書管理アプリ

 

「操作」→「新規登録」で登録画面が表示されます。

新規登録

蔵書管理アプリウインドウに記載し保存を押すとデータが保存されました。画像ファイルはサンプルデータのimg_004.pngを使用してみました。

登録



 

 

 

 

 

/* -----codeの行番号----- */