簡體   English   中英

主線程不在主循環中

[英]Main thread is not in main loop

我正在嘗試 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()

我已經嘗試過使用隊列和 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——運行ping_func()方法的線程——運行tkinter GUI 的線程不同(這是必需的,因為它不支持多線程)。

為了解決這個問題,我將ping_func()拆分為兩個單獨的部分,一個在另一個進程中運行 ping 命令並將結果附加到隊列中,另一個更新 GUI — 后者現在在我添加的新方法中完成,命名為process_incoming() (類似於我提到的例子)。

另請注意, Demo2 class 不再是Demo1的子類,因為沒有理由這樣做(並且可能會混淆問題)。 我還將self.listBox屬性更改為self.treeview因為它就是這樣。

盡管僅這些更改可以避免RuntimeError ,但理論上 GUI 仍然可以“凍結”直到所有任務完成,因為pool.map() function阻塞直到所有任務都完成,這可能會干擾tkintermainloop()取決於這需要多長時間。 為避免這種情況,我將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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM