14
19

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) を少しいじってみる (2024.11.16)

Last updated at Posted at 2024-11-15

はじめに

tkinter をいじり始めたついでに、もう少しいじってみることにした、ここでは、簡単なカスタマイズ事例を紹介している。
なお、当方の環境は以下の通り。

MacBook Pro 14"
Chip Apple M1 Pro
Memory 16GB
macOS Sequoia 15.1
Python 3.13.0
Tcl/Tk 9.00
matplotlib 3.9.2

(感想)

つい最近、何気なくbrew uodatebrew upgradeを実行したら、tcl/tkのバージョンが8.6から9.0にかわり、tkinterが動かなくなった。もちろん Python を再インストールしたのだが、
(1) ttkthemesのテーマが使えなくなる、
(2) Canvasmatplotlibでの作図を貼り付けるために必要な、FigureCanvasTkAggがインポートできない
という事象に見舞われた。
このため、ここでは、ttkにビルトインされているテーマ(clam)を使うことにした。

私のような素人は、バージョン管理に対する考え方が甘く、「なんでも最新を入れておけばいいや」という考えをもちがちであるが、注意が必要であることを思い知らされた。

個人で使用するプログラムであれば、最新版を入れてうまく動けば別に問題ないのだが、インポートできないとか、変なエラーが出るという事態への対応で余分な労力を割くリスク低減のためには、標準機能のみを使うことが有効であると感じた。

作例

作例の画面写真は以下の通り。カスタマイズの色のセンス(黄色背景とか)については、自分の見やすさを優先しているため、ご容赦願いたい。

IMG_2171.jpeg

上記写真では、4つの Python プログラムを同時実行しているが、以下のプログラムで実現している。なお、下記コードではプログラムが終了しないので、ターミナルからctl+cを打って終了させる。

import subprocess

from tkinter import ttk
s = ttk.Style()
print(s.theme_names())
#('aqua', 'clam', 'alt', 'default', 'classic')

cmd0='python -m tkinter'
cmd1='python py_tk1.py'
cmd2='python py_tk2.py'
cmd3='python py_tk3.py'

subprocess.Popen(cmd0,shell=True)
subprocess.Popen(cmd1,shell=True)
subprocess.Popen(cmd2,shell=True)
subprocess.Popen(cmd3,shell=True)

写真の中身は以下の通り。

  • 1番左(tkinter):tcl/tkのバージョンを表示する
  • 左から2番目(py_tk1.py):テーマを何も指定しない場合。カスタマイズもなし。aquaが適用されている模様。
  • 左から3番目(py_tk2.py):テーマにclamを指定。カスタマイズなし。
  • 1番右(py_tk3.py):clamを自分でカスタマイズしたもの

上記プログラム py_tk1.py のコードは以下の通り。main()関数の3〜6行目(コメントアウトしているところ)をいじれば、py_tk2.pyとなる。なお、やっていることは他愛もないことなので、説明は省略する。

py_tk1.py
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import numpy as np


def end_q():
    quit()


def click_1(tab1):
    messagebox.showinfo('','Button-1 clicked')
    tab1.focus_set()


def click_2(tab1):
    messagebox.showinfo('','Button-2 clicked')
    tab1.focus_set()


