(2024.11.26) 少し変更。クリップボードコピー機能を追加。指定した行に色をつけた。
はじめに
Tkinter(ttk)
のTreeview
を使う練習で、手持ちのガジェットを表示するプログラムを作ってみた。
機能は以下の通り。
- 2次元 numpy 配列で定義したデータを Treeview で表示する。
- 指定行の背景色・文字色を指定する(tag の利用)。
- 表示内容を pandas DataFrame に変換し csv ファイル出力する。
- 指定行をクリップボードコピーする(エディタにペースト可能)。
当方の環境は以下の通り。
MacBook Pro 14"
Chip Apple M1 Pro
Memory 16GB
macOS Sequoia 15.1
Python 3.13.0
Tcl/Tk 9.00
参考にしたサイトは以下の通り。
Treeviewの使い方
https://yuya-blog.net/【完全独学python】tkinterのtreeviewでオシャレな表を簡単作成/
スクロールバー
https://office54.net/python/tkinter/python-tkinter-scrollbar
Treeviewに表示された内容をDataFrame化
https://teratail.com/questions/311037
作例
まず作例の写真を示そう。
Tab1(All 2020-)
Tab2 (Only Apple)
能書
なぜこれを作ろうと思ったのか?
たまに、「この Mac はいつ買ったんだっけ?」とか思うことがあり、それらの情報をスピーディーに表示するものが欲しくなったから。もちろんTerminal
を立ち上げてpython ...
なんていうことはせず、Autometor
でapp
化、Stream Deck に登録してボタンひとつで確認できるようにしている。
こういうのは Notion とかでやるのが普通じゃない?
データは手元にあったほうがうれしいし、Notion の使い方もよくわからない。
エクセルで十分でない?
その通り。だけど自分で作ってみたかった。
実際、Tab1 の All 2020- は以下のエクセルファイルを読み込んで表示しているだけである。
機能説明
Tab1(All 2020-)
上記のように、エクセルで作成してあるデータを、pandas
で読み込み、numpy
配列に変換後、Treeview
で表示している。
表示は、Apple製品は黄色背景色で、メインで使用中の機器を水色背景で、すでに手元にない・使えないものを灰色文字としている。色をつける方法は、tag
を指定し、tag_configure
で背景色・文字色を指定するものである。
Tab2 (Only Apple)
データはプログラム内でnumpy
配列として定義、Treeview
で表示している。
共通
CSV 出力
「Output csv
」のボタンを設け、データバックアップのため、csv
ファイル出力できるようにしている。出力したらmessagebox
でメッセージを表示する。
クリップボードコピー
「c
」キーを押すとクリップボードに選択行(カーソル行)をコピーする。標準ショートカット「cmd + v
」でテキストエディタにペーストできる。ペースト結果は以下のようにカッコ付きタプルとなる。pyperclip
をインストールしておく必要あり。
('o', 'Caldigit USB-C HDMI Dock', 'Amazon', '2021.02.24', '2021.02.25', '26,400')
pip install pyperclip
コード
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import sys
import pandas as pd
import numpy as np
import pyperclip
def inp_data_1():
dir='/Users/kk/stream_deck/'
fnameR=dir+'inp_gadget.xls'
df=pd.read_excel(fnameR,sheet_name=0,header=0,index_col=None)
data=df.to_numpy(dtype=object)
return data
def inp_data_2():
data =np.array([
['MacBook Pro 13" (Mid 2014)', '2015.02.01', 132624, '2020.12.29', 22400],
['iPad mini 4 (64GB)', '2016.03.12', 58104, '2021.01.04', 19000],
['iMac Retina 21.5"', '2018.12.14', 183384, '2021.01.04', 53000],
['iPad Air 4, 64GB,Wi-Fi', '2020.11.08', 69080, '2021.11.13', 42000],
['MacBook Air (M1 2020) 8/256GB','2020.12.28', 115280, '2021.11.13', 64050],
['M1 iPad Pro 12.9 128GB', '2021.09.23', 129800, '2023.08.25', 96000],
['iPad mini 6 64GB', '2021.11.07', 59800, '2023.08.25', 53000],
['Apple Pencil', '2020.11.02', 15950, '---', '---'],
['iPhone SE White 64GB', '2021.07.21', 12848, '---', '---'],
['Magic Keyboard', '2021.10.04', 10800, '---', '---'],
['Magic Trackpad', '2021.10.04', 13800, '---', '---'],
['MacBook Pro (14-in 2021 M1 Pro 16/512GB)', '2021.10.27', 239800, '---', '---'],
['Apple Watch Series 7 45mm', '2022.04.17', 52800, '---', '---'],
['iPhone 13 mini 128GB blue', '2022.07.03', 86800, '---', '---'],
['11-in iPad Pro Wi-Fi 128GB spacegray', '2023.08.24', 124800, '---', '---']
],dtype=object)
return data
def outcsv(nnn,tree,col):
data=[]
for ii in tree.get_children():
s=tree.item(ii,'values')
data.append(s)
df=pd.DataFrame(data)
df.columns=col
dir='/Users/kk/stream_deck/'
match nnn:
case 1: fnameW='out_gadget1.csv'
case 2: fnameW='out_gadget2.csv'
fnameW=dir+fnameW
df.to_csv(fnameW,index=False)
s='saved as '+fnameW
messagebox.showinfo('',s)
def copy_row(event,tree):
# copy on clipboard
ii = tree.focus()
s = tree.item(ii, 'values')
pyperclip.copy(s)
def b_end():
sys.exit()
def table(nnn):
global tab1,tab2
match nnn:
case 1:
tabn=tab1
data=inp_data_1()
tp=(1,2,3,4,5,6)
col=['ox','Item','Shop','Order','Arrive','JPY']
ww=[50,400,100,100,100,100]
ac=[tk.CENTER,tk.W,tk.CENTER,tk.CENTER,tk.CENTER,tk.E]
case 2:
tabn=tab2
data=inp_data_2()
tp=(1,2,3,4,5)
col=['Item','Buy','JPY (buy)','Sell','JPY (sell)']
ww=[400,100,100,100,100]
ac=[tk.W,tk.CENTER,tk.E,tk.CENTER,tk.E]
nrow=len(data)
irow=20
if irow<=nrow: n=irow
else: n=nrow
tree = ttk.Treeview(tabn, style='Treeview',height=n)
tree['columns'] = tp
tree.column('#0', width=0, stretch=tk.NO)
tree.heading('#0', text='', anchor=tk.W)
for i in range(0,len(col)):
tree.column(i+1,width=ww[i],anchor=ac[i])
for i in range(0,len(col)):
tree.heading(i+1,text=col[i],anchor=ac[i])
match nnn:
case 1:
for i, (ox,item,shop,d1,d2,p,c) in enumerate(data):
if str(p).isdecimal()==True: pp = '{:,.0f}'.format(p)
else: pp=p
tree.insert('','end',iid=i,values=(ox,item,shop,d1,d2,pp),tags=c)
tree.tag_configure(2,background='#00ffff')
tree.tag_configure(1,background='#ffff66')
tree.tag_configure(-1,foreground='#aaaaaa')
case 2:
for i, (item,d1,p1,d2,p2) in enumerate(data):
if str(p1).isdecimal()==True: pp1 = '{:,.0f}'.format(p1)
else: pp1=p1
if str(p2).isdecimal()==True: pp2 = '{:,.0f}'.format(p2)
else: pp2=p2
tree.insert('','end',iid=i,values=(item,d1,pp1,d2,pp2))
tree.grid(row=0,column=0)
tree.bind('<KeyPress-c>', lambda event:copy_row(event,tree))
if irow<=nrow:
scrollbar=ttk.Scrollbar(tabn, orient='vertical', command=tree.yview)
scrollbar.grid(row=0,column=1,sticky=(tk.N, tk.S))
tree.configure(yscrollcommand=scrollbar.set)
btn=ttk.Button(tabn,text='Output csv', command=lambda:outcsv(nnn,tree,col))
btn.grid(row=1,column=0)
def main():
global tab1,tab2
root = tk.Tk()
root.resizable(False,False)
root.title('py_gadget.py')
style = ttk.Style()
style.theme_use('clam')
fontn='Calibri 11'
fontb='Calibri 11 bold'
style.configure('Treeview.Heading',font=(fontb))
style.configure('Treeview',font=(fontn),rowheight=25)
style.configure('TButton',font=(fontb))
style.configure('TNotebook.Tab',font=(fontb))
nb = ttk.Notebook(root)
tab1 = ttk.Frame(nb)
tab2 = ttk.Frame(nb)
nb.add(tab1, text='All 2020-')
nb.add(tab2, text='Only Apple')
nb.pack()
for nnn in [1,2]:
table(nnn)
q_button=ttk.Button(root,text='Exit',command=b_end)
q_button.pack()
root.mainloop()
if __name__ == '__main__': main()
以 上