主循环和带线程的文本

2024-09-28 20:40:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一些线程使用一些类的函数,这些函数打印很多我想在Text()小部件上显示的东西。在

所以我尝试将类中的窗口作为类变量,而命令:mainloop()似乎阻止了一切继续。。。。在

有什么解决办法吗?在

我想做的基本思想是:(将控制台转换为GUI…)

from tkinter import *


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

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

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

整个代码:

^{pr2}$

Tags: 函数textfrom命令部件guiroot线程
2条回答

一旦启动mainloop(),就会得到一个循环运行的事件驱动应用程序。行后的任何代码根.mainloop()将仅在GUI终止后运行。您的GUI应该或多或少是自包含的。用tkinter小部件填充它,这些小部件将绑定一些事件,每个事件都有适当的回调函数。在

但是请注意,tkinter不是线程安全的。例如,您需要很好地分离编码代码,确保它不会调用任何GUI小部件。在this page中,您可以找到一个Python2示例,介绍如何使用tkinter执行线程。在

但也许你根本不需要线程。例如,您可以使用after()来计划每X秒运行一次函数,它可以读取更新的日志文件或从数据库中获取更新的值,并相应地更新GUI。您可以在this page中找到一些示例和说明。在

@Victor Domingos的提到对您的情况非常有用,但是您真正的问题是您自己的代码!首先,看看你的应用程序的结构,明白它很弱,没有冒犯性(你甚至把一个master传递给一个函数来destroy它)。所以我建议您阅读Python中的类和继承(如果您还没有),然后看看here。在

下一站-你的重定向器。您重新分配了sys.stdout.write,但从未保留它-因此它是另一个弱点。好吧,假设现在您保留了它,但是如果我们保持面向对象的方法-我更喜欢this选项。在

另外,真的有必要destroy这个master?对于输出,您可以使用一个Toplevel小部件,如果您删除master只是为了避免两个mainloopYou can even hide ^{} while ^{} is active。太棒了,不是吗?在

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

我试着复制你的完整程序(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中被绊倒(当我不需要它的时候),因为我在事件上创建线程:Spawner的__init__(多亏了继承)和一个按钮点击事件。当然,这只是许多方法中的一种,但我希望现在你的问题对你更清楚些。在

相关问题 更多 >