-1

如何将其更改为非模态?如果单击按钮,则光标不会改变。如果我将其更改为非模态,那么它应该没问题。

def start_new_proc():
    # text.config(cursor="clock")
    root.config(cursor="clock")
    text.config(state="normal")
    command = "ping 8.8.8.8 -c 1"
    proc = Popen(command, shell=True, stdout=PIPE)
    for line in iter(proc.stdout.readline, ''):
        line = line.decode('utf-8')
        if line == '':
            break
        text.insert("end", line)
    proc.wait()
    # text.config(cursor="")
    root.config(cursor="")
    text.config(state="disable")



root = tk.Tk()
text = tk.Text(root, state="disabled")
text.pack()

button = tk.Button(root, text="Run", command=start_new_proc)
button.pack()

root.mainloop()
4

2 回答 2

0

您可以使用线程来实现这一点。

import threading
def ping_func(output):
    command = "ping 8.8.8.8 -c 1"
    proc = Popen(command, shell=True, stdout=PIPE)
    for line in iter(proc.stdout.readline, ''):
        line = line.decode('utf-8')
        if line == '':
            break
        output.append(line)
    

def check_complete():
    if not ping_thread.is_alive():
        root.config(cursor = "")
        button.config(state = "normal")
    else:
        root.after(100, check_complete)

def check_output():
    if output != []:
        text.config(state = "normal")
        text.insert("end", output.pop(0))
        text.config(state = "disabled")
    root.after(100, check_output)

def start_new_proc():
    #text.config(cursor="clock")
    root.config(cursor="clock")
    button.config(state = "disabled") #Disable button until process completed
    global output, ping_thread
    output = []
    ping_thread = threading.Thread(target = ping_func, args = (output,))
    ping_thread.start()
    check_output()
    check_complete()



root = tk.Tk()
text = tk.Text(root, state="disabled")
text.pack()

button = tk.Button(root, text="Run", command=start_new_proc)
button.pack()

root.mainloop()

通过将命令执行放在一个线程中,它不会阻止 tkinter 线程。函数ping_func是线程运行的。它执行程序之前所做的操作,但输出不同。因为 tkinter 不是线程安全的,所以不能在其他线程中使用 tkinter 对象。因此,我们需要传递一个变量,然后可以由 tkinter 线程轮询。这就是output清单的用途。的输出ping_func附加到output. 要检查内容output并将其插入到 Text 小部件中,我们需要一个函数check_output。它每 100 毫秒调用一次,以检查输出的内容是否已更改。如果他们有它将新行插入到 Text 小部件中。还有 check_complete它每 100 毫秒检查一次线程是否已结束并更改光标并启用按钮(如果有)。

于 2021-07-21T09:24:31.220 回答
0

这是一种方法,它使用线程和队列来不阻塞.mainloop()和不调用tkinter另一个线程的方法(这可能会破坏它):

import tkinter as tk
from subprocess import Popen, PIPE
from threading import Thread
from queue import Queue


def start_new_proc():
    # text.config(cursor="clock")
    root.config(cursor="clock")
    text.config(state="normal")
    queue = Queue()
    Thread(target=lambda: run_proc(queue)).start()
    update_text(queue)


def after_proc():
    # text.config(cursor="")
    root.config(cursor="")
    text.config(state="disable")


def update_text(queue):
    data = queue.get()
    if data == 'break':
        after_proc()
        return
    text.insert('end', data)
    root.after(100, update_text, queue)


def run_proc(queue):
    command = "ping 8.8.8.8"
    proc = Popen(command, shell=True, stdout=PIPE)
    for line in iter(proc.stdout.readline, ''):
        line = line.decode('utf-8')
        if line == '':
            queue.put('break')
            break
        queue.put(line)
    # proc.wait()


root = tk.Tk()
text = tk.Text(root, state="disabled")
text.pack()

button = tk.Button(root, text="Run", command=start_new_proc)
button.pack()

root.mainloop()

快速解释:

当您单击按钮时,它会调用start_new_proc()

现在首先运行所有配置,所以现在配置了光标和文本(请注意,时钟光标仅在光标不在文本小部件上时可见,因为文本小部件的配置已被注释掉)

然后创建一个队列对象,这将允许线程之间安全地通信(一旦函数“停止”,它将被垃圾收集)

然后在cmd运行命令的地方启动一个线程(哦,我对其进行了一些更改(删除了“-c 1”),因为我无法进行其他测试)。所以每次迭代都会将数据放入队列中,以便主线程安全地接收它,其他东西是一样的,还要注意一旦循环必须停止,就会有'“break”'放入队列中,以便另一端也知道停止(可能更好地放置一些对象,因为"break"可以通过line变量放置在那里)

然后update_text()调用该函数,该函数从队列接收数据,然后将其插入到文本小部件(还有"break"检查),然后是.after()在不阻塞的情况下使函数循环的方法.mainloop()(并且当"break"appears 时,配置函数被调用到 config状态和光标)

于 2021-07-21T09:28:09.007 回答