ろうとるがPythonを扱う、、（その11：Python+tkinterにてIPアドレス設定 ）

# IPアドレス設定をPython+tkinterにて
WindowsにてIPアドレス設定アプリケーションを、Python+tkinterにて実現してみた。ただし、機能不足（おそらく不具合も）があることに留意されたし。

## まずは結論
起動すると、下記のように、ネットワークアダプタごとの情報が表示される。
![一覧.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/07fa46c2-3636-b324-249e-151af094407a.png)
アダプタを選択して、Editをクリックすると、下記のような編集（設定）画面となる。
![Edit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/aee9f4e3-167a-3371-4f35-d09fcd6d576e.png)

## 方針
netshやPowershellのネットワーク系コマンドをSubprocessでハンドリングした方がEasyだったが、ここでは、pythonからWindowsの情報を取得できるWMI(Windows Management InstrumentationWindows Management Instrumentation)を利用。下記リンクなど参照。
- [WMI 1.5.1](https://pypi.org/project/WMI/)
- [python - WindowsでIPアドレスを変更するスクリプト](https://tutorialmore.com/questions-1289970.htm)

## ソースコード
### Import
```python
import wmi
import ctypes
import time
from socket import inet_aton
import tkinter as tk
from tkinter import messagebox
```
ここは説明なし。

### WMIからの情報取得
```python
### Network Adapter and Configuration List ###
AdapterList = []
NetConfList = []

### Network Interface Status ###
IF_State = ['Disabled', 'Connecting', 'Connected', 'Disconnecting', 'Hardware Not Present', \
            'Hardware Disabled', 'Hardware Malfunction', 'Disconnected', 'Media Disconnected', \
            'Authenticating', 'Authentication Succeeded', 'Authentication Failed', \
            'Invalid Address', 'Credentials Required']

### Get Network Interfaces ###
def Get_IF_Status():
    AdapterList.clear()
    EnabledAdapters = wmi.WMI().Win32_NetworkAdapter(NetEnabled=True)
    for adp in EnabledAdapters:
        AdapterList.append(adp)
    DisabledAdapters = wmi.WMI().Win32_NetworkAdapter(NetEnabled=False)
    for adp in DisabledAdapters:
        AdapterList.append(adp)
    NetConfList.clear()
    for i in range(len(AdapterList)):
        netif = wmi.WMI().Win32_NetworkAdapterConfiguration(InterfaceIndex=AdapterList[i].InterfaceIndex)
        NetConfList.append(netif)
```
WMIの情報は下記参照。
- [Win32_NetworkAdapter クラス](https://learn.microsoft.com/ja-jp/windows/win32/cimwin32prov/win32-networkadapter)
- [Win32_NetworkAdapterConfiguration クラス](https://learn.microsoft.com/ja-jp/windows/win32/cimwin32prov/win32-networkadapterconfiguration)

少々説明。
- IF_Stateはネットワークアダプタの状態（上記リンクの情報）をあらわす。
- インストールされているアダプタの内、NetEnabledに値（True or False）を持つものだけを対象とする。
- アダプタ情報をAdapterListに、IP関連情報をNetConfListに格納。

### IPアドレス情報の確認
```python
### Exsitence of Address Information ###
def IPaddrStr(idx):
    if NetConfList[idx][0].IPAddress is not None:
        str = NetConfList[idx][0].IPAddress[0]
    else:
        str = ''
    return str

def NetmaskStr(idx):
    try:
        str = NetConfList[idx][0].IPSubnet[0]
    except:
        str = ''
    return str

def GatewayStr(idx):
    try:
        str = NetConfList[idx][0].DefaultIPGateway[0]
    except:
        str = ''
    return str

def DNS1Str(idx):
    try:
        str = NetConfList[idx][0].DNSServerSearchOrder[0]
    except:
        str = ''
    return str

def DNS2Str(idx):
    try:
        str = NetConfList[idx][0].DNSServerSearchOrder[1]
    except:
        str = ''
    return str
```
- IP関連情報からアドレスなどを文字列化。
- 情報がない時は、空文字列をリターン。
    - 情報なしチェックは、「not None」か「try except」を利用。

### IPアドレスチェック
```python
### Check IP Address Format ###
def is_valid_ip(addr, flag):
    if (len(addr) == 0) and flag:
        return True
    try:
        inet_aton(addr)
        return True
    except:
        return False
```
[記事「PythonでIPアドレスの文字列表現が正しいフォーマットか確認する」](https://qiita.com/yutaka2487/items/1e0fda47081e65bf275e)を内容を少々アレンジし、入力値がなくてもフラグがTrueのときは正常とした（後述）。

### 一覧表示
```python
### Show Network Adapter List ###
def Show_List():
    all = root.grid_slaves()
    for item in all:
        item.grid_forget()	# Removal of shown items in Windows
    RdBtn = []
    selectedIF = tk.IntVar()
    selectedIF.set(1)
    btn = tk.Button(root, text = "Edit", command = lambda:Edit_Parameter(root, selectedIF.get()))
    name = tk.Label(root, text = "Interface")
    state = tk.Label(root, text = "Status")
    dhcp = tk.Label(root, text = "DHCP")
    ipaddr = tk.Label(root, text = "IP Address")
    mask = tk.Label(root, text = "Netmask")
    gateway = tk.Label(root, text = "Gateway")
    dns1 = tk.Label(root, text = "DNS server 1")
    dns2 = tk.Label(root, text = "DNS server 2")
    space = tk.Label(root, text = "	")
    refresh = tk.Button(root, text = "Refresh", command = lambda:ReShow())
    btn.grid(column = 0, row = 0)
    name.grid(column = 1, row = 0)
    state.grid(column = 2, row = 0)
    dhcp.grid(column = 3, row = 0)
    ipaddr.grid(column = 4, row = 0)
    mask.grid(column = 5, row = 0)
    gateway.grid(column = 6, row = 0)
    dns1.grid(column = 7, row = 0)
    dns2.grid(column = 8, row = 0)
    space.grid(column = 9, row = 0)
    refresh.grid(column = 10, row = 0)
    for i in range(len(NetConfList)):
        rdbutton = tk.Radiobutton(root, value=i, variable=selectedIF)
        RdBtn.append(rdbutton)
        name = tk.Label(root, text = AdapterList[i].NetConnectionID)
        try:
            str = IF_State[AdapterList[i].NetConnectionStatus]
        except:
            str = ''
        state = tk.Label(root, text = str)
        dhcp = tk.Label(root, text = ('On' if NetConfList[i][0].DHCPEnabled else 'Off'))
        ipaddr = tk.Label(root, text = IPaddrStr(i))
        mask = tk.Label(root, text = NetmaskStr(i))
        gateway = tk.Label(root, text = GatewayStr(i))
        dns1 = tk.Label(root, text = DNS1Str(i))
        dns2 = tk.Label(root, text = DNS2Str(i))
        RdBtn[i].grid(column = 0, row = i+1)
        name.grid(column = 1, row = i+1)
        state.grid(column = 2, row = i+1)
        dhcp.grid(column = 3, row = i+1)
        ipaddr.grid(column = 4, row = i+1)
        mask.grid(column = 5, row = i+1)
        gateway.grid(column = 6, row = i+1)
        dns1.grid(column = 7, row = i+1)
        dns2.grid(column = 8, row = i+1)

### ReShow Network Adapter List ###
def ReShow():
    Get_IF_Status()
    Show_List()
```
- grid_slaves()およびgrid_forget()により、画面クリア。
- ”Edit"ボタンおよび、クリックされたときの関数（Edit_Parameter）の定義。
- リストのタイトル定義。
- 再表示用のボタン定義。
- アダプタごとの情報取得および表示。
- 再表示時には、アダプタ情報を取得。

### Edit_Prameter（編集時関数）：終了処理
```python
### Edit Network Parameters ###
def Edit_Parameter(parent, idx):
    ## Click Cancel ##
    def Click_Cancel():
        dlg.destroy()
        Show_List()		# Re-List in parent windows
        
    def Close_Dialog():
        Get_IF_Status()		# Todo: Show message during this function. Thread.
        dlg.destroy()
        Show_List()		# Re-List in parent windows
```
- "Cancel"クリック時、ダイアログを閉じ、一覧リスト表示。
- ダイアログボックス正常Close指定時、アダプタ情報を再取得し、一覧リスト表示。

### Edit_Prameter：パラメータ設定
```python
    ## Set Network Patameters ##
    def Set_Parameter():
        if ctypes.windll.shell32.IsUserAnAdmin() == 0:	# Whether admin privilege or not;  1: admin 0: non-admin
            messagebox.showerror('Error', 'You need admin privilege')
            return
        if (AdapterList[idx].NetConnectionStatus != 0) and (adpState.get() == False):
            AdapterList[idx].Disable()
            Close_Dialog()
            return
        elif (AdapterList[idx].NetConnectionStatus == 0) and (adpState.get() == True):
            AdapterList[idx].Enable()
            time.sleep(5)	# It takes time to enable for 'EnableDHCP'

        dhcpset = False
        if dhcpState.get() == True:
            dhcpset = True
            if NetConfList[idx][0].DHCPEnabled == False:
                NetConfList[idx][0].SetGateways(DefaultIPGateway=[IPaddrStr(idx)])	# Removal
                NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[])	# Removal
                NetConfList[idx][0].EnableDHCP()
                # In order to remove fixed gateway information, adapter disable/enable is necessary.
                AdapterList[idx].Disable()
                AdapterList[idx].Enable()
                time.sleep(5)

        if (not is_valid_ip(ipaddrSet.get(), dhcpset)) or (not is_valid_ip(netmaskSet.get(), dhcpset)) \
           or (not is_valid_ip(gatewaySet.get(), True)) or (not is_valid_ip(dns1Set.get(), True)) \
           or (not is_valid_ip(dns2Set.get(), True)):
            messagebox.showerror('Error', 'Invalid IP address')
            Close_Dialog()
            return
            
        if dhcpset == False:
        #if (dhcpset == False) or (NetConfList[idx][0].DHCPEnabled == False):
            NetConfList[idx][0].EnableStatic(IPAddress=[ipaddrSet.get()], SubnetMask=[netmaskSet.get()])
            if len(gatewaySet.get()) > 0:
                NetConfList[idx][0].SetGateways(DefaultIPGateway=[gatewaySet.get()])

        if (len(dns1Set.get()) == 0) or (dnsdhcpState.get() == True):	# Only input of 2nd DNS is ignored.
            NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[])	# Removal
            
        if (dhcpset == False) or (dnsdhcpState.get() == False):
            if len(dns1Set.get()) > 0:	# Only input of 2nd DNS is ignored.
                if len(dns2Set.get()) == 0:
                    NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[dns1Set.get()])
                else:
                    NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[dns1Set.get(), dns2Set.get()])

        Close_Dialog()
```
- [管理者権限チェック](https://pdkz.hateblo.jp/entry/20140929/1411968951)、権限無し時にはメッセージを表示し、クローズ。
- NetConnectionStatus（[Win32_NetworkAdapter クラス](https://learn.microsoft.com/ja-jp/windows/win32/cimwin32prov/win32-networkadapter)）の値が0以外でアダプタが有効化されており、0で無効化されている。
    - 有効状態から無効に変更されたときは、メソッドDisable()をCallしリターン。
    - 無効状態から有効に変更されたときは、メソッドEnaable()をCall。有効化には時間がかかるようなので、5秒程待つ。
- DHCPによるIPアドレス取得（dhcpState.get() == True）、かつ、現在の状態が固定IPアドレス設定（DHCPEnabled == False）
    - 現在のIPアドレスをGatewayに設定するとGateway設定がクリア（[Win32_NetworkAdapterConfigurationクラス](https://learn.microsoft.com/ja-jp/windows/win32/cimwin32prov/win32-networkadapterconfiguration)記載より）。
        - ただし不具合があるようで、アダプタの無効化→有効化しないと完全にはクリアされないようだ。
    - DNSサーバーアドレスの削除。
    - DHCPによるIPアドレス取得有効化（EnableDHCP()）。
- 無効なIPアドレス形式入力時にはメッセージを表示。
    - ただし、DHCPからIPアドレス取得時のIPアドレスなどの空入力はOKとしている。
- 固定IPアドレス設定時には、EnableStatic()およびSetGateways()により、アドレスを設定する。
- DNSサーバーaddressを適宜設定する（1stが未入力の場合は2ndの入力があっても無視している）。
- ダイアログボックスのクローズ。

### Edit_Prameter：編集時の各種On/Off選択によるアドレスなどの入力有効・無効および他のOn/Off制御
```python
    ## Control items ##
    def addr_enabled():
        ipaddrSet['state'] = 'normal'
        netmaskSet['state'] = 'normal'
        gatewaySet['state'] = 'normal'
        
    def addr_disabled():
        ipaddrSet['state'] = 'disabled'
        netmaskSet['state'] = 'disabled'
        gatewaySet['state'] = 'disabled'

    def dns_enabled():
        dns1Set['state'] = 'normal'
        dns2Set['state'] = 'normal'

    def dns_disabled():
        dns1Set['state'] = 'disabled'
        dns2Set['state'] = 'disabled'

    def all_disabled():
        dhcpON['state'] = 'disabled'
        dhcpOFF['state'] = 'disabled'
        addr_disabled()
        dnsdhcpON['state'] = 'disabled'
        dnsdhcpOFF['state'] = 'disabled'
        dns_disabled()
```
- アダプタの無効化選択時には、全てをOff、すべての入力のを無効化するなどの制御。
- DHCPによるIPアドレス取得を選択時には、IPアドレス入力を無効化するなどの制御。

### Editダイアログボックス：さきほどの制御の続き
```python
    ## Click items ##
    def adp_click():
        if adpState.get() == True:
            dhcpON['state'] = 'normal'
            dhcpOFF['state'] = 'normal'
            dhcp_click()
        else:
            all_disabled()

    def dnsdhcp_click():
        if dnsdhcpState.get() == True:
            dns_disabled()
        else:
            dns_enabled()

    def dhcp_click():
        if dhcpState.get() == True:
            addr_disabled();
            dnsdhcpON['state'] = 'normal'
            dnsdhcpOFF['state'] = 'normal'
            dnsdhcpState.set(True)
        else:
            addr_enabled()
            dnsdhcpON['state'] = 'disabled'
            dnsdhcpOFF['state'] = 'disabled'
            dnsdhcpState.set(False)
        dnsdhcp_click()
 ```
- 一つ前の記載参照。

### Editダイアログボックス：モーダルダイアログ
```python
    ## Start of DialogBox ##
    dlg = tk.Toplevel(parent)
    dlg.title('Edit: ' + AdapterList[idx].NetConnectionID)
    dlg.geometry("300x300")

    dlg.grab_set()
    dlg.focus_set()
```
ここは各種サイトで見つかる情報をそのまま流用。

### Editダイアログボックス：ネットワークアダプタ情報およびIP関連情報の変数化
```python
    ## Get Current Information ##
    adpState = tk.BooleanVar()
    adpState.set(True if AdapterList[idx].NetConnectionStatus != 0 else False)	# 0:Disabled
    adp = tk.Label(dlg, text = 'Adapter')
    adpON = tk.Radiobutton(dlg, text='On', variable=adpState, command=lambda:adp_click(), value=True)
    adpOFF = tk.Radiobutton(dlg, text='Off', variable=adpState, command=lambda:adp_click(), value=False)
    dhcpState = tk.BooleanVar()
    dhcpState.set(NetConfList[idx][0].DHCPEnabled)
    dhcp = tk.Label(dlg, text = 'DHCP')
    dhcpON = tk.Radiobutton(dlg, text='On', variable=dhcpState, command=lambda:dhcp_click(), value=True)
    dhcpOFF = tk.Radiobutton(dlg, text='Off', variable=dhcpState, command=lambda:dhcp_click(), value=False)
    ipaddrLabel = tk.Label(dlg, text='IP Address')
    ipaddrSet = tk.Entry(dlg, width=20)
    ipaddrSet.insert(0, IPaddrStr(idx))
    netmaskLabel = tk.Label(dlg, text='Netmask')
    netmaskSet = tk.Entry(dlg, width=20)
    netmaskSet.insert(0, NetmaskStr(idx))
    gatewayLabel = tk.Label(dlg, text='Gateway')
    gatewaySet = tk.Entry(dlg, width=20)
    gatewaySet.insert(0, GatewayStr(idx))
    dnsdhcpState = tk.BooleanVar()
    dnsdhcpState.set(NetConfList[idx][0].DHCPEnabled)	# Todo:  No parameter in WIM
    dnsDHCP = tk.Label(dlg, text = 'DNS from DHCP')
    dnsdhcpON = tk.Radiobutton(dlg, text='On', variable=dnsdhcpState, command=lambda:dnsdhcp_click(), value=True)
    dnsdhcpOFF = tk.Radiobutton(dlg, text='Off', variable=dnsdhcpState, command=lambda:dnsdhcp_click(), value=False)
    dns1Label = tk.Label(dlg, text='DNS Server 1')
    dns1Set = tk.Entry(dlg, width=20)
    dns1Set.insert(0, DNS1Str(idx))
    dns2Label = tk.Label(dlg, text='DNS Server 2')
    dns2Set = tk.Entry(dlg, width=20)
    dns2Set.insert(0, DNS2Str(idx))
```
- 現在のアダプタ情報およびIP関連情報をもとに変数化。
    - ラジオボタンはBoolean。
    - アドレスなどの入力はEntry（テキスト）。
  
### Editダイアログボックス：表示
```python
    ## Display information ##
    adp.grid(column = 0, row = 0)
    adpON.grid(column = 1, row = 0)
    adpOFF.grid(column = 2, row = 0)
    if adpState.get() == False:
        all_disabled()
    dhcp.grid(column = 0, row = 1)
    dhcpON.grid(column = 1, row = 1)
    dhcpOFF.grid(column = 2, row = 1)
    if dhcpState.get() == True:
        addr_disabled()
        dns_disabled()
    ipaddrLabel.grid(column = 0, row = 2)
    ipaddrSet.grid(column = 2, row = 2)
    netmaskLabel.grid(column = 0, row = 3)
    netmaskSet.grid(column = 2, row = 3)
    gatewayLabel.grid(column = 0, row = 4)
    gatewaySet.grid(column = 2, row = 4)
    dnsDHCP.grid(column = 0, row = 5)
    dnsdhcpON.grid(column = 1, row = 5)
    dnsdhcpOFF.grid(column = 2, row = 5)
    dns1Label.grid(column = 0, row = 6)
    dns1Set.grid(column = 2, row = 6)
    dns2Label.grid(column = 0, row = 7)
    dns2Set.grid(column = 2, row = 7)
    ## Button ##
    ApplyBtn = tk.Button(dlg, text = "Apply", command = lambda:Set_Parameter())
    CloseBtn = tk.Button(dlg, text = "Cancel", command = lambda:Click_Cancel())
    ApplyBtn.grid(column = 0, row = 8)
    CloseBtn.grid(column = 2, row = 8)
    dlg.protocol("WM_DELETE_WINDOW", lambda:Click_Cancel())
```
- Editダイアログボックスへの表示。
    - gridによる表示。
    - ボタンクリック時の処理（間数）定義。

### メインプログラム
```python
##### Main Program #####
root = tk.Tk()
root.title("Network Interface Configuration")
root.geometry("950x300")

Get_IF_Status()
Show_List()

root.mainloop()
```
- tkinterお決まりのプログラム開始部分。
- アダプタおよびネットワーク情報取得。
- 情報のリスト表示。

### 全体
```python
# -*- coding: utf-8 -*-
import wmi
import ctypes
import time
from socket import inet_aton
import tkinter as tk
from tkinter import messagebox

### Network Adapter and Configuration List ###
AdapterList = []
NetConfList = []

### Network Interface Status ###
IF_State = ['Disabled', 'Connecting', 'Connected', 'Disconnecting', 'Hardware Not Present', \
            'Hardware Disabled', 'Hardware Malfunction', 'Disconnected', 'Media Disconnected', \
            'Authenticating', 'Authentication Succeeded', 'Authentication Failed', \
            'Invalid Address', 'Credentials Required']

### Get Network Interfaces ###
def Get_IF_Status():
    AdapterList.clear()
    EnabledAdapters = wmi.WMI().Win32_NetworkAdapter(NetEnabled=True)
    for adp in EnabledAdapters:
        AdapterList.append(adp)
    DisabledAdapters = wmi.WMI().Win32_NetworkAdapter(NetEnabled=False)
    for adp in DisabledAdapters:
        AdapterList.append(adp)
    NetConfList.clear()
    for i in range(len(AdapterList)):
        netif = wmi.WMI().Win32_NetworkAdapterConfiguration(InterfaceIndex=AdapterList[i].InterfaceIndex)
        NetConfList.append(netif)

### Exsitence of Address Information ###
def IPaddrStr(idx):
    if NetConfList[idx][0].IPAddress is not None:
        str = NetConfList[idx][0].IPAddress[0]
    else:
        str = ''
    return str

def NetmaskStr(idx):
    try:
        str = NetConfList[idx][0].IPSubnet[0]
    except:
        str = ''
    return str

def GatewayStr(idx):
    try:
        str = NetConfList[idx][0].DefaultIPGateway[0]
    except:
        str = ''
    return str

def DNS1Str(idx):
    try:
        str = NetConfList[idx][0].DNSServerSearchOrder[0]
    except:
        str = ''
    return str

def DNS2Str(idx):
    try:
        str = NetConfList[idx][0].DNSServerSearchOrder[1]
    except:
        str = ''
    return str

### Check IP Address Format ###
def is_valid_ip(addr, flag):
    if (len(addr) == 0) and flag:
        return True
    try:
        inet_aton(addr)
        return True
    except:
        return False

### Show Network Adapter List ###
def Show_List():
    all = root.grid_slaves()
    for item in all:
        item.grid_forget()	# Removal of shown items in Windows
    RdBtn = []
    selectedIF = tk.IntVar()
    selectedIF.set(1)
    btn = tk.Button(root, text = "Edit", command = lambda:Edit_Parameter(root, selectedIF.get()))
    name = tk.Label(root, text = "Interface")
    state = tk.Label(root, text = "Status")
    dhcp = tk.Label(root, text = "DHCP")
    ipaddr = tk.Label(root, text = "IP Address")
    mask = tk.Label(root, text = "Netmask")
    gateway = tk.Label(root, text = "Gateway")
    dns1 = tk.Label(root, text = "DNS server 1")
    dns2 = tk.Label(root, text = "DNS server 2")
    space = tk.Label(root, text = "	")
    refresh = tk.Button(root, text = "Refresh", command = lambda:ReShow())
    btn.grid(column = 0, row = 0)
    name.grid(column = 1, row = 0)
    state.grid(column = 2, row = 0)
    dhcp.grid(column = 3, row = 0)
    ipaddr.grid(column = 4, row = 0)
    mask.grid(column = 5, row = 0)
    gateway.grid(column = 6, row = 0)
    dns1.grid(column = 7, row = 0)
    dns2.grid(column = 8, row = 0)
    space.grid(column = 9, row = 0)
    refresh.grid(column = 10, row = 0)
    for i in range(len(NetConfList)):
        rdbutton = tk.Radiobutton(root, value=i, variable=selectedIF)
        RdBtn.append(rdbutton)
        name = tk.Label(root, text = AdapterList[i].NetConnectionID)
        try:
            str = IF_State[AdapterList[i].NetConnectionStatus]
        except:
            str = ''
        state = tk.Label(root, text = str)
        dhcp = tk.Label(root, text = ('On' if NetConfList[i][0].DHCPEnabled else 'Off'))
        ipaddr = tk.Label(root, text = IPaddrStr(i))
        mask = tk.Label(root, text = NetmaskStr(i))
        gateway = tk.Label(root, text = GatewayStr(i))
        dns1 = tk.Label(root, text = DNS1Str(i))
        dns2 = tk.Label(root, text = DNS2Str(i))
        RdBtn[i].grid(column = 0, row = i+1)
        name.grid(column = 1, row = i+1)
        state.grid(column = 2, row = i+1)
        dhcp.grid(column = 3, row = i+1)
        ipaddr.grid(column = 4, row = i+1)
        mask.grid(column = 5, row = i+1)
        gateway.grid(column = 6, row = i+1)
        dns1.grid(column = 7, row = i+1)
        dns2.grid(column = 8, row = i+1)

### ReShow Network Adapter List ###
def ReShow():
    Get_IF_Status()
    Show_List()
    
### Edit Network Parameters ###
def Edit_Parameter(parent, idx):
    ## Click Cancel ##
    def Click_Cancel():
        dlg.destroy()
        Show_List()		# Re-List in parent windows
        
    def Close_Dialog():
        Get_IF_Status()		# Todo: Show message during this function. Thread.
        dlg.destroy()
        Show_List()		# Re-List in parent windows
        
    ## Set Network Patameters ##
    def Set_Parameter():
        if ctypes.windll.shell32.IsUserAnAdmin() == 0:	# Whether admin privilege or not;  1: admin 0: non-admin
            messagebox.showerror('Error', 'You need admin privilege')
            return
        if (AdapterList[idx].NetConnectionStatus != 0) and (adpState.get() == False):
            AdapterList[idx].Disable()
            Close_Dialog()
            return
        elif (AdapterList[idx].NetConnectionStatus == 0) and (adpState.get() == True):
            AdapterList[idx].Enable()
            time.sleep(5)	# It takes time to enable for 'EnableDHCP'

        dhcpset = False
        if dhcpState.get() == True:
            dhcpset = True
            if NetConfList[idx][0].DHCPEnabled == False:
                NetConfList[idx][0].SetGateways(DefaultIPGateway=[IPaddrStr(idx)])	# Removal
                NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[])	# Removal
                NetConfList[idx][0].EnableDHCP()
                # In order to remove fixed gateway information, adapter disable/enable is necessary.
                AdapterList[idx].Disable()
                AdapterList[idx].Enable()
                time.sleep(5)

        if (not is_valid_ip(ipaddrSet.get(), dhcpset)) or (not is_valid_ip(netmaskSet.get(), dhcpset)) \
           or (not is_valid_ip(gatewaySet.get(), True)) or (not is_valid_ip(dns1Set.get(), True)) \
           or (not is_valid_ip(dns2Set.get(), True)):
            messagebox.showerror('Error', 'Invalid IP address')
            Close_Dialog()
            return
            
        if dhcpset == False:
        #if (dhcpset == False) or (NetConfList[idx][0].DHCPEnabled == False):
            NetConfList[idx][0].EnableStatic(IPAddress=[ipaddrSet.get()], SubnetMask=[netmaskSet.get()])
            if len(gatewaySet.get()) > 0:
                NetConfList[idx][0].SetGateways(DefaultIPGateway=[gatewaySet.get()])

        if (len(dns1Set.get()) == 0) or (dnsdhcpState.get() == True):	# Only input of 2nd DNS is ignored.
            NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[])	# Removal
            
        if (dhcpset == False) or (dnsdhcpState.get() == False):
            if len(dns1Set.get()) > 0:	# Only input of 2nd DNS is ignored.
                if len(dns2Set.get()) == 0:
                    NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[dns1Set.get()])
                else:
                    NetConfList[idx][0].SetDNSServerSearchOrder(DNSServerSearchOrder=[dns1Set.get(), dns2Set.get()])

        Close_Dialog()
                
    ## Control items ##
    def addr_enabled():
        ipaddrSet['state'] = 'normal'
        netmaskSet['state'] = 'normal'
        gatewaySet['state'] = 'normal'
        
    def addr_disabled():
        ipaddrSet['state'] = 'disabled'
        netmaskSet['state'] = 'disabled'
        gatewaySet['state'] = 'disabled'

    def dns_enabled():
        dns1Set['state'] = 'normal'
        dns2Set['state'] = 'normal'

    def dns_disabled():
        dns1Set['state'] = 'disabled'
        dns2Set['state'] = 'disabled'

    def all_disabled():
        dhcpON['state'] = 'disabled'
        dhcpOFF['state'] = 'disabled'
        addr_disabled()
        dnsdhcpON['state'] = 'disabled'
        dnsdhcpOFF['state'] = 'disabled'
        dns_disabled()

    ## Click items ##
    def adp_click():
        if adpState.get() == True:
            dhcpON['state'] = 'normal'
            dhcpOFF['state'] = 'normal'
            dhcp_click()
        else:
            all_disabled()

    def dnsdhcp_click():
        if dnsdhcpState.get() == True:
            dns_disabled()
        else:
            dns_enabled()

    def dhcp_click():
        if dhcpState.get() == True:
            addr_disabled();
            dnsdhcpON['state'] = 'normal'
            dnsdhcpOFF['state'] = 'normal'
            dnsdhcpState.set(True)
        else:
            addr_enabled()
            dnsdhcpON['state'] = 'disabled'
            dnsdhcpOFF['state'] = 'disabled'
            dnsdhcpState.set(False)
        dnsdhcp_click()
        
    ## Start of DialogBox ##
    dlg = tk.Toplevel(parent)
    dlg.title('Edit: ' + AdapterList[idx].NetConnectionID)
    dlg.geometry("300x300")

    dlg.grab_set()
    dlg.focus_set()

    ## Get Current Information ##
    adpState = tk.BooleanVar()
    adpState.set(True if AdapterList[idx].NetConnectionStatus != 0 else False)	# 0:Disabled
    adp = tk.Label(dlg, text = 'Adapter')
    adpON = tk.Radiobutton(dlg, text='On', variable=adpState, command=lambda:adp_click(), value=True)
    adpOFF = tk.Radiobutton(dlg, text='Off', variable=adpState, command=lambda:adp_click(), value=False)
    dhcpState = tk.BooleanVar()
    dhcpState.set(NetConfList[idx][0].DHCPEnabled)
    dhcp = tk.Label(dlg, text = 'DHCP')
    dhcpON = tk.Radiobutton(dlg, text='On', variable=dhcpState, command=lambda:dhcp_click(), value=True)
    dhcpOFF = tk.Radiobutton(dlg, text='Off', variable=dhcpState, command=lambda:dhcp_click(), value=False)
    ipaddrLabel = tk.Label(dlg, text='IP Address')
    ipaddrSet = tk.Entry(dlg, width=20)
    ipaddrSet.insert(0, IPaddrStr(idx))
    netmaskLabel = tk.Label(dlg, text='Netmask')
    netmaskSet = tk.Entry(dlg, width=20)
    netmaskSet.insert(0, NetmaskStr(idx))
    gatewayLabel = tk.Label(dlg, text='Gateway')
    gatewaySet = tk.Entry(dlg, width=20)
    gatewaySet.insert(0, GatewayStr(idx))
    dnsdhcpState = tk.BooleanVar()
    dnsdhcpState.set(NetConfList[idx][0].DHCPEnabled)	# Todo:  No parameter in WIM
    dnsDHCP = tk.Label(dlg, text = 'DNS from DHCP')
    dnsdhcpON = tk.Radiobutton(dlg, text='On', variable=dnsdhcpState, command=lambda:dnsdhcp_click(), value=True)
    dnsdhcpOFF = tk.Radiobutton(dlg, text='Off', variable=dnsdhcpState, command=lambda:dnsdhcp_click(), value=False)
    dns1Label = tk.Label(dlg, text='DNS Server 1')
    dns1Set = tk.Entry(dlg, width=20)
    dns1Set.insert(0, DNS1Str(idx))
    dns2Label = tk.Label(dlg, text='DNS Server 2')
    dns2Set = tk.Entry(dlg, width=20)
    dns2Set.insert(0, DNS2Str(idx))

    ## Display information ##
    adp.grid(column = 0, row = 0)
    adpON.grid(column = 1, row = 0)
    adpOFF.grid(column = 2, row = 0)
    if adpState.get() == False:
        all_disabled()
    dhcp.grid(column = 0, row = 1)
    dhcpON.grid(column = 1, row = 1)
    dhcpOFF.grid(column = 2, row = 1)
    if dhcpState.get() == True:
        addr_disabled()
        dns_disabled()
    ipaddrLabel.grid(column = 0, row = 2)
    ipaddrSet.grid(column = 2, row = 2)
    netmaskLabel.grid(column = 0, row = 3)
    netmaskSet.grid(column = 2, row = 3)
    gatewayLabel.grid(column = 0, row = 4)
    gatewaySet.grid(column = 2, row = 4)
    dnsDHCP.grid(column = 0, row = 5)
    dnsdhcpON.grid(column = 1, row = 5)
    dnsdhcpOFF.grid(column = 2, row = 5)
    dns1Label.grid(column = 0, row = 6)
    dns1Set.grid(column = 2, row = 6)
    dns2Label.grid(column = 0, row = 7)
    dns2Set.grid(column = 2, row = 7)
    ## Button ##
    ApplyBtn = tk.Button(dlg, text = "Apply", command = lambda:Set_Parameter())
    CloseBtn = tk.Button(dlg, text = "Cancel", command = lambda:Click_Cancel())
    ApplyBtn.grid(column = 0, row = 8)
    CloseBtn.grid(column = 2, row = 8)
    dlg.protocol("WM_DELETE_WINDOW", lambda:Click_Cancel())

##### Main Program #####
root = tk.Tk()
root.title("Network Interface Configuration")
root.geometry("950x300")

Get_IF_Status()
Show_List()

root.mainloop()
```

EOF