Python Forum
[Tkinter] How to get the result of a ping to show in tkinter? - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: [Tkinter] How to get the result of a ping to show in tkinter? (/thread-18983.html)



How to get the result of a ping to show in tkinter? - jacklee26 - Jun-09-2019

Do anyone knows how to let ping to print in the tkinter GUI, not on the terminal
i had tried many methods and find on the net just couldn't find a method to solve it.
Many resource mention on subprocess or thread related methods.
Could anyone help me, please?
Thanks and really appreciate.


import sys
import os
from tkinter import *
def ping():
    myinptext = entry.get()
    os.system("ping "+entry.get()+" -n 2" )
myGui = Tk()
entry = StringVar() 
myGui.geometry('300x300')
myGui.title("Get output inside GUI") 
mylabel = Label(myGui,text="Enter target IP or host as required.").pack() 
mybutton = Button(myGui,text ="Ping Test",command = ping).pack() 
myEntry = Entry(myGui,textvariable=entry)
myEntry.insert(-1,"8.8.8.8")
myEntry.pack()
myGui.mainloop()



RE: How to get the result of a ping to show in tkinter? - Yoriz - Jun-09-2019

This is quite a tricky task as calling ping can block the GUI mainloop.
The code below uses the technique from the following forum tutorial.
https://python-forum.io/Thread-Tkinter-How-to-deal-with-code-that-blocks-the-mainloop-freezing-the-gui

import functools
import os
import tkinter as tk
from concurrent import futures

thread_pool_executor = futures.ThreadPoolExecutor(max_workers=1)


def tk_after(target):

    @functools.wraps(target)
    def wrapper(self, *args, **kwargs):
        args = (self,) + args
        self.after(0, target, *args, **kwargs)

    return wrapper


def submit_to_pool_executor(executor):

    def decorator(target):

        @functools.wraps(target)
        def wrapper(*args, **kwargs):
            return executor.submit(target, *args, **kwargs)

        return wrapper

    return decorator


