主线程不在主循环中
Main thread is not in main loop
我正在尝试 ping 并在 1-2 内获得 100 多个 IP 的结果 sec.But 运行 使用 Tkinter 的这段代码给我以下错误,请帮助我解决这个问题。
RuntimeError: main thread is not in main loop
附上代码。请查看它,如果需要任何其他信息,请告诉我
谢谢。
import tkinter.messagebox
from tkinter import ttk
import os
import socket
import sys
import subprocess
import re
import threading
from tkinter import *
import multiprocessing.dummy
import multiprocessing
class Demo1:
data=[]
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.pack()
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2(Demo1):
t1=[]
s1=True
display=[]
def __init__(self, master):
self.master=master
self.kas(master)
def kas(self,master):
Demo2.t1=Demo1.data
self.master = master
cols = ('IP','Ping status')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.column('#0',width=50)
self.listBox.grid(row=1, column=0, columnspan=2)
self.ping_range(Demo2.t1)
def ping_func(self,ip):
p=[]
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
p.append(ip)
if (any('Reply from' in i for i in result)) and (any('Destination host unreachable' not in i for i in result)):
p.append("sucess")
else:
p.append("failed")
for i,(a) in enumerate(p):
self.listBox.insert('', 'end',value=(a))
return result
def ping_range(self,ip_list):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in ip_list])
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
我试过使用队列和 after() 的代码。但仍然出现相同的错误。对这些不太了解,请指导我哪里出错了,我该怎么做才能纠正它。谢谢
import tkinter.messagebox
from tkinter import ttk
import os
import socket
import sys
import subprocess
import re
import threading
import multiprocessing.dummy
import multiprocessing
import time, queue
class Demo1:
data=[]
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.pack()
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2(Demo1):
t1=[]
s1=True
display=[]
def __init__(self, master):
self.master=master
self.kas(master)
def kas(self,master):
self.running = True
self.queue = queue.Queue() #queue
Demo2.t1=Demo1.data
self.master = master
cols = ('IP','Ping status')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.column('#0',width=50)
self.listBox.grid(row=1, column=0, columnspan=2)
#self.ping_range(Demo2.t1)
self.running = True
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.t1])
def ping_func(self,ip):
while self.running:
pi=[]
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
pi.append(ip)
if (any('Reply from' in i for i in result)) and (any('Destination host unreachable' not in i for i in result)):
pi.append("sucess")
else:
pi.append("failed")
self.queue.put(pi) #Thread value to queue
m = self.queue.get_nowait()
print(m) #getting the correct value but after this statement, getting error as main thread is not in main loop
for i,(a) in enumerate(m):
self.listBox.insert('', 'end',value=(a))
self.periodic_call()
def periodic_call(self):
self.master.after(200, self.periodic_call) #checking its contents periodically
self.ping_func()
if not self.running:
import sys
sys.exit(1)
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
即使进行了更改,您仍然获得 RuntimeError
的原因是,您仍在尝试从线程更新 GUI — 运行 [=12] =] 方法 — 即 与 tkinter
GUI 运行 不同(这是必需的,因为它不支持多线程)。
为了解决这个问题,我将 ping_func()
分成了两个独立的部分,一个在另一个进程中运行 ping 命令并将结果附加到队列中,另一个更新 GUI — 后者现在是用我添加的新方法完成 process_incoming()
(类似于我提到的示例)。
另请注意,Demo2
class 不再是 Demo1
的子 class,因为没有理由这样做(这可能会混淆问题)。我还将 self.listBox
属性更改为 self.treeview
因为它就是这样。
尽管仅这些更改 就可以 避免 RuntimeError
,理论上 GUI 仍然可以“冻结”直到所有任务完成,因为 pool.map()
函数阻塞,直到所有任务都完成,这可能会干扰 tkinter
的 mainloop()
,具体取决于需要多长时间。为避免这种情况,我将 pool.map()
更改为 pool.async()
— 这不会阻塞,因为考虑到 Queue
的内容被反复轮询,这是不必要的。
import multiprocessing.dummy
import multiprocessing
import os
import socket
import sys
import subprocess
import re
import time
import threading
import tkinter.messagebox
from tkinter import ttk
import queue
class Demo1:
data = []
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK",
command=self.new_window)
self.button.pack()
def new_window(self):
self.inputValue = self.t.get("1.0",'end-1c')
Demo1.data = self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2:
t1 = []
s1 = True
display = []
def __init__(self, master):
self.master = master
self.kas(master)
def kas(self,master):
self.running = True
self.queue = queue.Queue()
Demo2.t1 = Demo1.data
self.master = master
cols = ('IP','Ping status')
self.treeview = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.treeview.heading(col, text=col)
self.treeview.column(col,minwidth=0,width=170)
self.treeview.column('#0',width=50)
self.treeview.grid(row=1, column=0, columnspan=2)
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map_async(self.ping_func, [x for x in Demo2.t1 if x])
self.periodic_call() # Start polling the queue for results.
def ping_func(self, ip):
pi = []
pingCmd = "ping -n 1 -w 1000 " + ip
with os.popen(pingCmd) as childStdout:
result = childStdout.readlines()
pi.append(ip)
if(any('Reply from' in i for i in result)
and any('Destination host unreachable' not in i for i in result)):
pi.append("success")
else:
pi.append("failed")
self.queue.put(pi) #Thread value to queue
def process_incoming(self):
""" Process any messages currently in the queue. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
print(msg)
self.treeview.insert('', 'end', value=(msg)) # Update GUI.
except queue.Empty: # Shouldn't happen.
pass
def periodic_call(self):
self.master.after(200, self.periodic_call) # checking its contents periodically
self.process_incoming()
if not self.running:
import sys
sys.exit(1)
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
我正在尝试 ping 并在 1-2 内获得 100 多个 IP 的结果 sec.But 运行 使用 Tkinter 的这段代码给我以下错误,请帮助我解决这个问题。
RuntimeError: main thread is not in main loop
附上代码。请查看它,如果需要任何其他信息,请告诉我
谢谢。
import tkinter.messagebox
from tkinter import ttk
import os
import socket
import sys
import subprocess
import re
import threading
from tkinter import *
import multiprocessing.dummy
import multiprocessing
class Demo1:
data=[]
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.pack()
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2(Demo1):
t1=[]
s1=True
display=[]
def __init__(self, master):
self.master=master
self.kas(master)
def kas(self,master):
Demo2.t1=Demo1.data
self.master = master
cols = ('IP','Ping status')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.column('#0',width=50)
self.listBox.grid(row=1, column=0, columnspan=2)
self.ping_range(Demo2.t1)
def ping_func(self,ip):
p=[]
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
p.append(ip)
if (any('Reply from' in i for i in result)) and (any('Destination host unreachable' not in i for i in result)):
p.append("sucess")
else:
p.append("failed")
for i,(a) in enumerate(p):
self.listBox.insert('', 'end',value=(a))
return result
def ping_range(self,ip_list):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in ip_list])
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
我试过使用队列和 after() 的代码。但仍然出现相同的错误。对这些不太了解,请指导我哪里出错了,我该怎么做才能纠正它。谢谢
import tkinter.messagebox
from tkinter import ttk
import os
import socket
import sys
import subprocess
import re
import threading
import multiprocessing.dummy
import multiprocessing
import time, queue
class Demo1:
data=[]
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.pack()
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2(Demo1):
t1=[]
s1=True
display=[]
def __init__(self, master):
self.master=master
self.kas(master)
def kas(self,master):
self.running = True
self.queue = queue.Queue() #queue
Demo2.t1=Demo1.data
self.master = master
cols = ('IP','Ping status')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.column('#0',width=50)
self.listBox.grid(row=1, column=0, columnspan=2)
#self.ping_range(Demo2.t1)
self.running = True
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.t1])
def ping_func(self,ip):
while self.running:
pi=[]
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
pi.append(ip)
if (any('Reply from' in i for i in result)) and (any('Destination host unreachable' not in i for i in result)):
pi.append("sucess")
else:
pi.append("failed")
self.queue.put(pi) #Thread value to queue
m = self.queue.get_nowait()
print(m) #getting the correct value but after this statement, getting error as main thread is not in main loop
for i,(a) in enumerate(m):
self.listBox.insert('', 'end',value=(a))
self.periodic_call()
def periodic_call(self):
self.master.after(200, self.periodic_call) #checking its contents periodically
self.ping_func()
if not self.running:
import sys
sys.exit(1)
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
即使进行了更改,您仍然获得 RuntimeError
的原因是,您仍在尝试从线程更新 GUI — 运行 [=12] =] 方法 — 即 与 tkinter
GUI 运行 不同(这是必需的,因为它不支持多线程)。
为了解决这个问题,我将 ping_func()
分成了两个独立的部分,一个在另一个进程中运行 ping 命令并将结果附加到队列中,另一个更新 GUI — 后者现在是用我添加的新方法完成 process_incoming()
(类似于我提到的示例)。
另请注意,Demo2
class 不再是 Demo1
的子 class,因为没有理由这样做(这可能会混淆问题)。我还将 self.listBox
属性更改为 self.treeview
因为它就是这样。
尽管仅这些更改 就可以 避免 RuntimeError
,理论上 GUI 仍然可以“冻结”直到所有任务完成,因为 pool.map()
函数阻塞,直到所有任务都完成,这可能会干扰 tkinter
的 mainloop()
,具体取决于需要多长时间。为避免这种情况,我将 pool.map()
更改为 pool.async()
— 这不会阻塞,因为考虑到 Queue
的内容被反复轮询,这是不必要的。
import multiprocessing.dummy
import multiprocessing
import os
import socket
import sys
import subprocess
import re
import time
import threading
import tkinter.messagebox
from tkinter import ttk
import queue
class Demo1:
data = []
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK",
command=self.new_window)
self.button.pack()
def new_window(self):
self.inputValue = self.t.get("1.0",'end-1c')
Demo1.data = self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2:
t1 = []
s1 = True
display = []
def __init__(self, master):
self.master = master
self.kas(master)
def kas(self,master):
self.running = True
self.queue = queue.Queue()
Demo2.t1 = Demo1.data
self.master = master
cols = ('IP','Ping status')
self.treeview = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.treeview.heading(col, text=col)
self.treeview.column(col,minwidth=0,width=170)
self.treeview.column('#0',width=50)
self.treeview.grid(row=1, column=0, columnspan=2)
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map_async(self.ping_func, [x for x in Demo2.t1 if x])
self.periodic_call() # Start polling the queue for results.
def ping_func(self, ip):
pi = []
pingCmd = "ping -n 1 -w 1000 " + ip
with os.popen(pingCmd) as childStdout:
result = childStdout.readlines()
pi.append(ip)
if(any('Reply from' in i for i in result)
and any('Destination host unreachable' not in i for i in result)):
pi.append("success")
else:
pi.append("failed")
self.queue.put(pi) #Thread value to queue
def process_incoming(self):
""" Process any messages currently in the queue. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
print(msg)
self.treeview.insert('', 'end', value=(msg)) # Update GUI.
except queue.Empty: # Shouldn't happen.
pass
def periodic_call(self):
self.master.after(200, self.periodic_call) # checking its contents periodically
self.process_incoming()
if not self.running:
import sys
sys.exit(1)
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()