所以我尝试使用customtkinter在python中执行一个程序,我必须使用我创建的GUI中的按钮启动命令,一旦启动,我想使用GUI中的停止按钮终止它。我将我的工作分为 2 个不同的文件 app.py,其中保存设计;一个 method.py,保存按下按钮时运行的方法。我运行的命令是一个终端命令,类似于我在下面执行的 ping 命令。如果有人想尝试或更好地了解我的代码,请查看下面的文件
在app.py中:
import customtkinter as ctk
import methods as mtd
# System Defaults
ctk.set_appearance_mode('Dark')
ctk.set_default_color_theme('dark-blue')
app = ctk.CTk()
app.title('APP')
my_font = ctk.CTkFont(family="Sans Serif", size=15)
# Set up the grid layout
app.grid_columnconfigure((0, 1, 2), weight=1)
app.grid_rowconfigure(1, weight=1)
app.grid_rowconfigure(0, weight=15)
ipAdd = ctk.CTkEntry(app, font=my_font, placeholder_text='Enter IP address...')
ipAdd.grid(row=1, column=0, sticky='we', padx=(5, 10), pady=(5, 5))
# Set Up Display
display = ctk.CTkTextbox(app)
display.grid(row=0, column=0, columnspan=3, sticky='nswe', padx=5, pady=(5, 0))
# Set up the buttons
startBtn = ctk.CTkButton(app, font=my_font, text='START', fg_color='#006600', border_width=2, corner_radius=50,
hover_color='#009900', border_color='#006600', command=lambda: mtd.ping_host(display, ipAdd))
startBtn.grid(row=1, column=1, padx=(0, 5))
stopBtn = ctk.CTkButton(app, font=my_font, text='STOP', fg_color='#FF0000', border_width=2, corner_radius=50,
hover_color='#CC0000', border_color='#FF0000', command=lambda: mtd.stop_ping(display))
stopBtn.grid(row=1, column=2, padx=(5, 5))
app.mainloop()
在 method.py 中我有:
import customtkinter as ctk
import subprocess
def clear_display(display: ctk.CTkTextbox, ipadd: ctk.CTkEntry):
display.delete(0.0, 'end')
ipadd.delete(0, 'end')
def ping_host(display: ctk.CTkTextbox, ipadd: ctk.CTkEntry):
host = ipadd.get()
display.delete(1.0, ctk.END) # Clear previous results
try:
# Open a process with the ping command
process = subprocess.Popen(['ping', '-n', '5', host], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# Read and display the output line by line
while True:
line = process.stdout.readline()
if not line:
break # Break the loop when there's no more output
display.insert(ctk.END, f"{line}")
display.update_idletasks() # Force update of the display
# Wait for the process to complete
process.wait()
# Display any remaining output after the process completes
remaining_output = process.stdout.read()
if remaining_output:
display.insert(ctk.END, f"remain out: {remaining_output}\n")
# Display any errors
errors = process.stderr.read()
if errors:
display.insert(ctk.END, f"Error: {errors}\n")
except Exception as e:
# Handle any exceptions that may occur
display.insert(ctk.END, f"Exception: {str(e)}\n")
我需要 stop_ping() 方法来停止 ping 一旦开始。尝试使用 ChatGPT 但不幸的是它没有提供任何有价值的信息。感谢任何帮助!
我实际上建议您将其放入一个类中,以便更清晰地跟踪这些变量,但这是一个微创解决方案。它依赖于
threading
,并且应该满足您所描述的需求。
它的核心是,
ping_host
保持执行线程直到它完成,所以停止按钮在它完成之前不能做任何事情。通过将 ping_host
调用放在单独的 threading.Thread
中,我们为停止按钮提供了执行操作的机会。
我创建了一个类来保存在
methods
模块中生成的进程,并稍微修改了该模块以存储进程句柄。
最后,停止按钮函数调用被重新路由到使用 python 的
Popen.kill
函数的函数,详细信息在这里。
应用程序.py
import customtkinter as ctk
import methods as mtd
import threading
class ProcHandleHolder:
def __init__(self):
self.procHandle = None
@property
def procHandle(self):
return self._procHandle
@procHandle.setter
def procHandle(self, invalue):
self._procHandle = invalue
def set(self, procHandle):
self.procHandle = procHandle
def reset(self):
self.procHandle = None
def stop_proc():
if phh.procHandle:
phh.procHandle.kill()
phh.reset()
print('proc killed')
else:
print('proc not running?')
def start_proc():
t = threading.Thread(target=mtd.ping_host, args=(display, ipAdd, phh))
t.start()
phh = ProcHandleHolder()
# System Defaults
ctk.set_appearance_mode('Dark')
ctk.set_default_color_theme('dark-blue')
app = ctk.CTk()
app.title('APP')
my_font = ctk.CTkFont(family="Sans Serif", size=15)
# Set up the grid layout
app.grid_columnconfigure((0, 1, 2), weight=1)
app.grid_rowconfigure(1, weight=1)
app.grid_rowconfigure(0, weight=15)
ipAdd = ctk.CTkEntry(app, font=my_font, placeholder_text='Enter IP address...')
ipAdd.grid(row=1, column=0, sticky='we', padx=(5, 10), pady=(5, 5))
# Set Up Display
display = ctk.CTkTextbox(app)
display.grid(row=0, column=0, columnspan=3, sticky='nswe', padx=5, pady=(5, 0))
# Set up the buttons
startBtn = ctk.CTkButton(app, font=my_font, text='START', fg_color='#006600',
border_width=2, corner_radius=50,
hover_color='#009900', border_color='#006600',
command=lambda: start_proc())
startBtn.grid(row=1, column=1, padx=(0, 5))
stopBtn = ctk.CTkButton(app, font=my_font, text='STOP', fg_color='#FF0000',
border_width=2, corner_radius=50,
hover_color='#CC0000', border_color='#FF0000',
command=lambda: stop_proc())
stopBtn.grid(row=1, column=2, padx=(5, 5))
app.mainloop()
方法.py
import customtkinter as ctk
import subprocess
def clear_display(display: ctk.CTkTextbox, ipadd: ctk.CTkEntry):
display.delete(0.0, 'end')
ipadd.delete(0, 'end')
def ping_host(display: ctk.CTkTextbox, ipadd: ctk.CTkEntry, phh: object):
host = ipadd.get()
display.delete(1.0, ctk.END) # Clear previous results
try:
# Open a process with the ping command
process = subprocess.Popen(['ping', '-n', '5', host],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
phh.set(process)
# Read and display the output line by line
while True:
line = process.stdout.readline()
if not line:
break # Break the loop when there's no more output
display.insert(ctk.END, f"{line}")
display.update_idletasks() # Force update of the display
# Wait for the process to complete
process.wait()
# Display any remaining output after the process completes
remaining_output = process.stdout.read()
if remaining_output:
display.insert(ctk.END, f"remain out: {remaining_output}\n")
# Display any errors
errors = process.stderr.read()
if errors:
display.insert(ctk.END, f"Error: {errors}\n")
except Exception as e:
# Handle any exceptions that may occur
display.insert(ctk.END, f"Exception: {str(e)}\n")
希望这有帮助!有任何问题请随时评论。