By Glenn McCallum

Tkinter python GUI package

I decided to teach myself Tkinter and build a little program to help quickly search and ping/connect to networking devices. The tkinter package (“Tk interface”) is the standard Python interface to the Tcl/Tk GUI toolkit. Both Tk and tkinter are available on most Unix platforms, including macOS, as well as on Windows systems.

End result we will make

What this post will end up making

So tkinter has some learning curve but ideally you just want to layout your program in boxes and then fill those items with buttons, text boxes and anything you want to build. You can see a more detailed documentation here.

This little program grew as I learnt more and more about layouts and positioning and was built to be packaged up and run on a Windows box as the buttons run commands on Windows OS. I have added a few devices into the CSV file as a placeholder but this helped our team search for devices across all the customers we managed.

I split up the program into 3 sections

  1. top-Frame (orange)
  2. Centre-Frame split into
    1. ctr-left (red)
    2. ctr-right (red)
  3. Bottom Frame (blue)

I will break down the code sections below and there is full code on my github https://github.com/glennmccallum/tkinter-nms-helper

Code

from tkinter import *
from tkinter import ttk
import pandas as pd
import os

This code is imported to run tkinter and pandas to read in the csv

#----main----#
host = ""
# Read in csv using pandas
devices = pd.read_csv("all_devices.csv")

# Get All hostnames in a sorted List
hostnames = devices['Hostname']
hostnames = list(hostnames)
hostnames = sorted(hostnames)
# Get All Companies in a sorted List
companies = devices['Company'].unique()
companies = list(companies)
companies = sorted(companies)

This is reading in the csv using pandas and then extracing and sorting the hostnames and customers in alphabetical order.

# Initalise Tkinter
root = Tk()
# Title on window
root.title('NMS HELPER 2.0')
# favicon for window
root.iconbitmap("favicon.ico")
# Size of window
root.geometry('{}x{}'.format(460, 375))

Setup the window using tkinter, naming the, adding a favicon and sizing the window

# create all of the main containers
top_frame = Frame(root, bg='#333333', width=450, height=50, pady=3)
center = Frame(root, bg='gray2', width=50, height=40, padx=3, pady=3)
btm_frame = Frame(root, bg='#333333', width=450, height=45, pady=3)

# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

# Set the way window can expand
top_frame.grid(row=0, sticky="ew")
center.grid(row=1, sticky="nsew")
btm_frame.grid(row=3, sticky="ew")

This sets up the top, centre and bottom frames of the window

# Create the widgets for the top frame
# Search Label and place in topframe
searchLabel = Label(top_frame, text="Filter Hostname:", bg='#333333', fg="grey")
# Entry textbox for user to filter results in topframe
entry = Entry(top_frame, bg='grey', font=(12))
# give curser focus 
entry.focus()
# when user adds charactrs to filter pass to on_keyrelease function
entry.bind('<KeyRelease>', on_keyrelease)
# Clear search Button assigned to top frame, if pressed pass to clearEntry function
clearSearch = Button(top_frame, text = "Clear Search", command = clearEntry)
# Company seach dropdown comboxbox in top frame
comboBoxLabel = Label(top_frame, text="Filter Company:", bg='#333333', fg="grey")
# Fill the combobox with companies
comboBox = ttk.Combobox(top_frame, values=companies)
# If user selects a company pass to callbackFunc to filter hostnames
comboBox.bind("<<ComboboxSelected>>", callbackFunc)

# layout the widgets in the top frame
searchLabel.grid(row=0, column=0)
entry.grid(row=0, column=1)
clearSearch.grid(row=0, column=2, padx=3, pady=3)
comboBoxLabel.grid(row=1, column=0)
comboBox.grid(row=1, column=1)

This is setting up widgets in Top Frame

# create the center widgets row and columns 
center.grid_rowconfigure(0, weight=1)
center.grid_columnconfigure(0, weight=1)
center.grid_columnconfigure(1, weight=1)
center.grid_columnconfigure(2, weight=4)

# layout the widgets in center frame
ctr_left = Frame(center, bg='#333333')
ctr_mid = Frame(center, bg='#333333')
ctr_right = Frame(center, bg='#333333', padx=3, pady=3)
ctr_left.grid(row=0, column=0, sticky="nsew")
ctr_mid.grid(row=0, column=1, sticky="nsew")
ctr_right.grid(row=0, column=2, sticky="nsew")