def main():
    root =tk.Tk()
    root.resizable(False,False)
    root.title('py_tk1.py')
    #root.title('py_tk2.py')
    #style=ttk.Style()
    #style.theme_use('clam')

    frame0=ttk.Frame(root)
    label0=ttk.Label(frame0,text='Test frame')
    label0.pack()

    nb = ttk.Notebook(frame0)
    tab1 = ttk.Frame(nb)
    tab2 = ttk.Frame(nb)
    tab3 = ttk.Frame(nb)
    nb.add(tab1, text='Tab1')
    nb.add(tab2, text='Tab2')
    nb.add(tab3, text='Tab3')
    nb.pack(fill=tk.X)

    frame1=ttk.Frame(tab1)
    label_t1a=ttk.Label(frame1,text='label_t1a')
    label_t1b=ttk.Label(frame1,text='label_t1b')
    entry_t1a=ttk.Entry(frame1,justify=tk.CENTER,width=10)
    entry_t1b=ttk.Entry(frame1,justify=tk.CENTER,width=10)
    entry_t1a.insert(tk.END,'entry_t1a')
    entry_t1b.insert(tk.END,'entry_t1b')
    button_t1a=ttk.Button(frame1,text='button_t1a',command=lambda:click_1(tab1))
    button_t1b=ttk.Button(frame1,text='button_t1b',command=lambda:click_2(tab1))
    label_t1a.grid(row=0,column=0,pady=(3,3))
    entry_t1a.grid(row=0,column=1,pady=(3,3))
    label_t1b.grid(row=1,column=0,pady=(3,3))
    entry_t1b.grid(row=1,column=1,pady=(3,3))
    button_t1a.grid(row=2,column=0,columnspan=2,pady=(3,3))
    button_t1b.grid(row=3,column=0,columnspan=2,pady=(3,3))
    frame1.pack(anchor=tk.CENTER)

    label_t2a=ttk.Label(tab2,text='This is Tab2')
    label_t2a.pack()

    label_t3a=ttk.Label(tab3,text='This is Tab3')
    label_t3a.pack()

    frame_lf=ttk.LabelFrame(frame0,text='LabelFrame',relief=tk.FLAT)
    n,m=4,3
    lrc=np.empty((n,m),dtype=object)
    for i in range(0,n):
        for j in range(0,m):
            s='lrc[{0},{1}]'.format(i,j)
            lrc[i,j]=ttk.Label(frame_lf,text=s,borderwidth=1,relief=tk.SOLID,anchor=tk.CENTER,width=8)
            lrc[i,j].grid(row=i,column=j)
    frame_lf.pack()
    frame0.pack()

    q_button=ttk.Button(root,text='Quit',command=end_q)
    q_button.pack()

    root.mainloop()


if __name__ == '__main__': main()

カスタマイズの説明

見た目をカスタマイズしたpy_tk3.pyについて少し解説しておく。

テーマの指定

まず、テーマclamを指定する。

    style=ttk.Style()
    style.theme_use('clam')

全体のフォントと色

wedgit全体のフォントと背景色・前面色(フォントの色)を変更する。LabelframeLabelだけは太字(bold)とする。

    fontn='Calibri 14'
    fontb='Calibri 14 bold'
    bg_base,fg_base='#ffff00','#393939'
    style.configure('.',font=(fontn))
    style.configure('.',background=bg_base,foreground=fg_base)
    style.configure('TLabelframe.Label',font=(fontb))

タブの装飾

タブに表示する文字のフォントを太字とし、非選択時の背景色・前面色をconfigureで指定する。また選択時の背景色・前面色をmapを用いて指定する。

    style.configure('TNotebook.Tab',
        font=(fontb),
        background='#dddddd',
        foreground='#777777')
    style.map('TNotebook.Tab',
        foreground=[('selected',fg_base)],
        background=[('selected',bg_base)])

ボタンの装飾

ボタンの非選択時の背景色・前面色、見た目(relief)をconfigureで指定する。また選択時(マウスホバー時及び処理実行時)の背景色・前面色をmapを用いて指定する。

   style.configure('TButton',
       background='#eeeeee',
       foreground='#393939',
       relief=tk.RAISED)
   style.map('TButton',
       foreground=[('active','#0000ff')],
       background=[('active','#00ffff')])

Entryのフォント

なお、下記のように、Entreywedgitを定義する段階でフォントを指定する必要がある。

entry_t1a=ttk.Entry(frame1,justify=tk.CENTER,width=10,font=(fontn))

カスタマイズしたコード

