2017-05-04 8 views
1
)

나는 어떤 클래스의 함수를 사용하는 스레드가 있는데,이 함수는 Text() 위젯에 표시하고자하는 것들을 많이 인쇄한다. 메인 루프와 텍스트 (

가에 대한 해결책이 .... (가) 계속 이르기까지 모든 것을 중지 할 것 돌이 :

그래서 내가 클래스 변수와 명령과 클래스의 창을 만들려고?

내가하고 싶은 일반적인 생각 : (GUI 콘솔을 변환 ..)이

from tkinter import * 


root = Tk() 
textbox = Text(root) 
textbox.pack() 

def redirector(inputStr): 
    textbox.insert(INSERT, inputStr) 

sys.stdout.write = redirector 
root.mainloop() 

전체 코드 : 즉시 당신이 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

, 당신이 얻을 루프에서 실행되는 이벤트 기반 앱 root.mainloop() 행 뒤에있는 모든 코드는 GUI가 종료 된 후에 만 ​​실행됩니다. GUI가 어느 정도 자체적으로 포함될 것으로 예상됩니다. 적절한 이벤트 콜백 함수를 가진 각 이벤트를 바인드 할 tkinter 위젯으로 채 웁니다.

그러나 tkinter는 스레드로부터 안전하지 않습니다. 예를 들어, GUI 위젯을 호출하지 않도록 코드를 잘 분리해야합니다. this page에서는 tkinter로 스레딩하는 방법에 대한 Python2 예제를 찾을 수 있습니다.

하지만 스레드가 필요하지 않을 수도 있습니다. 예를 들어 after()으로 X 초마다 실행되는 함수를 예약 할 수 있습니다. 업데이트 된 로그 파일을 읽거나 데이터베이스에서 업데이트 된 값을 가져오고 이에 따라 GUI를 업데이트 할 수 있습니다. this page에서 몇 가지 예와 설명을 찾을 수 있습니다.

+0

ye 그래서 메인 스레드에 있어야한다는 오류가 발생했습니다 ... 무슨 뜻입니까? –

+0

제공된 코드 샘플을 사용하여 문제를 재현 할 수 없습니다 ... 실행되고 tkinter 창에 빈 텍스트 위젯이 표시됩니다. 당신이 코딩 한 것처럼 행동하고 있습니다, 나는 믿습니다. 이제 원하는 것이 콘솔에 나타나는 모든 것을 실시간으로 보여주고 사용자 입력에 반응하는 텍스트 위젯을 사용하는 것이 훨씬 더 복잡한 작업이 될 것입니다. 가능한지 확실하지 않지만 한계가있을 수 있습니다. –

+0

예, 내 메인 코드를 링크하지 못했습니다. 단지 예제가 잘못되었습니다. 1 초 –

1

@ Victor Domingos의 언급은 귀하의 경우에 정말 유용하지만 실제 문제는 - 귀하의 코드입니다! 우선 - 애플리케이션의 구조를 살펴보고 취약한 약점이 있다는 것을 이해하십시오 (심지어 masterdestroy 함수로 전달). 따라서 파이썬에서 클래스와 상속에 대해 읽은 다음 (아직하지 않은 경우) here을 보도록 제안합니다.

다음 중지 - 리디렉터. sys.stdout.write을 재 할당하지만 보존하지 않으므로 또 다른 약점이 있습니다. 좋습니다, 이제는 보존한다고 가정 해 봅시다. 그러나 우리가 객체 지향 접근법을 유지한다면 - 나는 this 옵션을 선호 할 것입니다.

또한 실제로 destroymaster이 필요합니까? 출력의 경우 을 삭제하면 두 개의 mainloop을 피하기 위해 Toplevel 위젯을 사용할 수 있습니다. You can even hide root while Toplevel is active. 기이 한가?

마지막으로 솔루션에 대한 질문에 대답하십시오. 직접적인 해결책은 없지만 읽기와 시도 만 가능합니다. 당신은 이미 대답했습니다 mainloop는 모든 것을 멈추지 만 귀하의 질문은 정말 광범위합니다.

# 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') 

보시다시피 : -

내가 전체 프로그램을 재현 해 보았습니다 (2 윈도우 응용 프로그램은, 1 사용자 입력, 2 콘솔과 같은 및 스레드로 작업을 인쇄하는 몇 가지 예) 여기에는 코드입니다 - 에 이벤트가 발생하여 "Spawner"(상속 덕분에) __init__ 및 버튼 클릭 이벤트가 발생하기 때문에 붙여 넣기가 필요하지 않습니다.물론, 많은 사람들로부터 하나의 접근법 일뿐입니다.하지만 이제는 문제가 더 분명해지기를 바랍니다.

+0

정말 고맙습니다 :) TopLevel 앱은 무엇을 의미합니까? 수업을 어떻게 사용합니까? , 나는 정말로 무엇을 .. 그리고 나는 destory master를 이해할 수 없으므로 닫는다. –

+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

나는 UR 코드를 이해하지만 그게 정말 열심히 내가 그것을 사용하기 때문에 정말로 무엇을하고있어 ... 내가 무엇을 삭제하고 내 자신의 코드를 넣을 지 말해 줄 수 있을까? 이 코드에서 잃어버린 메신저 때문에 –