Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tqdm.write(string) doesn't work well when string has \n in it #564

Open
half1red opened this issue Jun 6, 2018 · 6 comments
Open

tqdm.write(string) doesn't work well when string has \n in it #564

half1red opened this issue Jun 6, 2018 · 6 comments
Assignees
Labels
question/docs ‽ Documentation clarification candidate

Comments

@half1red
Copy link

half1red commented Jun 6, 2018

Hi,
I'am using tqdm 4.23.4 on python 3.6.3.

I noticed what I think might be an issue when I use tqdm.write with a multiline string.
The two snippets below don't work the same way (the correct behavior I think is the second one) :

from time import sleep
from tqdm import tqdm, trange

def test():
    for x in tqdm(range(30)):
        sleep(0.05)
        
for x in trange(10):
    tqdm.write("\ntest " + str(x))
    test()
from time import sleep
from tqdm import tqdm, trange

def test():
    for x in tqdm(range(30)):
        sleep(0.05)
        
for x in trange(10):
    tqdm.write("")
    tqdm.write("test " + str(x))
    test()

Also, but that may be an other issue, I haven't find a way to have nested progress bars with print between bar that would remain plotted (and not print above all the bars).

@half1red half1red changed the title tqdm.write(string) doesn't work well when string as \n in it tqdm.write(string) doesn't work well when string has \n in it Jun 6, 2018
@casperdcl
Copy link
Sponsor Member

related: #520 (end != "\n"), #398, #467.

I think the problem here is you don't clear the nested bar or print a newline after it.

You need:

def test():
    for x in tqdm(range(30), leave=False):
        sleep(0.05)

A minimal reproduction of your problem:

from tqdm import trange

for i in trange(10):
    for j in trange(10):
        pass

# print("\n")
print("hello")  # prints on top of last nested bar.

@casperdcl casperdcl self-assigned this Jun 7, 2018
@casperdcl casperdcl added the question/docs ‽ Documentation clarification candidate label Jun 7, 2018
@half1red
Copy link
Author

half1red commented Jun 7, 2018

Thanks for looking.
I'm not sure we are speaking about the same issue.
The problem I encountered happens when there is a multiline (\n in it) string in tqdm.write() :

from tqdm import trange

for i in trange(3):
    tqdm.write("task " + str(i) + " started")
    for j in trange(4):
        pass

works.

But with

    tqdm.write("task " + str(i) + " done\nand well started")

doesn't.

However I may have misunderstood your answer. If so I'm sorry.

@casperdcl
Copy link
Sponsor Member

The issue in your new example is the same - the inner trange(4) needs a leave=False.

@half1red
Copy link
Author

half1red commented Jun 8, 2018

Actually, putting leave=false delete the inner progress bar, which is a different behavior.
What I thought, is that :

    tqdm.write("task " + str(i) + " done\nand well done")

would behave in the same way that :

    tqdm.write("task " + str(i) + " done")
    tqdm.write("and well done")

My guess is that in the second snippet, the code knows that it has to climb up two lines, in the first one, it thinks only one is enough.

In a second time, but I think it's a clearly different, more complex subject, it would be nice to be able to keep all the inner progress bar (for example to keep the stats) while inserting text between them. This issue is linked I think to #230 :

outer: 78%|███████████ | 2/3 [00:00<00:00, 6189.33it/s]
Starting inner1
inner: 100%|██████████████| 4/4 [00:00<00:00, 164482.51it/s]
Starting inner2
inner: 100%|██████████████| 4/4 [00:00<00:00, 164482.51it/s]
Starting inner3
inner: 80%|██████████ | 4/4 [00:00<00:00, 164482.51it/s]

@casperdcl
Copy link
Sponsor Member

casperdcl commented Jun 10, 2018

if you want all nested bars to remain, then you can manually position them:

from tqdm import tqdm, trange
from time import sleep


def test(position=None, bar=None):
    if bar is None:
        bar = trange(30, position=position)
    for x in bar:
        sleep(0.05)


N = 10

then either:

for x in trange(N):
    test(position=x + 1)
print('\n' * (N - 1))

or pre-allocate:

bars = [trange(30, position=i +1, desc=str(i + 1)) for i in range(N)]
for x in trange(N):
    #tqdm.write("test")
    #tqdm.write("test"); tqdm.write("foo")
    #tqdm.write("test\nfoo")
    test(bar=bars[x])
print('\n' * (N - 1))

However in both cases tqdm.write() doesn't work well - which is an issue (#230)

@BrandonSmithJ
Copy link

Here's a solution:

from tqdm import trange 
import time 

def line_messages(messages):
    for i, m in enumerate(messages, 1):
        trange(1, desc=str(m), position=i, bar_format='{desc}')

n_messages = 2
for i in trange(3):
    line_messages(['iter: %s' % i, 'half: %s' % (i/2)])
    time.sleep(1)

for _ in range(n_messages): print()

Preview:

33%|█████████████████████                                  | 1/3 [00:01<00:02,  1.00s/it]
iter: 1
half: 0.5

You can switch the bar to appear below the messages by changing the position parameters of the two trange declarations (i.e. outer one should be length of messages, inner should start at zero, print n_messages+1 at the end).

The downside is having to print newlines after the overall loop to keep the cursor in the correct place afterwards. Also, haven't tested on windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question/docs ‽ Documentation clarification candidate
Projects
None yet
Development

No branches or pull requests

3 participants