Arhn - архитектура программирования

Основной поток не находится в основном цикле

Я пытаюсь выполнить ping и получить результат более 100 IP-адресов в течение 1-2 секунд, но запуск этого кода с использованием 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()

Я пробовал код с использованием очереди и после (). но все равно получаю ту же ошибку. не имею большого представления об этом, пожалуйста, объясните мне, где я ошибаюсь и что я могу сделать, чтобы исправить это. Спасибо

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

  • Tkinter не является потокобезопасным. Если все, что вы делаете, это pinging, то более подходящим будет консольное приложение. 15.07.2020
  • Привет @JoshuaNixon, Спасибо за ответ. Я пытаюсь создать приложение, которое показывает статус проверки связи в реальном времени. поэтому я был знаком с python, поэтому использовал tkinter. У вас есть идеи, как получить статус 100+ ips в течение 1-2 секунд без использования этой концепции потока. 15.07.2020
  • Как сказал @Joshua, tkinter не поддерживает многопоточность. Один из способов обойти это ограничение - поместить результаты потоков в Queue и периодически проверять его содержимое с помощью универсального виджета tkinter _ 4_. Мой ответ на связанный вопрос есть пример того, как это сделать. 15.07.2020
  • Привет @martineau, Спасибо за ответ. Я буду смотреть в него. 15.07.2020
  • Привет @martineau, я изучил ваш ответ и попытался исправить его. Поскольку я не очень разбираюсь в этом, все равно получаю ту же ошибку. Отредактированный код вставлен выше, посмотрите и укажите, где я ошибаюсь. Спасибо. 16.07.2020

Ответы:


1

Причина, по которой вы все еще получаете RuntimeError даже с изменениями, заключается в том, что вы все еще пытаетесь обновить графический интерфейс из потока - того, который запускает метод ping_func(), - который не тот же самый запуск tkinter графического интерфейса пользователя (что необходимо, поскольку он не поддерживает многопоточность).

Чтобы исправить это, я разделил ping_func() на две отдельные части: одна запускает команду ping в другом процессе и добавляет результаты в очередь, а другая обновляет графический интерфейс - последнее теперь выполняется в новом методе, который я добавил с именем process_incoming() ( аналогично тому примеру, на который я вас сослал).

Также обратите внимание, что класс Demo2 больше не является подклассом Demo1, поскольку для этого нет причин (и это может запутать ситуацию). Я также изменил атрибут self.listBox на self.treeview, потому что он такой.

Хотя сами по себе эти изменения позволят избежать RuntimeError, теоретически графический интерфейс может зависнуть до тех пор, пока не будут выполнены все задачи, потому что функция pool.map() блокирует, пока не будут выполнены все задачи, что может помешать с mainloop() tkinter в зависимости от того, сколько времени это займет. Чтобы избежать этого, я изменил 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()
16.07.2020
  • Привет @martineau, Большое спасибо за помощь. Я был так сбит с толку. 16.07.2020
  • soumya: Это сложный предмет, поэтому его легко запутать. Обязательно обратите внимание на дополнительную информацию, которую я только что добавил, о потенциальных ловушках использования pool.map(). 16.07.2020
  • конечно, сэр, спасибо .. Мне очень помогло @martineau 16.07.2020
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..