使用程序 GUI 中的按钮终止 Python 应用程序中的子进程

问题描述 投票:0回答:1

所以我尝试使用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 但不幸的是它没有提供任何有价值的信息。感谢任何帮助!

python python-3.x subprocess
1个回答
0
投票

我实际上建议您将其放入一个类中,以便更清晰地跟踪这些变量,但这是一个微创解决方案。它依赖于

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")

希望这有帮助!有任何问题请随时评论。

© www.soinside.com 2019 - 2024. All rights reserved.