Mainloop 和带有线程的文本
我有使用某些类函数的线程,这些函数打印了很多我想在 Text() 小部件上显示的东西.
I have threads that use some class's functions , and those functions print alot of stuff , that i want to display on a Text() widget .
所以我尝试将类中的窗口作为类变量,命令:mainloop() 似乎阻止了一切继续......
So i tried making the window in the class as a class variable and the command : mainloop() seems to stop everything from continuing ....
有什么解决办法吗?
我想做的一般想法:(将控制台转换为 GUI ..)
The general idea i want to do : (converting the console to GUI..)
from tkinter import *
root = Tk()
textbox = Text(root)
textbox.pack()
def redirector(inputStr):
textbox.insert(INSERT, inputStr)
sys.stdout.write = redirector
root.mainloop()
整个代码:
import threading
from queue import Queue
from Spider import Spider
from domain import *
from general import *
from tkinter import *
def mmm(answer1,answer2,master): # answer1,answer2 are user inputs from the first GUI that gets info, master is the root so i can close it
master.destroy()
PROJECT_NAME = answer1
HOMEPAGE = answer2
DOMAIN_NAME = get_domain_name(HOMEPAGE)
QUEUE_FILE = PROJECT_NAME + '/queue.txt'
CRAWLED_FILE = PROJECT_NAME + '/crawled.txt'
NUMBER_OF_THREADS = 8
queue = Queue() # thread queue
Spider(PROJECT_NAME, HOMEPAGE, DOMAIN_NAME) # a class where the prints happen and some other functions.
root = Tk()
textbox = Text(root)
textbox.pack()
def redirector(inputStr):
textbox.insert(INSERT, inputStr)
sys.stdout.write = redirector
root.mainloop()
# create threads (will die when exit)
def create_threads():
for x in range(NUMBER_OF_THREADS):
t = threading.Thread(target=work)
t.daemon = True
t.start()
# do the next link in the queue
def work():
while True:
url = queue.get()
Spider.crawl_page(threading.current_thread().name, url)
queue.task_done()
# each link is a new job
def create_jobs():
for link in file_to_set(QUEUE_FILE):
queue.put(link) # put the link in the thread queue
queue.join() # block until all processed
crawl()
# if there are items in the queue, crawl them
def crawl():
queued_links = file_to_set(QUEUE_FILE)
if len(queued_links) > 0:
print(str(len(queued_links)) + ' links in the queue')
create_jobs()
create_threads()
crawl()
@Victor Domingos 的提及对您的情况非常有用,但您的真正问题是您自己的代码!首先 - 看看你的应用程序的结构并理解它很弱,没有冒犯性(你甚至将 master
传递给一个函数来 destroy
它).所以我建议你阅读 Python 中的类和继承(如果你还没有),然后看看 此处.
A @Victor Domingos's mentions are really usefull in your case, but your real problem - your own code! First of all - take a look at structure of your application and understand, that it's weak, no offence (you even pass a master
to a function to destroy
it). So I suggest you to read about classes and inheritance in Python (if you don't already) and then take a look here.
下一站 - 您的重定向器.您重新分配 sys.stdout.write
,但您从未保留它 - 因此它又是另一个弱点.好吧,假设现在你保留它,但如果我们保持面向对象的方法 - 我更喜欢 这个 选项.
Next stop - your redirector. You reassign sys.stdout.write
, but you never preserve it - so it another weak spot. Ok, let's say that now you preserve it, but if we keeping object oriented approach - I would prefer this option.
另外,真的有必要摧毁
master
吗?对于输出,如果您销毁 master
只是为了避免两个 mainloop
,则可以使用 Toplevel
小部件.你可以甚至在 Toplevel
处于活动状态时隐藏 root
.太棒了,不是吗?
Also, is it really necessary to destroy
the master
? For output you can use a Toplevel
widget if you destroing master
just to avoid two mainloop
's. You can even hide root
while Toplevel
is active. Marvelous, isn't it?
最后,回答您关于解决方案的问题.没有直接的解决方案,只有一个:阅读并尝试.您已经回答了为什么 mainloop
停止一切,但您的问题非常广泛.
Finally, to answer your question about solution. There're no straight solution, but only one: read and try. You're already answered why mainloop
stops everything, but your question is really broad.
我尝试重现您的完整程序(2 个窗口应用程序、第一个用户输入、第二个 - 类似控制台的和一些带有线程的示例打印任务),这是一个代码:
I tried to reproduce your full program (2-window app, 1st-user input, 2nd - console-like and some example printing task with thread) and here's a code:
# imports:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
import sys
import string
import random
import threading
# classes:
class ReStdout:
# common stdout-redirector
def __init__(self, target_widget, start_redirection=True):
self.text_console = target_widget
if start_redirection:
self.start_redirection()
def __del__(self):
self.stop_redirection()
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop_redirection()
def __enter__(self):
pass
def flush(self):
pass
def write(self, stdout_line):
self.text_console.insert('1.0', stdout_line)
def start_redirection(self):
sys.stdout = self
@staticmethod
def stop_redirection():
sys.stdout = sys.__stdout__
class App(tk.Tk):
# common tk app
def __init__(self):
tk.Tk.__init__(self)
self.resizable(width=True, height=False)
self.minsize(width=250, height=25)
self.some_entry = tk.Entry(self)
self.some_entry.insert(0, 'You can pass something to Spawner!')
self.some_entry.pack(expand=True, fill='x')
self.start_spawner_button = tk.Button(self, text='Start Spawner', command=self.spawn_spawner)
self.start_spawner_button.pack(expand=True, fill='x')
def spawn_spawner(self):
Spawner(self, self.some_entry.get())
def close_app(self):
self.destroy()
class Spawner(tk.Toplevel):
# common tk app - task spawner
def __init__(self, master, entry_string):
tk.Toplevel.__init__(self, master)
self.resizable(width=False, height=False)
self.preserved_count = threading.active_count()
self.master = master
self.master.withdraw()
self.spawn_task_button = tk.Button(self, text='Spawn Task', command=spawn_task)
self.spawn_task_button.pack(expand=True, fill='x')
self.quit_button = tk.Button(self, text='Quit', command=self.close_app)
self.quit_button.pack(expand=True, fill='x')
self.text = tk.Text(self, bg='black', fg='white')
self.text.pack(expand=True, fill='both')
self.stdout = ReStdout(self.text)
self.protocol('WM_DELETE_WINDOW', self.close_app)
# print what have been passed
print('Users Input: %s' % entry_string)
# let's spawn something right now
# after here just for example
# try to use it w/o after
self.after(500, multi_spawn)
def close_app(self):
if threading.active_count() == self.preserved_count:
self.stdout.stop_redirection()
self.master.deiconify()
self.destroy()
else:
# code to handle threads
print('\n**** Cant quit right now! ****\n')
# non - class functions:
def multi_spawn(count=1):
for _ in range(count):
spawn_task()
def spawn_task():
task_count = threading.active_count()
task = threading.Thread(target=lambda: common_task(comment='%d printing task' % task_count,
iteration_count=random.randint(1, 10)))
task.start()
def common_task(comment, iteration_count=1):
# example task wait + print
task_numb = comment.split(None, 1)[0]
print('\nTask spawned:\n%s\nIteration count: %d\n' % (comment, iteration_count))
for _ in range(iteration_count):
threading.Event().wait(1)
print('Task: %s \t Iteration: %d \t Generated: %s' % (task_numb, _ + 1, generate_smth()))
print('\nTask %s completed!' % task_numb)
def generate_smth(size=6, chars=string.ascii_uppercase + string.digits):
# generate random
return ''.join(random.choice(chars) for _ in range(size))
# entry-point:
print('Just another example from SO')
app = App()
app.mainloop()
print('Beep')
如您所见 - 我从不卡在 mainloop
中(当我不需要它时),因为我在事件上创建线程:__init__
of "Spawner" (感谢继承)和一个按钮点击事件.当然,这只是许多方法中的一种,但我希望现在你的问题对你来说更清楚了.
As you see - I never get stucked (when I don't needed it) in mainloop
, because I create threads on events: __init__
of "Spawner" (thanks to inheritance) and a button click event. Of course, it's just one approach from many, but I wish that now your problem is clearer to you.