# create the widgets in the ctr_left Frame
scrollbar = Scrollbar(ctr_left)
# Make listbox with a black background and grey text
listbox = Listbox(ctr_left, bd=0, background="black", fg="grey",selectbackground="grey", font = (12))

# layout the scrollbar and listbox and attach in the ctr_left Frame
scrollbar.pack(side=RIGHT, fill=Y)
listbox.pack(fill = BOTH, expand=True)
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)
# For debugging to show selection was selected in terminal
listbox.bind('<<ListboxSelect>>', on_select)

This sets up the centre_left frame with listbox to list the devices and attach the scrollbar to listbox.

# Show all hostnames when first opening program
listbox_update(hostnames)

shows all the hostnames in the list box when starting the program, listbox_update function explained in a bit

# create the widgets in the centre Right Frame to display hostname details
hostnameLabel = Label(ctr_mid, text="HOSTNAME:", bg='#333333', fg="grey",font = (12))
hostname_entry = Entry(ctr_right, text="",bg='grey',font = (12))

# layout the widgets in the ctr_right Frame
hostnameLabel.pack(fill = X, expand=True)
hostname_entry.pack(fill = X, expand=True)

I wont list all of the text fields but this is a snapshot of how I created the labels and the fields to be dynamically populated

# create the buttons in the Bottom Frame to send to each function to action
ping = Button(btm_frame, text = "PING", command = pingDevice, font = (12))
ssh = Button(btm_frame, text = "SSH", command = sshDevice, font = (12))
telnet = Button(btm_frame, text = "TELNET", command = telnetDevice, font = (12))
http = Button(btm_frame, text = "HTTP", command = httpDevice, font = (12))
https = Button(btm_frame, text = "HTTPS", command = httpsDevice, font = (12))
vnc = Button(btm_frame, text = "VNC", command = vncDevice, font = (12))

# layout the widgets in the Bottom Frame
ping.grid(row=0, column=1, padx=3, pady=3)
ssh.grid(row=0, column=2, padx=3, pady=3)
telnet.grid(row=0, column=3, padx=3, pady=3)
http.grid(row=0, column=4, padx=3, pady=3)
https.grid(row=0, column=5, padx=3, pady=3)
vnc.grid(row=0, column=6, padx=3, pady=3)

Create the buttons in the bottom frame

# run the program
root.mainloop()

Run the program

Functions called from the above code

# Keep filtering listbox when user enters characters in textbox
def on_keyrelease(event):

    # Get value from Entrybox
    value = event.widget.get()
    # Strip whitespaces and convert to lower case
    value = value.strip().lower()

    # If nothing entered display all hostnames
    if value == '':
        data = hostnames
    # else serach hostnames and display any that match string
    else:
        data = []
        for item in hostnames:
            if value in item.lower():
                data.append(item)                

    # Update hostanmes in listbox
    listbox_update(data)

This function was binding to textbox when user typed in to filter the list of devices after each key is pressed it is filters the listbox

# Used to populate the listbox from data entered
def listbox_update(data):
    # delete previous data
    listbox.delete(0, 'end')

    # sorting data 
    data = sorted(data, key=str.lower)

    # put new data
    for item in data:
        listbox.insert('end', item)

listbox_update populates the listbox from the data entered in the text box

# For debugging purposes to show active seclection in terminal
def on_select(event):
    # display element selected on list
    print('(event) previous:', event.widget.get('active'))
    print('(event)  current:', event.widget.get(event.widget.curselection()))
    print('---')
    infoDevice()

Just a debug function to see what the user selected in the terminal

# Get IP of selection from CSV file
def getSelection():
    # Get Current Selection
    selected = listbox.curselection()
    # Get the value of selection
    for item in selected:
        host = listbox.get(item)
    # From selected hostname get the IP from CSV file
    ip = devices.loc[devices['Hostname'] == host, 'IP'].item()

    #return the IP of selected device
    return ip

Once user clicks on a hostname it them looks up the device in csv and returns the IP address

