tkinterで表・ボタン・時間表示

#はじめに + tkinterとは
ロボットを操作する際に色んな環境と競合せず，簡単に使用できるpythonのためのGUIライブラリはないかなーとなったときにtkinterを発見しました．ボタンやラベルを使って比較的簡単にUIを作ることができます．

[参考]tkinter
https://docs.python.org/ja/3/library/tkinter.html
[参考]python用のGUIインターフェース一覧
https://www.simugrammer.com/python_gui_matome/

ちなみにpythonにはどうやらたくさんGUIを作れるライブラリがあるっぽいです．
正直，比較していないので特にtkinterが良い理由はないです（笑），，，

その中で今回はロボットのような状態を命令したり表示したりすることが求められる際に利用できる書き方を以下に示したいと思います．
tkinterを使って何か操作したり表示させたい場合はぜひ参考にしてください．
ただ長らくお世話になっている先人たちが丁寧に説明してくださっているので，ここでは割愛してコメントのみ記載しています．
割とtkinterを使用してきた人向けにしています．

#作製したもののポイント
- ボタン用画像読み込み

```python
img = tk.PhotoImage(file=name)#tkinterで画像を読む書き方
img = img.subsample(4, 4)#サイズを変更
```

- ボタン表示

```python
self.forth_btn = tk.Button(self.forth_frame, cursor='hand2', image=self.btn_imgs[4], borderwidth=5, relief="raised",state='normal', command=lambda:self.callback("grid_"))
self.forth_btn.pack(padx=10, pady=10)
```

- リストボックス

```python
self._chk_value = tk.BooleanVar(value = True)#チェックボックス用の値True/False            
self.third_chk = tk.Checkbutton(self.third_setting, variable=self._chk_value, font=("MSゴシック", "10", "bold"))#チェックボックス生成
self.third_chk.grid(row=0, column=1)
```

- チェックボックス

```python
_time_length = [0,1,2,3,4,5]#リストボックス用の値
self._limit_time_combo = ttk.Combobox(self.third_setting, state='readonly', width=8)#リストボックス生成
self._limit_time_combo['values'] = _time_length#リストボックスの値格納
self._limit_time_combo.current(0)#リストボックスのindex番号を初期値として記載
self._limit_time_combo.grid(row=1, column=1)
```

- ラベル

```python
self.second_lbl = tk.Label (self.second_frame, text='ラベルとか表とか作ってみた', bg='skyblue', font=("MS", "13", "bold"))
self.second_lbl.pack(fill='x', padx=100)
```

- 表（単純なもの．*複雑なものは省略）
以下に簡単な表の作製方法を示す．
Frameで表の枠を作り，Labelで表の一つずつを生成してgridで位置を確定する．
表を結合する場合は***grid内にsticky=tk.N+tk.S***をつくる．この場合はNとSなので上下の結合．

```python
k = 0
self.items_third = [0]*(raw*col)
for i in range (raw): #一番シンプルな表の生成
     for j in range (col):
         self.items_third[k] = tk.Label(self.third_table, width=6, borderwidth=3, relief="ridge") 
         self.items_third[k].grid(row=i,column=j) #単純にgridで行列を指定
         if i==0: #一行目は名前
            self.items_third[k]['bg']='gray'
            if j==0:#一列目
                self.items_third[k] ['text'] = ""
            else:
                self.items_third[k] ['text'] = str(j)
         else:#一行目以外は値などを格納
            if j==0:#一列目
                self.items_third[k] ['text'] = str(raw-i)
      k+=1
```

- タイマーカウント

