Posts: 93
Threads: 31
Joined: Nov 2017
Jun-09-2019, 06:50 AM
(This post was last modified: Jun-09-2019, 04:06 PM by Yoriz.)
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()
Posts: 2,164
Threads: 35
Joined: Sep 2016
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-H...ng-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()
NebularNerd likes this post
Posts: 2,066
Threads: 9
Joined: May 2017
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()
Posts: 93
Threads: 31
Joined: Nov 2017
seem like the code is really complicate, is there any more easier way.
Posts: 165
Threads: 7
Joined: Nov 2018
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
Posts: 2,164
Threads: 35
Joined: Sep 2016
(Jun-15-2019, 07:53 PM)joe_momma Wrote: Yoriz I guess your using windows
Yes i'm using windows.
Posts: 3
Threads: 1
Joined: Feb 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
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
|