# Used for clear search button to clear all fields
def clearEntry():
    # clear search
    entry.delete(0, 'end')
    # clear data in textbox and update listbox
    value = ''
    data = hostnames
    listbox_update(data)
    # leave curser in focus in textbox
    entry.focus()

    # clear out all device infomration fields
    hostname_entry.delete(0,END)
    natIp_entry.delete(0,END)
    custIp_entry.delete(0,END)
    customer_entry.delete(0,END)
    model_entry.delete(0,END)
    serial_entry.delete(0,END)
    circuit_entry.delete(0,END)
    site_entry.delete(0,END)
    critical_entry.delete(0,END)
    sla_entry.delete(0,END)
    version_entry.delete(0,END)

    #Clear out dropdown cobo box
    comboBox.set('')

Used when you click on the clear button so it resets the filter and clears out all fields

# used for Ping Button
def pingDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and ping IP
    os.system("start cmd /k ping " + ip + " -t")

# used for SSH Button
def sshDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and SSH to IP using Putty
    os.system("start cmd /k putty.exe -ssh " + ip + " 22")

# used for TELNET Button
def telnetDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and telnet to IP using Putty
    os.system("start cmd /k putty.exe -telnet " + ip + " 23")

# used for HTTP Button
def httpDevice():
    # Get IP from selection
    ip = getSelection()
    # Start browser and http to IP
    url = "http://" + ip
    os.startfile(url)

# used for HTTPS Button
def httpsDevice():
    # Get IP from selection
    ip = getSelection()
    # Start browser and https to IP
    url = "https://" + ip
    os.startfile(url)

# used for VNC Button
def vncDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and telnet to IP using Putty
    os.system("start cmd /k vncviewer.exe " + ip)

The functions when you click on each of the buttons. This was built for Windows box so either runs a ping using command prompt or putty.exe for ssh and telnet, opens browser for http and https and uses vncviewer.exe to vnc to host.

# used to get info from CSV for selected device
def infoDevice():
    # Get Current Selection
    selected = listbox.curselection()

    # Get the value of selection
    for item in selected:
        host = listbox.get(item)

    # Get all fields required from CSV
    ip = devices.loc[devices['Hostname'] == host, 'IP'].item()
    cust_ip = devices.loc[devices['Hostname'] == host, 'Customer IP'].item()
    customer = devices.loc[devices['Hostname'] == host, 'Company'].item()
    models = devices.loc[devices['Hostname'] == host, 'Model'].item()
    sn = devices.loc[devices['Hostname'] == host, 'Serial number'].item()
    circuit = devices.loc[devices['Hostname'] == host, 'Circuits'].item()
    site = devices.loc[devices['Hostname'] == host, 'Site'].item()
    critical = devices.loc[devices['Hostname'] == host, 'Critical'].item()
    sla = devices.loc[devices['Hostname'] == host, 'SLA'].item()
    version = devices.loc[devices['Hostname'] == host, 'OS Version'].item()

    # Display results in text fields
    hostname_entry.delete(0,END)
    hostname_entry.insert(0,host)
    natIp_entry.delete(0,END)
    natIp_entry.insert(0,ip)
    custIp_entry.delete(0,END)
    custIp_entry.insert(0,cust_ip)
    customer_entry.delete(0,END)
    customer_entry.insert(0,customer)
    model_entry.delete(0,END)
    model_entry.insert(0,models)
    serial_entry.delete(0,END)
    serial_entry.insert(0,sn)
    circuit_entry.delete(0,END)
    circuit_entry.insert(0,circuit)
    site_entry.delete(0,END)
    site_entry.insert(0,site)
    critical_entry.delete(0,END)
    critical_entry.insert(0,critical)
    sla_entry.delete(0,END)
    sla_entry.insert(0,sla)
    version_entry.delete(0,END)
    version_entry.insert(0,version)

Used to populate the fields about the device once user selects it

# used for Dropdown when selecting a specific company
# display only hostnames in that company
def callbackFunc(event):
    company_Selected = comboBox.get()
    print (company_Selected)
    # locate all hostnames under that copany selected
    data = devices.loc[devices.Company == company_Selected, 'Hostname']
    # update data in listbox
    listbox_update(data)

Used to display only hosts that are related to the customer user picks from the dropdown list

This started out as a learning experience about tkinter and kept evolving so there might be a better way to set out the layout but this worked for me.

Please find the full code on my github https://github.com/glennmccallum/tkinter-nms-helper

Share this:
Python, Scripting 0

Leave a Reply

Your email address will not be published. Required fields are marked *