简体   繁体   中英

Using sys.stdout.write(“\r…”) in tkinter

I have a small script that processes many thousand small files. The operation takes a while, so the program outputs progress to the console. It's simulated in the code below:

files = 5000 #Number of files
    for i in range(files):
        sys.stdout.write("\r{0} of {1}".format(i, files))

I like this way of outputting progress; only a single line is printed in the console. However now I want to run this script from a GUI (tkinter), and I want the output to be printed to a statusbar (a label). I'm a bit at loss right now how to implement this. Is there a way I can handle the output in the GUI without editing the script?

While you can intercept stdout and do whatever you want with it, this is a bad idea—and, even if you do that, your program doesn't make sense in the first place.

You can't run an operation that "takes a while" in a GUI app. While the operation is running, your GUI's main loop is not running, which means the entire GUI is frozen. Besides giving you a beachball, this also means that any changes you make won't be visible until you return.

What if you did the operation in another thread? Well, then it wouldn't prevent the GUI from running… but you can't talk to Tkinter widgets from any thread but the main thread, so that doesn't help.

So, what you need to do is do a single step of your operation at a time, each time scheduling the rest of the operation as a callback to do later.

For example:

class StatusWriter(object):
    def write(self, msg):
        statusbar.config(text=msg[1:])

def count():
    sys.stdout = StatusWriter()
    i = 0
    def step():
        nonlocal i
        sys.stdout.write("\r{} of {}".format(i, 50))
        i += 1
        if i == 50:
            sys.stdout = sys.__stdout__
        else:
            root.after(100, step)
    root.after(100, step)

But really, once you're rewriting things anyway, it makes a lot more sense to also remove the stdout-replacing hackery:

def count():
    sys.stdout = StatusWriter()
    i = 0
    def step():
        nonlocal i
        statusbar.config(text="{} of {}".format(i, 50))
        i += 1
        if i == 50:
            sys.stdout = sys.__stdout__
        else:
            root.after(100, step)
    root.after(100, step)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM