Forum: Ruby Printing right arrow vs. a sane progressbar library

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Cec345a59245af9d06e4438a413f4eb5?d=identicon&s=25 Shot (Piotr Szotkowski) (Guest)
on 2008-11-23 02:22
(Received via mailing list)
I want to track the progress of a set of (forkoff’d) processes in
a simple way. What I have in mind is a line of dots equal in length
to the number of processes, and once a process number X finishes,
it prints \r, then advances X characters to the right and prints o
– so after three of eight processes finish, it could look like ..o..oo.

The closest I got is the below (in one line, so easily pastable to IRB):

print '.' * 25; (0...25).sort_by{rand}.each { |n| print "\r"; print
"\c[[C" * n; print 'o'; sleep 0.5 }; print "\n"

It seems to work in IRB and in a script (after $stdout.sync = true),
unless the number of processes is larger than the terminal window’s
width.

Is this a sane method of doing what I want to do, or does
it just ‘happens to work’ in GNOME Terminal on my Ubuntu?

Is there a saner way to advance by a char than printing the right arrow?

On a similar note: Is there a simple progressbar library for Ruby
(ideally if itcan be somehow used with forkoff’d processes, but I’m
aware it’d require some kind of passing of state to the outside of the
block)?

Is there anything else than Ruby/ProgressBar¹ (say, in a gem…)?

¹ http://0xcc.net/ruby-progressbar/

(I’m aware the above is prone to a race condition if two processes
finish at the same time and the second one of them prints its \r
before the first one prints its o, but let’s disregard this for now.)

-- Shot
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2008-11-23 02:57
(Received via mailing list)
On Nov 22, 2008, at 8:18 PM, Shot (Piotr Szotkowski) wrote:

> print '.' * 25; (0...25).sort_by{rand}.each { |n| print "\r"; print
> arrow?
status='.'*25; print status; (0...25).sort_by{rand}.each{|n|
status[n]='o'; print "\r#{status}"; sleep(0.1+rand/10.0)};puts

While it still has trouble with more values that screen columns, it
should be a bit better with race conditions.  You could, perhaps, add
a clear-to-EOL sequence, too.

-Rob

>
> (I’m aware the above is prone to a race condition if two processes
> finish at the same time and the second one of them prints its \r
> before the first one prints its o, but let’s disregard this for now.)
>
> -- Shot
> --
> If I was to use Bugzilla when I was a tester, I'd quit
> and become a gardener.      -- a Syllable OS developer

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
Cec345a59245af9d06e4438a413f4eb5?d=identicon&s=25 Shot (Piotr Szotkowski) (Guest)
on 2008-11-23 18:36
(Received via mailing list)
Rob Biedenharn:

> On Nov 22, 2008, at 8:18 PM, Shot (Piotr Szotkowski) wrote:

>> I want to track the progress of a set of
>> (forkoff’d) processes in a simple way. […]

>> print '.' * 25; (0...25).sort_by{rand}.each { |n| print "\r"; print "\c[[C" * n; print 
'o'; sleep 0.5 }; print "\n"

> status='.'*25; print status; (0...25).sort_by{rand}.each{|n| status[n]='o';
> print "\r#{status}"; sleep(0.1+rand/10.0)};puts

A very good idea, but unfortunately it doesn’t work in a forkoff’d
block; every block gets its own copy of the original status and so
generates a line with 24 dots and one o:

shot@devielle:~$ irb -rubygems
>> require 'forkoff'
=> true
>> status='.'*25; print status; (0...25).sort_by{rand}.forkoff{|n| status[n]='o'; print 
"\r#{status}"; sleep(0.1+rand/10.0)};puts
..................o......
=> nil

Hence my need for a right arrow, or for printing a ‘transparent’
character that doesn’t overwrite what’s already under the cursor.

> You could, perhaps, add a clear-to-EOL sequence, too.

I’m sorry, but I’m a bit braindead today; where would I want to use
this? (And, when we’re at it, what’s the escape sentence for it?)

Thanks a lot for your interest in this thread and your reply!

-- Shot
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2008-11-23 23:27
(Received via mailing list)
On Nov 23, 2008, at 12:30 PM, Shot (Piotr Szotkowski) wrote:
>> status[n]='o';
>>> status[n]='o'; print "\r#{status}"; sleep(0.1+rand/10.0)};puts
> ..................o......
> => nil
>
> Hence my need for a right arrow, or for printing a ‘transparent’
> character that doesn’t overwrite what’s already under the cursor.