コード全文は以下の通り。

py_tk3.py
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import numpy as np


def end_q():
    quit()


def click_1(tab1):
    messagebox.showinfo('','Button-1 clicked')
    tab1.focus_set()


def click_2(tab1):
    messagebox.showinfo('','Button-2 clicked')
    tab1.focus_set()


def main():
    root =tk.Tk()
    root.resizable(False,False)
    root.title('py_tk3.py')
    style=ttk.Style()
    style.theme_use('clam')
    fontn='Calibri 14'
    fontb='Calibri 14 bold'
    bg_base,fg_base='#ffff00','#393939'
    style.configure('.',font=(fontn))
    style.configure('.',background=bg_base,foreground=fg_base)
    style.configure('TLabelframe.Label',font=(fontb))
    style.configure('TNotebook.Tab',
        font=(fontb),
        background='#dddddd',
        foreground='#777777')
    style.map('TNotebook.Tab',
        foreground=[('selected',fg_base)],
        background=[('selected',bg_base)])
    style.configure('TButton',
        background='#eeeeee',
        foreground='#393939',
        relief=tk.RAISED)
    style.map('TButton',
        foreground=[('active','#0000ff')],
        background=[('active','#00ffff')])

    frame0=ttk.Frame(root)
    label0=ttk.Label(frame0,text='Test frame',font=(fontb))
    label0.pack()

    nb = ttk.Notebook(frame0)
    tab1 = ttk.Frame(nb)
    tab2 = ttk.Frame(nb)
    tab3 = ttk.Frame(nb)
    nb.add(tab1, text='Tab1')
    nb.add(tab2, text='Tab2')
    nb.add(tab3, text='Tab3')
    nb.pack(fill=tk.X)

    frame1=ttk.Frame(tab1)
    label_t1a=ttk.Label(frame1,text='label_t1a')
    label_t1b=ttk.Label(frame1,text='label_t1b')
    entry_t1a=ttk.Entry(frame1,justify=tk.CENTER,width=10,font=(fontn))
    entry_t1b=ttk.Entry(frame1,justify=tk.CENTER,width=10,font=(fontn))
    entry_t1a.insert(tk.END,'entry_t1a')
    entry_t1b.insert(tk.END,'entry_t1b')
    button_t1a=ttk.Button(frame1,text='button_t1a',command=lambda:click_1(tab1))
    button_t1b=ttk.Button(frame1,text='button_t1b',command=lambda:click_2(tab1))
    label_t1a.grid(row=0,column=0,pady=(3,3))
    entry_t1a.grid(row=0,column=1,pady=(3,3))
    label_t1b.grid(row=1,column=0,pady=(3,3))
    entry_t1b.grid(row=1,column=1,pady=(3,3))
    button_t1a.grid(row=2,column=0,columnspan=2,pady=(3,3))
    button_t1b.grid(row=3,column=0,columnspan=2,pady=(3,3))
    frame1.pack(anchor=tk.CENTER)

    label_t2a=ttk.Label(tab2,text='This is Tab2')
    label_t2a.pack()

    label_t3a=ttk.Label(tab3,text='This is Tab3')
    label_t3a.pack()

    frame_lf=ttk.LabelFrame(frame0,text='LabelFrame',relief=tk.FLAT)
    n,m=4,3
    lrc=np.empty((n,m),dtype=object)
    for i in range(0,n):
        for j in range(0,m):
            s='lrc[{0},{1}]'.format(i,j)
            lrc[i,j]=ttk.Label(frame_lf,text=s,borderwidth=1,relief=tk.SOLID,anchor=tk.CENTER,width=8)
            lrc[i,j].grid(row=i,column=j)
    frame_lf.pack()
    frame0.pack()

    q_button=ttk.Button(root,text='Quit',command=end_q)
    q_button.pack()

    root.mainloop()


if __name__ == '__main__': main()

以 上

14
19
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
14
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?