Python 主线程不在主循环中

Python 主线程不在主循环中,python,tkinter,python-multithreading,ping,Python,Tkinter,Python Multithreading,Ping,我正在尝试ping并在1-2秒内获得100多个IP的结果。但是使用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 r

我正在尝试ping并在1-2秒内获得100多个IP的结果。但是使用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()
我已经使用queue和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()

即使进行了更改,您仍然会收到
运行时错误
,这是因为您仍在尝试从一个线程更新GUI—运行
ping_func()
方法的线程—与运行
tkinter
GUI的线程不同(这是必需的,因为它不支持多线程)

为了解决这个问题,我将ping_func()拆分为两个独立的部分,一个在另一个进程中运行ping命令并将结果附加到队列,另一个更新GUI,后者现在使用名为
process_incoming()
的新方法完成(类似于我提到的示例)

还要注意的是,
Demo2
类不再是
Demo1
的子类,因为没有理由这样做(这可能会混淆问题)。我还将
self.listBox
属性更改为
self.treeview
,因为它就是这样

尽管仅这些更改就可以避免运行时错误,但理论上,GUI仍然可以“冻结”直到所有任务完成,因为
pool.map()
功能块会阻止所有任务完成,这可能会干扰
tkinter
mainloop()
,具体取决于需要多长时间。为了避免这种情况,我将
pool.map()
更改为
pool.async()
——这不会阻塞,因为考虑到
队列
的内容会被重复轮询,这是不必要的

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

Tkinter不是线程安全的。如果您所做的只是ping,那么控制台应用程序将更合适。您好@JoshuaNixon,谢谢您的回复。我正在尝试构建一个显示实时ping状态的应用程序。所以我有点熟悉python,所以使用tkinter。您是否知道如何在1-2秒内获得100+IP状态而不使用此线程概念。正如@Joshua所说,
tkinter
不支持多线程。解决该限制的一种方法是将线程的结果放入
队列
,并使用
tkinter
的通用小部件方法定期检查其内容。我对一个相关问题的回答有一个这样做的例子。嗨@martineau,谢谢你的回答。我会调查的。嗨@martineau,我已经调查了你的答案,并试图让它正确。因为我没有太多的想法,仍然得到相同的错误。编辑后的代码粘贴在上面,请看一看,并请指导我哪里出了问题。谢谢。你好,马蒂诺,非常感谢你的帮助。苏美亚:这是一个复杂的问题,所以很容易被弄糊涂。请务必注意我刚才添加的关于使用
pool.map()
的潜在陷阱的附加信息。当然,先生,非常感谢。。这对我很有帮助@martineau