ろうとるがPythonを扱う、、（その５：tkinterでタブ）

# タブ
ブラウザのように複数のタブをtkinterで実現できるので、その記録。

## 何をするか
[前回](https://qiita.com/infinite1oop/items/fd3793325c5999119b16)や[前々回](https://qiita.com/infinite1oop/items/96a153037bd7b6edf093)に続き、Subprocessを用いてWindowsコマンドを実行するのであるが、コマンドごとにタブを分ける。

## 結果サンプル
コマンドとして、「ipconfig」、「ping」、「tracert」を取り扱う。
![ipconfigのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/352653d7-c4d3-0758-c962-6b22e79abdc2.png)
![ping.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/c2fb15ce-c484-3f0b-c63c-b7e5e9243a02.png)
![tracert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/94754f4c-9820-ca93-f58b-4730ddd76edb.png)

## ソースコード
ポイントと感じたところを下記する。

### タブの作成
こちらのサイト（[【Python tkinter】複数のタブを実装したGUIアプリの作成（Notebookウィジェット）](https://office54.net/python/tkinter/ttk-notebook-tab)）をもとに作成。

```python
# Create Notebook Widget
note = ttk.Notebook(root)

# Create tab
tab0 = tk.Frame(note)
tab1 = tk.Frame(note)
tab2 = tk.Frame(note)

# Add tab
note.add(tab0, text=cmd_list[0])
note.add(tab1, text=cmd_list[1])
note.add(tab2, text=cmd_list[2])

# Create content of tab
create_content(tab0, 0)
create_content(tab1, 1)
create_content(tab2, 2)

# Locate tab
note.pack(expand=True, fill='both') #, padx=10, pady=10)
```
先ほどのリンク先を参照して作成。タブは３つ。タブコンテンツの作成は、後述する関数（create_content()）を用いる。

タブカラーの設定（引用先サイトがどこかわからなくなりました）。
```python
# Create an instance of ttk style
fgcolor = "yellow"
bgcolor = "gray80"
style = ttk.Style()
style.theme_create("style1", parent="alt", settings={
        "TNotebook.Tab": {
            "configure": {"background": bgcolor },
            "map":       {"background": [("selected", fgcolor)],
                          } } } )
style.theme_use("style1")
```
選択中タブは黄色、それ以外はグレー。

タブ内コンテンツの作成関数。
```python
# Create content of tab
def create_content(frm, arg):
    frm1 = tk.Frame(frm)
    frm2 = tk.Frame(frm)
    frm1.pack()
    frm2.pack()
    frm.text = tk.Text(frm2, width=80, height=40)
    ysc = tk.Scrollbar(frm2, orient=tk.VERTICAL, command=frm.text.yview)
    frm.text["yscrollcommand"] = ysc.set
    okBtn = ttk.Button(frm1, text='OK', command=lambda:ok_clk(frm, arg))
    label = ttk.Label(frm1, text='Enter Arguments')
    label.grid(row=0, column=0)
    frm.entry = ttk.Entry(frm1)
    frm.entry.grid(row=0, column=1)
    okBtn.grid(row=0, column=2)
    ysc.pack(side=tk.RIGHT, fill="y")
    frm.text.pack()
    return
```
２つのフレームを設ける。１つ目のフレームには、文字列（label）、コマンド引数用エントリー（frm.entry)、OKボタン（okBtn）を配置する。２つ目のフレームには、コマンド実行結果を格納するテキストボックス（frm.text）＋スクロールバーを配置する。また、タブの種類を示す引数（frm）を用いている。

OKボタンがクリックされたときの関数。
```python
# command to execute
cmd_list = ['ipconfig', 'ping', 'tracert']

# Function for click OK
def ok_clk(frm, arg):
    cmd_arg = [cmd_list[arg]]
    if frm.entry.get() != '':
        cmd_arg.extend(frm.entry.get().split(' ')) # merge list
    output = subprocess.run(cmd_arg, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout
    output = output.decode('shift_jis')
    frm.text.config(state='normal')   # editable
    frm.text.insert(tk.END, output)
    frm.text.config(state='disabled') # not editable
    frm.text.see('end')               # going to last line
    return
```
コマンド自体のリスト化、引数がある場合は、リストを結合する。Subprocessによりコマンドを実行し、コードをShift-JIS化。テキストボックスを編集モードにし、結果を書き込み、非編集モードにする。最後に、最終行に移動して完了。

### コード全体
```python
import subprocess
import tkinter as tk
import tkinter.ttk as ttk

# command to execute
cmd_list = ['ipconfig', 'ping', 'tracert']

# Function for click OK
def ok_clk(frm, arg):
    cmd_arg = [cmd_list[arg]]
    if frm.entry.get() != '':
        cmd_arg.extend(frm.entry.get().split(' ')) # merge list
    output = subprocess.run(cmd_arg, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout
    output = output.decode('shift_jis')
    frm.text.config(state='normal')   # editable
    frm.text.insert(tk.END, output)
    frm.text.config(state='disabled') # not editable
    frm.text.see('end')
    return

# Create content of tab
def create_content(frm, arg):
    frm1 = tk.Frame(frm)
    frm2 = tk.Frame(frm)
    frm1.pack()
    frm2.pack()
    frm.text = tk.Text(frm2, width=80, height=40)
    ysc = tk.Scrollbar(frm2, orient=tk.VERTICAL, command=frm.text.yview)
    frm.text["yscrollcommand"] = ysc.set
    okBtn = ttk.Button(frm1, text='OK', command=lambda:ok_clk(frm, arg))
    label = ttk.Label(frm1, text='Enter Arguments')
    label.grid(row=0, column=0)
    frm.entry = ttk.Entry(frm1)
    frm.entry.grid(row=0, column=1)
    okBtn.grid(row=0, column=2)
    ysc.pack(side=tk.RIGHT, fill="y")
    frm.text.pack()
    return

# Start of main program
# root main window
root = tk.Tk()
root.title("Tab Practice")
root.geometry("700x500")

# Create an instance of ttk style
fgcolor = "yellow"
bgcolor = "gray80"
style = ttk.Style()
style.theme_create("style1", parent="alt", settings={
        "TNotebook.Tab": {
            "configure": {"background": bgcolor },
            "map":       {"background": [("selected", fgcolor)],
                          } } } )
style.theme_use("style1")

# Create Notebook Widget
note = ttk.Notebook(root)

# Create tab
tab0 = tk.Frame(note)
tab1 = tk.Frame(note)
tab2 = tk.Frame(note)

# Add tab
note.add(tab0, text=cmd_list[0])
note.add(tab1, text=cmd_list[1])
note.add(tab2, text=cmd_list[2])

# Create content of tab
create_content(tab0, 0)
create_content(tab1, 1)
create_content(tab2, 2)

# Locate tab
note.pack(expand=True, fill='both') #, padx=10, pady=10)

# main loop
root.mainloop()
```

EOF