I took a quick look at the code for forkoff and unless you're
increasing the processes (or just looking for one that is "slow"),
this doesn't seem to help much.

>
>
>> You could, perhaps, add a clear-to-EOL sequence, too.
>
> I’m sorry, but I’m a bit braindead today; where would I want to use
> this? (And, when we’re at it, what’s the escape sentence for it?)

That sequence and other ANSI control sequences can be found via the
web, for example, at:
  http://en.wikipedia.org/wiki/ANSI_escape_code

> Thanks a lot for your interest in this thread and your reply!
>
> -- Shot
> --
> A distributed system is one in which I cannot get something done
> because
> a machine I've never heard of is down.                 -- Leslie
> Lamport



Perhaps a sequence of:
     "\[[s\[[#{n+1}Go\[[u"
if your terminal supports the save/restore cursor position

print '.' * 25; (0...25).sort_by{rand}.forkoff { |n| sleep(0.25+rand/
10.0); print "\033[s\033[#{n+1}Go\033[u" }; print "\n"


Or manually go back to the "end" of the line each time:
     "\033[#{n+1}Go\033[#{25+1}G"

print '.' * 25; (0...25).sort_by{rand}.each { |n| sleep(0.2+rand/2.0);
print "\033[#{n+1}Go\033[26G" }; print "\n"


Perhaps get the status via some other mechanism in another process
(but that's going to fly away from a "simple way" even faster).

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
Cec345a59245af9d06e4438a413f4eb5?d=identicon&s=25 Shot (Piotr Szotkowski) (Guest)
on 2008-11-24 01:05
(Received via mailing list)
Rob Biedenharn:

> On Nov 23, 2008, at 12:30 PM, Shot (Piotr Szotkowski) wrote:

>> A very good idea, but unfortunately it doesn’t work in a forkoff’d
>> block; every block gets its own copy of the original status and so
>> generates a line with 24 dots and one o […]

>> Hence my need for a right arrow, or for printing a ‘transparent’
>> character that doesn’t overwrite what’s already under the cursor.

> I took a quick look at the code for forkoff and unless you're
> increasing the processes (or just looking for one that is "slow"),
> this doesn't seem to help much.

Erm, I’m not sure what doesn’t seem to help much. :) Using the right
arrow trick works as expected (as opposed to editing a string that’s
unfortunately not the same object across the processes):

shot@devielle:~$ irb -rubygems
>> require 'forkoff'
=> true
>> print '.' * 25; (0...25).sort_by{rand}.forkoff { |n| print "\r"; print "\c[[C" * n; 
print 'o'; sleep 0.5 }; print "\n"
ooooooooooooooooooooooooo
=> nil

(Although it’s more prone to race conditions than your solutions below.)

In my actual case, I want a general mechanism that runs parallel
processes in some way (forkoff for now, maybe DRb in the future)
and report back some kind of a visual information on how much is
being done over time.

As I assume each of such parallel runs won’t be able to access the
actual objects outside the forked-off block (only copies of them),
I believe I can’t employ a simple, tick-based progressbar; the above
was my idea to side-step the need of communication with the parent
(although I could perhaps make the code that collects¹ the returned
values trigger/tick the progressbar as soon as a result comes in…).

¹ forkoff has this nice feature that it handles the collecting of the
returned values transparently, so I simply don’t *have* any code that
collects the results at the moment…

> That sequence and other ANSI control sequences can be found via the
> web, for example, at: http://en.wikipedia.org/wiki/ANSI_escape_code

Ah, thank you! I started this quest by recalling how \b and \r work,
which led me to the ASCII control characters; I totally forgot to
look at ANSI stuff, just recalled I can come up with the sequence
for the right arrow by `cat`ing stdin to a file and then looking
at it. I should’ve thought ‘ANSI!’ when I saw ^[[C. :)

> Perhaps a sequence of:
>     "\[[s\[[#{n+1}Go\[[u"
> if your terminal supports the save/restore cursor position

> Or manually go back to the "end" of the line each time:
>     "\033[#{n+1}Go\033[#{25+1}G"

Actually, a simple "\033[#{n+1}Go" works well enough; thanks! The trick
with going to the end of the line each time makes it easier on the eyes
if the cursor is visible, so I’ll most probably end up using it. Thanks
again!

(And just before sending this email I found out that "\033[?25l" makes
the cursor disappear, and so seems to be the cleanest solution without
any output-related race conditions whatsoever.) :)

-- Shot
This topic is locked and can not be replied to.