【发布时间】:2020-11-26 13:49:59
【问题描述】:
我正在尝试构建一个实时项目,其中状态每秒更新一次,因此某些部分代码会不断重复。当我想更改必须更新的信息时,我只需单击新按钮,它会为我提供第一个窗口,我可以在其中更新新信息。但这样做会给我以下错误。如果我使用 after() 而不是线程,则错误不会出现,但输出窗口会挂起。请帮助我解决这个问题。谢谢。
Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\threading.py", line 926, in _bootstrap_inner
self.run()
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\threading.py", line 1177, in run
self.function(*self.args, **self.kwargs)
File "C:/Users/Desktop/Tool/t.py", line 47, in ae
self.treeview.insert('', 'end',image=self._img, value=(a))
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\tkinter\ttk.py", line 1370, in insert
res = self.tk.call(self._w, "insert", parent, index, *opts)
_tkinter.TclError: invalid command name ".!treeview"
我有问题的代码:
def aaa(self):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.t1])
self.process_incoming()
#threading.Timer(1.0, self.aaa).start()-this gives the error while pressing new button and updating information
self.master.after(100, self.aaa) #it hangs the output window
示例代码:
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
from tkinter import *
class Demo1: #window 1
data=[]
def __init__(self, master):
self.master = master
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.grid(row=1, column=1)
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.grid(row=2,column=1)
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.mainloop()
class Demo2: #window 2
value = []
display = []
num=0
def __init__(self, master):
self.master = master
self.queue = queue.Queue()
Demo2.value = Demo1.data
self.button = tkinter.Button(self.master,height=2,width=11, text="new",command=self.new).place(x=0,y=0)
self.label = tkinter.Label(self.master, text="monitor", font=("Arial",20)).grid(row=0, columnspan=3)
cols = ('aa','bb')
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.grid(row=1, column=0)
self._img=tkinter.PhotoImage(file="green1.gif")
self.aaa()
def aaa(self):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.value])
self.process_incoming()
#threading.Timer(1.0, self.aaa).start()
self.master.after(100, self.aaa)
def ping_func(self,ip): #Ping every ip and append the result
ping_result = []
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
ping_result.append(ip)
if(any('Reply from' in i for i in result)):
ping_result.append("success")
else:
ping_result.append("failed")
self.queue.put(ping_result) #Thread value to queue
def process_incoming(self): #add the ping result to treeview
while self.queue.qsize():
try:
if Demo2.num<len(Demo1.data):
self._img=tkinter.PhotoImage(file="green1.gif")
self._img1=tkinter.PhotoImage(file="red.gif")
msg = self.queue.get_nowait()
Demo2.display.append(msg) #adding queue value to variable(display)
if(len(Demo2.display)==len(Demo1.data)):
self.treeview.insert("","end",values=(0,0,0,0,0))
self.treeview.delete(*self.treeview.get_children())
for i,(a,b) in enumerate(Demo2.display):
if(Demo2.display[i][1]=='success' ):
self.treeview.insert('', 'end',image=self._img, value=(a,b))
else:
self.treeview.insert('', 'end',image=self._img1, value=(a,b))
Demo2.num=Demo2.num+1
Demo2.display.clear()
else:
Demo2.display.clear()
Demo2.num=0
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 new(self):
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo1(self.master) # create Demo2 window
self.master.mainloop()
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
【问题讨论】:
-
所有 GUI 都不喜欢在线程、子进程中使用小部件。 Subprocess 甚至无法访问小部件。因此,您可以使用线程/子进程运行一些长时间运行的代码,而无需更改小部件中的值,并使用
Queue将结果发送到主线程/进程 - 然后您可以使用after()定期检查Queue中的新值并更新小部件。 -
嗨@furas,我是这些线程和队列概念的新手..对此没有太多想法。我会调查一下。谢谢。
-
你的程序挂在
result = (childStdout.readlines())我对使用多处理来提供进一步的帮助还不够熟悉。它从不调用 .after() 方法。 -
首先更好地组织代码,因为它不可读 - 即。添加空行,使用有意义的变量等。PEP 8 -- Style Guide for Python Code
-
@furas,对不起,我已经尝试纠正它..希望你现在可以得到代码。谢谢
标签: python tkinter python-multithreading ping