Closed TheFox closed 7 years ago
@TheFox: A friend of mine (@brucec5) helped me troubleshoot the same exact issue. Since StringIO
does not have a window size (not a terminal), the format of the output goes back to a default formatter, which is exactly the output you are seeing!
Relevant code is here: https://github.com/jfelchner/ruby-progressbar/blob/master/lib/ruby-progressbar/outputs/non_tty.rb#L29
One ~Your~ solution would be to override the non_tty file, add the correct formatter, and specify a (hopefully conservative) window size.
Hope this helps!
Props to @brucec5 !!!!
UPDATE:
Additionally, the output is determined on creation of the progress bar here
To go a step further, here is a sample implementation that will:
to_s
with a specified formattermodule Example
class ProgressBar
PROGRESS_FORMAT = "Progress: [%B] [%c/%C]".freeze
# This is required. This is our fake output class that will handle any
# output and shove it straight into the void.
class FakeOutput
def self.tty?() true end
def self.print(str) str end
def self.flush() self end
end
def initialize(total)
@bar ||= ::ProgressBar.create(
total: total,
format: PROGRESS_FORMAT,
# TODO: Specify the length of the progress bar! This is required.
length: 100,
output: FakeOutput
)
end
def progress(amount = 1)
@bar.progress += amount
puts @bar
end
end
end
And the associated usage:
>> bar = Example::ProgressBar.new(100)
=> #<Example::ProgressBar:0x007fea5d6940c0 @bar=#<ProgressBar:0/100>>
>> bar.progress
Progress: [ ] [1/100]
>> bar.progress
Progress: [= ] [2/100]
>> bar.progress
Progress: [== ] [3/100]
>> bar.progress(3)
Progress: [==== ] [6/100]
Can you all elaborate on the use case here? I have to admit this looks completely convoluted to me 😂
What is the point of getting the actual string of the progress bar rather than querying the progress bar object itself?
@dannypaz Thank you for this informations. I have not tested your code yet. I will. Just to be correct, the object is called StringIO
instead of String.IO
as mentioned in your first comment. ;)
@jfelchner My use case: I'm making a time tracking CLI tool, called Timr. Where you can create tasks and an estimation for each task. When you add an estimation to a task you also get an progressbar on the info page to visualize the time left you have for this task. Since Timr is no interactive programm I do not need a "#{progressbar}\r"
-loop rather than one single "#{progressbar}\n"
string. It's like (the most) Git commands: you start it, the process prints informations (for example how far the progress is and a progressbar) and then the process ends. I do not need any "\r"
s or "\n"
s in my progressbar string because I do not want to explicitly show the progressbar. I also want to print other informations to the user, before and after the progressbar.
@jfelchner I don't blame your feelings. The use-case is having progress bar/status in a system log with code containing multiple ruby forks/subprocesses.
My reasoning for this code is because non-tty does not support custom formatters, which is what myself and @TheFox were trying to use. We would see the correct output in a terminal window, but once exported, either through StringIO
or into a file, it would change due to .detect
.
tty vs. non-tty is something that makes sense, but was not the behavior I was expecting.
@TheFox @dannypaz your use case is extremely specific and not something I'd generally change the codebase for, but here's what I'm willing to do for you all (because it's very little code change, is fairly general, and has almost zero performance cost).
I can change Output.detect
to check to see if options[:output]
is a subclass of ProgressBar::Output
. If it is, it will instantiate it with all the options that the current output classes receive. In this way, you can create whatever kind of outputter you'd like (as long as it subclasses ProgressBar::Output
). If you want to do something extremely specific (as you seem to be doing), all you'll have to do is override the appropriate methods and you're off to the races.
For example:
class MyOutputter < ProgressBar::Output::Tty
def clear
end
# You will be REQUIRED override this if you want something other than the default IO stream
def stream
@stream = whatever_your_desired_io_object_is
end
def default_format
'Progress: [%B] [%c/%C]'
end
def eol
''
end
end
@bar ||= ::ProgressBar.create(
total: total,
output: MyOutputter
)
@bar.progress += 1 # this would output the bar to the stream with (I think) no `\r` or `\n`
Or whatever. :)
How's that sound?
Technically if you wanted to get super crazy you could override initialize
.
I want to be super clear though, I will make no attempt to follow Semver on the outputters. I will change the interface to whatever I want, whenever I want. I take Semver very seriously and I've managed to go years without a backward-incompatible change. I won't tie my hands for this small use case.
That said, the interface for the outputters hasn't changed in a couple years so you're fairly safe.
@TheFox @dannypaz I didn't hear back from you all, but it was a minor change so it's going in 1.9 anyway.
Hope this helps.
@jfelchner Sorry for not responding anymore. Unfortunately, I already hadn't the chance to test it. I implemented an simple version of a progressbar for my project. Thank you for your help.
@TheFox no problem. I actually I went ahead and went a step further for you.
I added a Null
outputter, which, if passed into the progress bar like so:
require 'ruby-progressbar/outputs/null'
progressbar = ProgressBar.create(output: ProgressBar::Outputs::Null)
will disable all outputting of the progressbar completely (or should anyway 😉). For this class I will make sure it's compatible if there are any changes to the base Output
class, so it's safe to use.
If you use the above code, you should be able to still use progressbar.to_s
to get the static bar for the purposes you need it for.
Hope this helps.
Nice! Thank you.
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
I do not want to print it to STDOUT. I want only one String of the bar.
So I tried this:
I want to use
bar_s
for further actions. But it's at progress 0%. Even if I use:starting_at => 4
the value is----------
.So I changed it to this:
bar_s
is now####------
but I don't want to print it to STDOUT on executingbar.progress = 4
.So I changed it to this:
As expected it doesn't print anything to STDOUT anymore, but
bar_s
is nowProgress: ||
. It ignores all options. Why? It has no progress. Why? It ignoresbar.progress = 4
. Why?Even if I read it from
sio
it's wrong:bar_s
has now\x0a\x0aProgress: |
as value.Is StringIO the wrong type to just write the bar to a String? How can I get the bar as one single String?