通过在 Popen 进程中发送 .communicate 请求来冻结 tkinter 窗口

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

我有这段代码,是我的想象和 ChatGPT 帮助的产物:

import subprocess
import threading
import tkinter as tk

class PingThread(threading.Thread):
    def __init__(self, text_widget):
        super().__init__()
        self.text_widget = text_widget
        self.process = None
        self.stop_event = threading.Event()

    def run(self):
        self.process = subprocess.Popen(['cmd'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        self.process.stdin.write(b'ping -t google.com\n')
        self.process.stdin.flush()

        while not self.stop_event.is_set():
            line = self.process.stdout.readline().decode('cp866')
            if not line:
                break
            self.text_widget.insert(tk.END, line)
            self.text_widget.see(tk.END)

    def stop(self):
        if self.process:
            self.process.communicate(b'\x03')
            self.process.wait()                                #Window freezes on the .communicate line, so this and line below are not executing at all
        self.stop_event.set()

def ping():
    ping_thread = PingThread(text)
    ping_thread.start()

    def handle_ctrl_c(event):
        ping_thread.stop()
        text.insert(tk.END, '\nProcess terminated.\n')

    root.bind('<Control-c>', handle_ctrl_c)

root = tk.Tk()

text = tk.Text(root)
text.pack()

button = tk.Button(root, text='Ping', command=ping)
button.pack()

root.mainloop()

我正在尝试在 Tkinter 中创建控制台模拟。我正在发送 ping 请求并监听控制台的响应。一切正常,除了

Ctrl+C
命令,它应该完成
ping
执行并从控制台响应统计信息。窗口刚刚冻结,当我尝试发送
self.process.communicate(b'\x03')

是什么原因造成的?据我所知,这条线应该发送

Ctrl+C
到控制台,
while loop
应该从控制台接收最后几行,以及 ping 的统计信息?

python python-3.x multithreading tkinter subprocess
2个回答
0
投票

只需更换

self.process.communicate(b'\x03')
self.process.wait()

self.process.kill()
root.unbind('<Control-c>')

注意当您点击

Ctrl+c
时选择了您的tkinter窗口,因为终端也使用此快捷方式。在这里,您覆盖快捷方式的根键绑定以在 python 中终止子进程
Ctrl+c
在终端中代表 KeyboardInterrupt 并中断你的整个脚本。


0
投票

所以现在我更好地理解了你的问题,这是另一种尝试。我的回答并不完美,因为我错过了

STDOUT
中的 2 行,而且我找不到问题所在。 但是,对于您的问题,最重要的两件事是:

  1. 创建流程时使用
    creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
    。如果我理解正确的话,这会为终端和 ping 命令创建一个不同的进程,因此您仍然可以在通过停止信号后从终端获得输出。
  2. 要终止
    ping
    命令,请在 Windows 上发送
    signal.CTRL_BREAK_EVENT
    。一般来说,这些信号似乎与操作系统不一致。 (另请参阅@chris_se 的评论)

如果你知道为什么我的代码遗漏了一些

STDOUT
行,请告诉我。可能是发送和接收之间的重叠问题......

代码如下:

import subprocess
import threading
import tkinter as tk
import signal

class PingThread(threading.Thread):
    def __init__(self, text_widget):
        super().__init__()
        self.text_widget = text_widget
        self.process = None
        self.stop_event = threading.Event()

    def stop(self):
        if self.process:
            self.stop_event.set()
            self.process.send_signal(signal.CTRL_BREAK_EVENT)

    def run(self):
        self.process = subprocess.Popen(['cmd'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
        self.process.stdin.write(b'ping -t google.com\n')
        self.process.stdin.flush()

        line = self.process.communicate()

        while not self.stop_event.is_set():
            self.text_widget.insert(tk.END, line)
            self.text_widget.see(tk.END)

        self.text_widget.insert(tk.END, line)
        self.text_widget.see(tk.END)

        self.process.kill()
        root.unbind('<Control-c>')      

def ping():
    ping_thread = PingThread(text)
    ping_thread.start()

    def handle_ctrl_c(event):
        ping_thread.stop()
        text.insert(tk.END, '\nProcess terminated.\n')

    root.bind('<Control-c>', handle_ctrl_c)

root = tk.Tk()

text = tk.Text(root)
text.pack()

button = tk.Button(root, text='Ping', command=ping)
button.pack()

root.mainloop()
© www.soinside.com 2019 - 2024. All rights reserved.