pythontkinterpingtraceroute

Realtime Ping or Trace output to Tkinter Window


I have a working UI that I can initiate a ping command with. In the UI I have an output window. Currently when I click the button to ping there is a small delay and the ping responses then show in the output window. I'm wondering if there is a way to have the responses appear in realtime? Especially for something like a trace route where each hop will appear.

This is the ping code:

def button_ping():
    hostname = info.get()
    response = os.popen("ping " + hostname).readlines()
    print_out.delete("1.0", "end")
    print_out.insert("end-1c",response)

Entire code is:

import tkinter
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import *
from tkinter.scrolledtext import ScrolledText
import subprocess
import requests
import urllib3
import os
import re
from requests.auth import HTTPBasicAuth

def button_ping():
    hostname = info.get()
    response = os.popen("ping " + hostname).readlines()
    print_out.delete("1.0", "end")
    print_out.insert("end-1c",response)


#GUI SECTION
#WINDOW PARAMETERS
root = Tk()
root.title("Steve's Network Tool")
#root.iconbitmap('icon.ico')
root.geometry("550x550")
root.resizable(False, False)

#WINDOW MENU OPTIONS
def about():
    messagebox.showinfo('Version History', 'Version 1.0 Test Edition')

menubar = Menu(root)
file = Menu(menubar, tearoff=0)
file.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=file)
help = Menu(menubar, tearoff=0)
help.add_command(label="About", command=about)
menubar.add_cascade(label="Help", menu=help)


#INPUT FIELDS - PASSWORD CHARACTERS REPLACED WITH '*'
#LEFT FRAME
frame0 = Frame(root)
frame0.grid(row=1)

tkinter.Label(frame0, text="Enter Input:").grid(row=1)
info = tkinter.Entry(frame0)
info.grid(row=1, column=1)

root_label = tkinter.Label(frame0, text="Network Tasks:", pady=5)
root_label.grid(row=3, column=3)

#NETWORK BUTTONS - ROW3
#GET DEVICE ID
button_get_id = Button(frame0, text="Ping", command=button_ping)
button_get_id.grid(row=3, column=4, padx=0)
#DELETE ID
#button_delete_id = Button(frame0, text="Trace", command=button_trace)
#button_delete_id.grid(row=3, column=5, padx=0)

#ACTION BUTTONS & OUTPUT WINDOW
#FRAME3
frame3 = Frame(root)
frame3.grid(row=9, pady=10)

#OUTPUT WINDOW
print_out = ScrolledText(frame3, height=20, width=50, bg="Black", fg='yellow')
print_out.grid(row=3, column=1)

#Copy Text Button
def copy_text_to_clipboard():
    #root.clipboard_clear()  # clear clipboard contents
    root.clipboard_append(print_out.get("1.0", tkinter.END).rstrip())  # append new value to clipboard

#COPY TO CLIPBOARD BUTTON
button_copy = Button(frame3, text="Copy To Clipboard", command=copy_text_to_clipboard)
button_copy.grid(row=11, column=1)

#CLEAR OUTPUT WINDOW
def clear():
    print_out.delete("1.0","end")
button_copy = Button(frame3,text="Clear Output", command=clear)
button_copy.grid(row=12, column=1)

#EXIT PROGRAM BUTTON
button_quit = Button(frame3, text="Exit", command=root.quit)
button_quit.grid(row=13, column=1)


root.config(menu=menubar)
root.mainloop()

Many thanks in advance


Solution

  • You can use after loop (using .after()) to read the output line by line and insert the output into the text box:

    def read_output(fp):
        line = fp.readline()
        if line:
            print_out.insert("end", line)
            print_out.see("end")
            print_out.after(10, read_output, fp)
    
    def button_ping():
        hostname = info.get()
        fp = os.popen(f"ping {hostname}")
        read_output(fp)
    

    Note that it is recommended to use subprocess.Popen() instead of os.popen().