```python
def time_count(self):
    self.flag_time=1
    self.start_time = int(time.time())
    while self.flag_time==1:
        cur_time = int(time.time())
        cnt = str((cur_time-self.start_time)//60).zfill(2) + ' : '+ str((cur_time-self.start_time)%60).zfill(2)
        self.second_time_status['text'] = cnt

def callback(self, command_name): 
    if command_name == 'time_count':
        print("TIME COUNT")
        if self.flag_time==0:#時間カウント:threadを使用するとtkinterのmainloopと別でloopを回すことが可能
            self.thread_time = threading.Thread(target=self.time_count)
            self.thread_time.setDaemon(True)#mainloop終了したら同時に終了させる，ないとバグ発生するのでかなり重要
            self.thread_time.start()#threadをスタート
```

# 完成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1106935/d25fa0b7-2698-7429-a221-ea1425f3174c.png)


#ソースコード

```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import time
import threading
import tkinter as tk
from tkinter import ttk

class UI_Operation(tk.Frame):
    def __init__(self, master):
        self._num = 7#表の段
        self._col = 8#個別タイプのものの個数（maxで10）
        
        #tkinterメイン画面の作製
        self.master = master
        self.master.geometry("720x720")
        self.master.title('きーた')
        
        #ボタン画像読み込み+表示
        btn_list = ["power.png", "start.png", "stop.png", "update.png", "poweroff.png"]
        self.btn_imgs = [0]*len(btn_list)
        self.btn_img(btn_list)
        
        #時間カウントフラッグ
        self.flag_time = 0
        
        #フレーム作製
        self.make_first()
        self.make_second()
        self.make_third()
    
    #一つ目のフレーム
    def make_first(self):
        try:
            #タイトル，横一杯にに広げる
            self.first_title = tk.Label(self.master, text="tkinterでいろいろ作ってみた", font=("MSゴシック", "15", "bold"))
            self.first_title.pack(fill='x')
            
            self.first_func_frame = tk.Frame(self.master, bd=2, relief="ridge")
            self.first_func_frame.pack(fill='x', side='top')
            
            #時間カウント開始のためのボタン
            self.first_exe_frame = tk.Frame(self.first_func_frame, bg='skyblue', bd=2, relief="ridge")
            self.first_exe_frame.pack(side='left')
            self.first_exe__lbl = tk.Label(self.first_exe_frame, text="## ボタン ##", bg='skyblue',font=("MSゴシック", "13", "bold"))
            self.first_exe__lbl.pack(fill='x', padx=10)
            self.first_exe_btn = tk.Button(self.first_exe_frame, image=self.btn_imgs[0], bg='skyblue',command=lambda:self.callback("time_count"))
            self.first_exe_btn.pack(padx=10, pady=10)
            
            self.first_select_frame = tk.Frame(self.first_func_frame, bd=2, relief="ridge", bg='skyblue',)
            self.first_select_frame.pack(fill='x')
            self.choice = ['qqqqqa', 'wwwww', 'eeeee', 'rrrrr']
            self.first_select_lbl = tk. Label (self.first_select_frame, text='## ボタンとかリストボックスとか ##', bg='skyblue',font=("MSゴシック", "13", "bold"))
            self.first_select_lbl.pack(fill='x', padx=100)
            self.first_select_status = tk.Label(self.first_select_frame, text="状態", width=20, borderwidth=3, bg="white", relief="ridge")
            self.first_select_status.pack(fill='y', side='left', padx=10, pady=10)
            self.first_select_lot_frame = tk. Frame(self.first_select_frame, bg='skyblue',) 
            self.first_select_lot_frame.pack(side='left', padx=10)
            self.first_select_lot = tk. Label (self.first_select_lot_frame, text='　選択肢　', bg='skyblue',font=("MS5", "10", "bold"))
            self.first_select_lot.pack(side='top')
            self.first_select_comb = ttk.Combobox(self.first_select_lot_frame, state='readonly', width=20)#リストボックスを設置
            self.first_select_comb['values'] = self.choice#リストボックスの内容
            self.first_select_comb.pack(side='bottom', padx=3) 
            self.first_select_comb.current(0)#リストボックスの初期値（indexの番号で表示内容を呼ぶ）
            self.first_select_btn_start = tk.Button(self.first_select_frame, image=self.btn_imgs [1], command=lambda:self.callback("start"))
            self.first_select_btn_start.pack(side='left', padx=10, pady=10)
            self.first_select_btn_end = tk.Button(self.first_select_frame, image=self.btn_imgs [2], command=lambda:self.callback("end")) 
            self.first_select_btn_end.pack(side='left', padx=10, pady=10)
        except:
            print("miss first frame")
        
    def make_second(self):
        try:
            self.second_frame = tk. Frame (self.master, bd=2, bg='skyblue',relief="ridge") 
            self.second_frame.pack(side='top', fill='x')
            
            self.second_lbl = tk.Label (self.second_frame, text='ラベルとか表とか作ってみた', bg='skyblue', font=("MS", "13", "bold"))
            self.second_lbl.pack(fill='x', padx=100) 
            
            self.second_time_frame = tk.Frame(self.second_frame, bg='skyblue',)
            self.second_time_frame.pack(side='left', fill='y') 
            self.second_time_lbl =  tk.Label(self.second_time_frame, text="じかん", height=2, width=10, borderwidth=1, bg="gray", relief="ridge") 
            self.second_time_lbl.pack(side='top', fill='x')        
            self.second_time_status = tk.Label(self.second_time_frame, text='　　',  height=6, width=10, borderwidth=1, bg="white", relief="ridge", font=("MS", "13", "bold"))
            self.second_time_status.pack(side='top', fill='x') 
            
            #表の作製→ラベルをframe内でgridを使って整列させる
            self.second_grid_frame = tk.Frame(self.second_frame, bg='skyblue',)
            self.second_grid_frame.pack(side='left', fill='y')
            second_grid_num = self._num+2
            second_grid_raw = self._col+4
            self.items_grid_name = [0]*(2*second_grid_raw)
            self.items_grid_col_name = [0]*self._num
            self.items_grid_a = [0]*self._num
            self.items_grid_b = [0]*self._num
            self.items_grid_c = [0]*self._num
            self.items_grid_d = [0]*(self._num*self._col)
            second_grid_list = ["a", "b", "c", "d", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
            for i in range (second_grid_num):
                for j in range (second_grid_raw):
                    if i==0:
                        self.items_grid_name[i*second_grid_raw+j] = tk.Label(self.second_grid_frame, width=6, borderwidth=1, relief="ridge")
                        self.items_grid_name[i*second_grid_raw+j]['bg'] = 'gray'
                        if j<second_grid_raw-self._col:
                            self.items_grid_name[i*second_grid_raw+j]['text'] = second_grid_list[j]
                            self.items_grid_name[i*second_grid_raw+j].grid(row=i, column=j, rowspan=2, sticky=tk.N+tk.S)#表を結合したい場合はsticky
                        elif j==second_grid_raw-self._col:
                            self.items_grid_name[i*second_grid_raw+j]['text'] = "e"
                            self.items_grid_name[i*second_grid_raw+j].grid(row=i, column=j, columnspan=self._col, sticky=tk.E+tk.W)#gridでラベルの位置を指定する
                        else:
                            pass
                    elif i==1:
                        if j>=second_grid_raw-self._col:
                            self.items_grid_name[i*second_grid_raw+j] = tk.Label(self.second_grid_frame, width=6, borderwidth=1, relief="ridge")
                            self.items_grid_name[i*second_grid_raw+j]['text'] = second_grid_list[j]
                            self.items_grid_name[i*second_grid_raw+j]['bg'] = 'gray'
                            self.items_grid_name[i*second_grid_raw+j].grid(row=i, column=j)
                    elif i>=2:
                        if j==0:
                            self.items_grid_col_name[i-2] = tk.Label(self.second_grid_frame, width=6, borderwidth=1, relief="ridge")
                            self.items_grid_col_name[i-2]['text'] = str(second_grid_num-i)
                            self.items_grid_col_name[i-2]['bg'] = 'gray'
                            self.items_grid_col_name[i-2].grid(row=i, column=j)
                        elif j==1:
                            self.items_grid_a[i-2] = tk.Label(self.second_grid_frame, width=6, bg='white', borderwidth=1, relief="ridge")
                            self.items_grid_a[i-2].grid(row=i, column=j)
                        elif j==2:
                            self.items_grid_b[i-2] = tk.Label(self.second_grid_frame, width=6, bg='white', borderwidth=1, relief="ridge")
                            self.items_grid_b[i-2].grid(row=i, column=j)
                        elif j==3:
                            self.items_grid_c[i-2] = tk.Label(self.second_grid_frame, width=6, bg='white',borderwidth=1, relief="ridge")
                            self.items_grid_c[i-2].grid(row=i, column=j)
                        else:
                            self.items_grid_d[(i-2)*self._col+(j-self._col+1)] = tk.Label(self.second_grid_frame, width=6, bg='white',borderwidth=1, relief="ridge")
                            self.items_grid_d[(i-2)*self._col+(j-self._col+1)].grid(row=i, column=j)                                       
        except:
            return -1
                        
    def make_third(self):
        try:            
            raw = 5
            col = 5
            self.third_frame = tk.Frame(self.master, bd=2, relief="ridge", width=100)
            self.third_frame.pack(side='left', fill='y')
            self.third_lbl = tk.Label (self.third_frame, text='表1', font=("MSゴシック", "13", "bold"))
            self.third_lbl.pack(fill='x', padx=30)
            
            self.third_settings_frame = tk.Frame(self.third_frame)
            self.third_settings_frame.pack(side='top', padx=15, pady=15)
            
            self.third_setting = tk.Frame(self.third_settings_frame)
            self.third_setting.pack(side='left', padx=10)
            
            self.third_setting_lbl = tk.Label (self.third_setting, text='ちぇっく', font=("MSゴシック", "10", "bold"))
            self.third_setting_lbl.grid(row=0, column=0)
            self._chk_value = tk.BooleanVar(value = True)            
            self.third_chk = tk.Checkbutton(self.third_setting, variable=self._chk_value, font=("MSゴシック", "10", "bold"))
            self.third_chk.grid(row=0, column=1)
            self._limit_time = tk.Label(self.third_setting, text='選べ', font=("MSゴシック", "10", "bold"))
            self._limit_time.grid(row=1, column=0)
            _time_length = [0,1,2,3,4,5]
            self._limit_time_combo = ttk.Combobox(self.third_setting, state='readonly', width=8)
            self._limit_time_combo['values'] = _time_length
            self._limit_time_combo.current(0)
            self._limit_time_combo.grid(row=1, column=1)
            self.third_btn = tk.Button(self.third_settings_frame, image=self.btn_imgs[1], command=lambda:self.callback("grid"))
            self.third_btn.pack(side='right', padx=10)
            
            self.third_table = tk.Frame(self.third_frame)
            self.third_table.pack(side='top', padx=10)
            k = 0
            self.items_third = [0]*(raw*col)
            for i in range (raw): #一番シンプルな表の生成
                for j in range (col):
                    self.items_third[k] = tk.Label(self.third_table, width=6, borderwidth=3, relief="ridge") 
                    self.items_third[k].grid(row=i,column=j) #単純にgridで行列を指定
                    if i==0: 
                        self.items_third[k]['bg']='gray'
                        if j==0:
                            self.items_third[k] ['text'] = ""
                        else:
                            self.items_third[k] ['text'] = str(j)
                    else:
                        if j==0:
                            self.items_third[k] ['text'] = str(raw-i)
                    k+=1

            self.forth_frame = tk.Frame(self.master, bd=2, relief="ridge", width=100)
            self.forth_frame.pack(side='left', fill='y')
            self.forth_lbl = tk. Label (self.forth_frame, text='表2', font=("MSゴシック", "13", "bold"))
            self.forth_lbl.pack(fill='x', padx=30)
            self.forth_btn = tk.Button(self.forth_frame, cursor='hand2', image=self.btn_imgs[4], borderwidth=5, relief="raised",state='normal', command=lambda:self.callback("grid_"))
            self.forth_btn.pack(padx=10, pady=10)
            self.forth_table_frame = tk.Frame(self.forth_frame)
            self.forth_table_frame.pack(side='top', padx=10)
            self.items_forth = [0]*(col*(raw))
            self.items_forth_row = [0]*(raw)
            self.items_forth_col = [0]*(col+1)
            self.items_forth_stck = [0]*(col*(raw))
            for i in range (raw*2-1): 
                for j in range (col):
                    if i==0: 
                        self.items_forth_col[j] = tk.Label(self.forth_table_frame, width=6,borderwidth=1, relief="ridge")
                        self.items_forth_col[j]['bg']='gray'
                        if j==0:
                            self.items_forth_col[j] ['text'] = ""
                            self.items_forth_col[j]['width']='6'
                        else:
                            self.items_forth_col[j] ['text'] = str(j)
                        self.items_forth_col[j].grid(row=i,column=j) 
                    else:
                        if j==0:
                            if i%2==1:
                                self.items_forth_row[i//2] = tk.Label(self.forth_table_frame, width=6,borderwidth=1, relief="ridge")
                                self.items_forth_row[i//2] ['text'] = str(raw-i//2-1)
                                self.items_forth_row[i//2]['bg']='gray'
                                self.items_forth_row[i//2].grid(row=i,column=j, rowspan=2, sticky=tk.N+tk.S) 
                        else:
                            if i%2==1:
                                self.items_forth[(i//2)*col+j-1] = tk.Label(self.forth_table_frame, width=6,bg='white',borderwidth=1, relief="ridge")
                                self.items_forth[(i//2)*col+j-1].grid(row=i,column=j) 
                            else:
                                self.items_forth_stck[(i//2-1)*col+j-1] = tk.Label(self.forth_table_frame, width=6,bg='white',borderwidth=1, relief="ridge")
                                self.items_forth_stck[(i//2-1)*col+j-1].grid(row=i,column=j) 
        except:
            pass
        
    def btn_img(self, btn_list): 
        for i, name in enumerate(btn_list):
            img = tk.PhotoImage(file=name)
            if i==3:
                img = img.subsample(4, 4)
            else: 
                img = img.subsample(2, 2)
            self.btn_imgs[i]=img

    def time_count(self):
        self.flag_time=1
        self.start_time = int(time.time())
        while self.flag_time==1:
            cur_time = int(time.time())
            cnt = str((cur_time-self.start_time)//60).zfill(2) + ' : '+ str((cur_time-self.start_time)%60).zfill(2)
            self.second_time_status['text'] = cnt
            
    def callback(self, command_name): 
        if command_name == 'time_count':
            print("TIME COUNT")
            if self.flag_time==0:#時間カウント:threadを使用するとtkinterのmainloopと別でloopを回すことが可能
                self.thread_time = threading.Thread(target=self.time_count)
                self.thread_time.setDaemon(True)#mainloop終了したら同時に終了させる，ないとバグ発生するのでかなり重要
                self.thread_time.start()#threadをスタート
            elif self.flag_time==1:
                self.flag_time = 0
        elif command_name== 'start': 
            print("start")
            product_name = self.first_select_comb.get()
            if product_name=='': 
                print("NOT SELECTED")
            else:
                print(product_name)
        elif command_name == 'end':
            print('end')
        elif command_name == 'grid':
            chk = self._chk_value.get()#ちぇっくぼっくすやリストボックスの内容を取得
            num = self._limit_time_combo.get()
            print('chk is ', chk, ' num id ', num)
        elif command_name == 'grid_':
            print('forth')
        
def main():
    master = tk.Tk()#tkinterのメインの起動
    UI_Operation(master=master)
    master.mainloop()
    
if __name__=='__main__':
    main()
```


以上です．