如何将其更改为非模态?若按钮被点击,那个么光标并没有改变。如果我把它改为非模态,那么它应该是可以的。
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()
您可以使用线程来实现这一点。
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毫秒检查一次线程是否已结束,并更改光标,如果已结束则启用按钮。
这里有一种方法,它使用线程和队列来不阻塞.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()
:
现在首先运行所有配置,所以现在配置了光标和文本(注意,只有当光标不在text小部件上时,时钟光标才可见,因为文本小部件的配置被注释掉了(
然后创建一个队列对象,这将允许线程之间安全地通信(一旦函数"停止",它将被垃圾收集(
然后启动一个运行cmd
命令的线程(哦,我对它做了一点更改(删除了"-c1"(,因为我无法进行其他测试(。因此,每次迭代都会将数据放入队列中,以便主线程安全地接收它,其他东西都是一样的,也要注意有";中断"一旦循环必须停止,就放入队列,这样另一端也知道要停止(可能最好放置一些对象,因为"break"
可以通过line
变量放置在那里(
然后调用update_text()
函数,该函数从队列接收数据,然后将其插入到文本窗口小部件(还有"break"
检查(,然后使用.after()
方法在不阻塞.mainloop()
的情况下使函数循环(并且当应用"break"
时,调用配置函数来配置状态和光标(