2017-05-04 66 views
1

我有使用某些类的函数的线程,并且这些函数打印了很多东西,我想要在Text()小部件上显示。主线程和带线程的文本

所以我试图使该窗口的类为类变量和命令:主循环()似乎停止一切从持续....

是否有任何解决方案?

总体思路我想要做的:(控制台转换为图形用户界面。)

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

回答

1

一旦你开始mainloop(),你会得到一个事件驱动的应用程序,在循环中运行。任何放在root.mainloop()行之后的代码只会在GUI终止后才能运行。预计你的GUI或多或少是自包含的。你用tkinter小部件填充它,这些小部件将绑定一些事件,每个事件都有其适当的回调函数。

但请注意,tkinter不是线程安全的。例如,您需要非常好地分隔引导代码,确保它不调用任何GUI小部件。在this page中,您可以找到关于如何使用tkinter进行线程处理的Python2示例。

但也许你甚至不需要线程。例如,您可以安排一个函数,每012秒运行一个函数,可以读取更新的日志文件或从数据库获取更新的值,并相应地更新GUI。你可以在this page找到一些例子和解释。

+0

咋,所以我得到了这个错误,它需要在主线程中...这是什么意思? –

+0

我无法用您提供的代码示例在这里重现您的问题......它运行并在tkinter窗口中显示一个空文本小部件。我相信它的行为与你编码的一样。现在,如果你想要的是有一个文本小部件实时显示无论在控制台中显示的任何内容,并且还对用户输入作出反应,它将是一个更复杂的任务。我不确定是否有可能,但可能有一些限制。 –

+0

是我的坏我没有链接我的主代码....这只是例子虐待编辑它1秒 –

1

@Victor Domingos的提及在你的案例中非常有用,但是你真正的问题 - 你自己的代码!首先 - 看看你的应用程序的结构和理解,它很弱,没有进攻(你甚至通过masterdestroy函数)。所以我建议你阅读关于Python中的类和继承(如果你还没有),然后看看here

下一站 - 您的重定向器。你重新分配sys.stdout.write,但你永远不会保留它 - 所以它是另一个弱点。好吧,让我们说,现在你保存它,但如果我们保持面向对象的方法 - 我宁愿this选项。

另外,是否真的有必要destroymaster?对于输出,如果您为了避免两个mainloop而销毁master,则可以使用Toplevel小部件。 You can even hide root while Toplevel is active。奇妙,不是吗?

最后,回答你关于解决方案的问题。没有直接的解决方案,但只有一个:阅读并尝试。你已经回答了为什么mainloop停止了一切,但你的问题是非常广泛的。

我试图重现完整的程序(2窗口的应用程序,第一个用户输入,2日 - 控制台类和一些例如打印螺纹任务),这里是代码:

# 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__“Spawner”(感谢继承)和一个按钮点击事件。当然,这只是众多方法之一,但我希望现在你的问题对你更清楚。

+0

非常感谢评论:) TopLevel应用程序是什么意思?什么是类的使用? ,我真的不明白为什么..和我destory掌握,所以我关闭... –

+0

'Toplevel'只是另一个tkinter部件像按钮或条目。 [一般来说tkinter有很好的材料](http://effbot.org/tkinterbook/)。你关于课程的问题也非常广泛。但是这里有一个[另一个链接](https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/)。如果它看起来像你想要的,随时重写我的代码! – CommonSense

+0

我理解你的代码,但它真的很难使用它,因为它不是真的接近我在做什么......你可以告诉我要删除什么以及在哪里放置我自己的代码?因为我喜欢在这个代码中丢失 –