1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python Tkinter (ttk) Treeviewで表を作る (2024.11.25)

Last updated at Posted at 2024-11-24

(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-)

Screenshot 2024-11-26 at 12.49.33.png

Tab2 (Only Apple)

Screenshot 2024-11-26 at 12.49.43.png

能書

なぜこれを作ろうと思ったのか?

たまに、「この Mac はいつ買ったんだっけ?」とか思うことがあり、それらの情報をスピーディーに表示するものが欲しくなったから。もちろんTerminalを立ち上げてpython ...なんていうことはせず、Autometorapp化、Stream Deck に登録してボタンひとつで確認できるようにしている。

こういうのは Notion とかでやるのが普通じゃない?

データは手元にあったほうがうれしいし、Notion の使い方もよくわからない。

エクセルで十分でない?

その通り。だけど自分で作ってみたかった。
実際、Tab1 の All 2020- は以下のエクセルファイルを読み込んで表示しているだけである。

Screenshot 2024-11-26 at 12.50.17.png

機能説明

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()

以 上

1
3
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
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?