class MainFrame(tk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.master.geometry('500x350')
        self.master.title("Get output inside GUI")
        self.entry = tk.StringVar()
        label = tk.Label(
            self.master, text="Enter target IP or host as required.")
        label.pack()
        entry = tk.Entry(self.master, textvariable=self.entry)
        entry.insert(-1, "8.8.8.8")
        entry.pack()
        self.button = tk.Button(
            self.master, text="Ping Test", command=self.on_button)
        self.button.pack()
        self.text = tk.Text(self.master)
        self.text.config(state=tk.DISABLED)
        self.text.pack(padx=5, pady=5)

    @tk_after
    def button_state(self, enabled=True):
        state = tk.NORMAL
        if not enabled:
            state = tk.DISABLED
        self.button.config(state=state)

    @tk_after
    def clear_text(self):
        self.text.config(state=tk.NORMAL)
        self.text.delete(1.0, tk.END)
        self.text.config(state=tk.DISABLED)

    @tk_after
    def insert_text(self, text):
        self.text.config(state=tk.NORMAL)
        self.text.insert(tk.END, text)
        self.text.config(state=tk.DISABLED)

    def on_button(self):
        self.ping()

    @submit_to_pool_executor(thread_pool_executor)
    def ping(self):
        self.button_state(False)
        self.clear_text()
        self.insert_text('Starting ping request')

        result = os.popen("ping "+self.entry.get()+" -n 2")
        for line in result:
            self.insert_text(line)

        self.insert_text('ping request finished')
        self.button_state(True)


if __name__ == '__main__':
    app = tk.Tk()
    main_frame = MainFrame()
    app.mainloop()



RE: How to get the result of a ping to show in tkinter? - DeaD_EyE - Jun-09-2019

Hm, I like the idea about using decorators for that.
Previous I made an example, but was not willing to show it.
It's too complex.

I guess the OP will have problems to understand everything.
The main problem in a GUI application is, that blocking calls,
blocks the guis eventloop and then the gui is freezing for this time.

Those kind of blocking calls should run in another thread or process,
if it's a cpu intensive calculation. For io stuff a thread or threadpool is ok.

import sys
import socket
from queue import Queue
from ipaddress import ip_address
from threading import Thread
from subprocess import check_output
from tkinter.ttk import (
    Label, Entry,
    )
from tkinter import (
    Tk, Button, Text, StringVar, END,
    Toplevel, BOTH
)
# from tkinter.messagebox import showinfo
# we need to make our own showinfo widget


def validate_ip(ip):
    """
    Validate an ip address
    if the address is a valid ipv4 or ipv6 address
    the functions returns True, otherwise
    it returns False
    """
    try:
        ip_address(ip)
    except:
        return False
    else:
        return True


class Showinfo(Toplevel):
    """
    Spawns a new Toplevel window.
    """
    def __init__(self, *, title, msg, width, height):
        super().__init__(width=width, height=height)
        self.title(title)
        Label(self, text=msg).pack(fill=BOTH)
        Button(self, text="Ok", command=self.destroy).pack(fill=BOTH)


class App(Tk):
    def __init__(self):
       super().__init__()
       self.title('My Ping GUI')
       #self.geometry('500x400')
       self.ping_active = False
       self.validation_queue = Queue()
       self.validation_loop()
       self.ip = StringVar(self)
       self.ip.trace_add("write", self.validate)
       self.setup()

    def setup(self):
        Label(self, text="Enter target IP or host as required.").pack()
        Entry(self, textvariable=self.ip).pack()
        ping_button = Button(self, text="Ping Test", command=self.ping)
        ping_button.pack()
        self.ping_button = ping_button
        self.textbox = Text(self, width=150, height=10)
        self.textbox.pack(fill=BOTH)
        Button(self, text="Exit", command=self.destroy).pack()


    def validate(self, *args):
        self.validation_queue.put(self.ip.get())

    def validation_loop(self):
        self._validation_loop = Thread(target=self._validation_worker, daemon=True)
        self._validation_loop.start()

    def set_ping_color(self, color):
        self.ping_button['activebackground'] = color
        self.ping_button['bg'] = color
        self.ping_button['highlightbackground'] = color

    def _validation_worker(self):
        while True:
            ip_or_host = self.validation_queue.get()
            is_ip = validate_ip(ip_or_host)
            if is_ip:
                self.set_ping_color("green")
            else:
                self.set_ping_color("red")
            # is useful if you want to join a queu
            # then join blocks, until all tasks are done
            self.validation_queue.task_done()

    def ping(self):
        if not self.ping_active:
            self.ping_active = True
            self.textbox.delete(1.0, END)
            ip = self.ip.get()
            thread = Thread(target=self.ping_thread)
            thread.start()

    def ping_thread(self):
        # code tested on linux
        # ping on windows has different options
        stdout = check_output(['ping', '-c', '3', self.ip.get()], encoding="utf8")
        # print(stdout)
        self.textbox.insert(END, stdout)
        Showinfo(title='Results', msg=stdout, width=500, height=100)
        self.ping_active = False


App().mainloop()



RE: How to get the result of a ping to show in tkinter? - jacklee26 - Jun-10-2019

seem like the code is really complicate, is there any more easier way.


RE: How to get the result of a ping to show in tkinter? - joe_momma - Jun-15-2019

thanks Yoriz and Deadeye for your examples. Yoriz I guess your using windows because to get it to work on linux I changed:
        result = os.popen("ping "+self.entry.get()+" -n 2")
        for line in result:
            self.insert_text(line)
 
to:
        result = os.popen("ping "+self.entry.get()+" -c 2")
        for line in result:
            self.insert_text(line)
no output other wise
DeadEye I got an attribute error:
Output:
File "/home/pi/tk_ping.py", line 53, in __init__ self.ip.trace_add("write", self.validate) AttributeError: 'StringVar' object has no attribute 'trace_add'
and changed this line in your __init__:
self.ip.trace_variable("w", self.validate)
then I got a type error:
Output:
File "/home/pi/tk_ping.py", line 102, in ping_thread stdout = check_output(['ping', '-c', '3', self.ip.get()], encoding="utf8") File "/usr/lib/python3.5/subprocess.py", line 316, in check_output **kwargs).stdout File "/usr/lib/python3.5/subprocess.py", line 383, in run with Popen(*popenargs, **kwargs) as process: TypeError: __init__() got an unexpected keyword argument 'encoding'
so I deleted the keyword encoding and everything worked:
stdout = check_output(['ping', '-c', '3', self.ip.get()],)
fyi, Joe


RE: How to get the result of a ping to show in tkinter? - Yoriz - Jun-15-2019

(Jun-15-2019, 07:53 PM)joe_momma Wrote: Yoriz I guess your using windows

Yes i'm using windows.


RE: How to get the result of a ping to show in tkinter? - NebularNerd - Feb-10-2023

(Jun-09-2019, 04:13 PM)Yoriz Wrote: This is quite a tricky task as calling ping can block the GUI mainloop.

Thanks you for saving what little sanity I had left Smile

I've been trying to get this to work for about a week, I'm using this with customtkinter after a bit of adapting it works a treat, now I can live print to the GUI and not have the whole thing freeze up I can move forward Big Grin