I have a Ruby script that does some long taking jobs. It is command-line only and I would like to show that the script is still running and not halted. I used to like the so called "spinning cursor" in the old days and I managed to reproduce it in Ruby under Windows.
Question: does this work in the other OS's? If not, is there an OS-independent way to accomplish this?
No IRB solutions please.
10.times {
print "/"
sleep(0.1)
print "\b"
print "-"
sleep(0.1)
print "\b"
print "\\"
sleep(0.1)
print "\b"
print "|"
sleep(0.1)
print "\b"
}
Yes, this works on Windows, OS X, and Linux. Improving on Niklas' suggestion, you can make this more general like so:
def show_wait_cursor(seconds,fps=10)
chars = %w[| / - \\]
delay = 1.0/fps
(seconds*fps).round.times{ |i|
print chars[i % chars.length]
sleep delay
print "\b"
}
end
show_wait_cursor(3)
If you don't know how long the process will take, you can do this in another thread:
def show_wait_spinner(fps=10)
chars = %w[| / - \\]
delay = 1.0/fps
iter = 0
spinner = Thread.new do
while iter do # Keep spinning until told otherwise
print chars[(iter+=1) % chars.length]
sleep delay
print "\b"
end
end
yield.tap{ # After yielding to the block, save the return value
iter = false # Tell the thread to exit, cleaning up after itself…
spinner.join # …and wait for it to do so.
} # Use the block's return value as the method's
end
print "Doing something tricky..."
show_wait_spinner{
sleep rand(4)+2 # Simulate a task taking an unknown amount of time
}
puts "Done!"
This one outputs:
Doing something tricky...|
Doing something tricky.../
Doing something tricky...-
Doing something tricky...\
(et cetera)
Doing something tricky...done!
# First define your chars
pinwheel = %w{| / - \\}
# Rotate and print as often as needed to "spin"
def spin_it
print "\b" + pinwheel.rotate!.first
end
EDIT from peter: here a working version
def spin_it(times)
pinwheel = %w{| / - \\}
times.times do
print "\b" + pinwheel.rotate!.first
sleep(0.1)
end
end
spin_it 10
I wrote a gem spin_to_win that displays a spinner while yielding a block. For example:
SpinToWin.with_spinner('Zzzz') do |spinner|
spinner.banner('sleepy')
sleep 1
end
Zzzz \ [sleepy]
It can also track work pending vs. work completed:
SpinToWin.with_spinner('Zzzz') do |spinner|
spinner.increment_todo!(3)
spinner.banner('snore')
sleep 1
spinner.increment_done!
spinner.banner('dream')
sleep 1
spinner.increment_done!
spinner.banner('wake up!')
sleep 1
spinner.increment_done!
end
Zzzz \ 3 of 3 [wake up!]
I use rainbow gem to print color-changing string to indicate the code is working.
require 'rainbow'
def self.print_with_random_color
content = "I am still working on it. Please wait..."
colors = ["aqua","chartreuse","crimson","fuchsia","gold","lawngreen","palegoldenrod","powderblue","sandybrown","deepskyblue"]
loop do
print Rainbow("[#{Time.now}] " + content).send(colors.sample)
(content.length + 1).times {print "\r"}
sleep 0.3
end
end
Related
Hello how can I make the three dot loading (...), so that it does something like this except without making a new line everytime:
Loading
Loading.
Loading...
Loading..
Loading.
and so on:
This is what I came up with, only problem is that I can't remove the dots one by one if loading continues for more than 1.5 seconds.
puts "Sending"
sleep(0.5)
print "."
sleep(0.5)
print "."
sleep(0.5)
print "."
Hopefully I am clear enough.
Thank you for your help.
10.times do |i|
print "Sending." + ("." * (i % 3)) + " \r"
$stdout.flush
sleep(0.5)
end
So, how does this work?
10.times do |i|
Repeat the following code 10 times, with the variable i indicating the current iteration (0, 1, 2, 3, 4...)
print "Sending." + ("." * (i % 3)) + " \r"
Print the phrase "Sending." followed by a couple of things:
("." * (i % 3)) repeats ("multiplies") the string "." several times, specifically i % 3, which is the remainder of i when it's divided by 3. So when i is 7, i % 3 is 1.
"\r" moves the cursor back to the beginning of the line without making a new line. If you think of a typewriter, it's like returning the carriage (the bit that types) to the beginning of the line so that you can type over the same line again. It's a carriage return.
$stdout.flush
Make sure the data prints.
sleep(0.5)
Sleep for half a sec.
You can try with $stdout.flush:
def loading number_of
number_of.times do |t|
print "Sending#{'.' * (t % 4)} \r"
$stdout.flush
sleep 0.5
end
end
Demonstration
Does anyone know how to create a small delay (perhaps 0.1 seconds) between the printing of each character in a string in Ruby?
I have tried some other methods (shown below); however, I'm certain that a simpler method exists as the method im using is time (and space) consuming:
#ugly/boring method vvv
print "h"
sleep 0.1
print "e"
sleep 0.1
print "l"
sleep 0.1
print "l"
sleep 0.1
print "o"
I have done some research on the topic and found a method under C and php but no one seems to have an answer for Ruby.
Thanks in advance for the help.
"hello".each_char do |c|
sleep 0.1
print c
end
Docs: String#each_char
You could also monkey-patch the string class:
class String
def print_slowly
self.each_char do |c|
sleep 0.1
print c
end
end
end
#usage
"hello".print_slowly
A super simple approach would be something like this:
"hello".split(//).each do |c|
sleep 0.1
print c
end
Here's a more verbose explanation:
string = "hello"
split_that_string = string.split(//);
for character in split_that_string
sleep 0.1
print character
end
You could also make a method for it:
def print_a_string_one_character_at_a_time(string)
array_of_characters = string.split(//);
for character in array_of_characters
sleep 0.1
print character
end
end
and then you can pass any string to that method. Here's an example:
print_a_string_one_character_at_a_time("Hello, World!")
That will type out Hello, World! one character at a time (and return the array of characters).
I wrote ruby script like below.
puts a and echo of gets are mixed.
how can I arrange print value 'a' on terminal position 0,0
and gets on position y=10, x=0?
thanks.
Thread.new do
a = 1
loop do
puts a
a += 1
sleep 0.5
end
end
loop do
gets
end
For this answer I wrote code like:
def show_wait_spinner
dirty = false
spinner = Thread.new{
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
}
yield
spinner.kill
print "\b" if dirty
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
The goal is to ensure that the final output was "AB"—to print a final "\b" if it was not already printed by the thread. That code seems messy to me in Ruby where begin/rescue/ensure exists. So I tried some other implementations of show_wait_spinner; all of them fail to ensure that "AB" is always the output, and never "A*B" or "AB*".
Is there a cleaner, more Ruby-esque way to implement this logic?
Stop at end of loop via Mutex
def show_wait_spinner
stop = false
stopm = Mutex.new
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
stopm.synchronize{ break if stop }
}
}
yield
stopm.synchronize{ stop = true }
STDOUT.flush
end
…but my logic must be off, since this always results in "A*B".
Stop at end of loop via Thread-local variable
This second attempt results in sometimes "A*B" being printed, sometimes "AB":
def show_wait_spinner
stop = false
spinner = Thread.new{
Thread.current[:stop] = false
loop{
print "*"
sleep 0.1
print "\b"
stopm.synchronize{ break if Thread.current[:stop] }
}
}
yield
spinner[:stop] = true
STDOUT.flush
end
Kill and Ensure the Thread
def show_wait_spinner
spinner = Thread.new{
dirty = false
begin
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
ensure
print "\b" if dirty
end
}
yield
spinner.kill
STDOUT.flush
end
Raise and Rescue the Thread
def show_wait_spinner
spinner = Thread.new{
dirty = false
begin
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
rescue
puts "YAY"
print "\b" if dirty
end
}
yield
spinner.raise
STDOUT.flush
end
Instead of killing your thread, why don't you flip a variable that causes it to stop at a pre-defined point? If you let it cycle through and exit at the end of the loop you won't have nearly as much trouble.
For instance:
def show_wait_spinner
running = true
spinner = Thread.new do
while (running) do
print "*"
sleep 0.1
print "\b"
end
end
yield
running = false
spinner.join
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
When you call Thread#kill you have no idea where the thread is, and the thread isn't given an opportunity to clean up what it's doing. You can always kill the thread if your polite "stop running" request isn't respected.
I prefer your synchronized stop condition approach but you have a couple bugs:
after you set the stop variable, you almost immediately end the
program, so the thing that stops the thread is program exit, not the
conditional test in the loop; use Thread#join to wait for the thread
to exit normally and you'll get the consistent output you want.
break in the synchronize block breaks out of the block, not the
loop.
def show_wait_spinner
stop = false
stopm = Mutex.new
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
break if stopm.synchronize{ stop }
}
}
yield
stopm.synchronize{ stop = true }
spinner.join
STDOUT.flush
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
I would avoid any solution involving Thread#raise and Thread#kill as their behavior can never be predictable and correct, see Charles Nutter's rant about the brokenness of these methods.
The Mutex#synchronize is only necessary for this simple boolean flipping if you really care a lot about the precise timing around the race condition when the parent thread sets the var, which in this example isn't likely, so you could avoid the overhead and just set and read stop normally.
In your mutex example, you need to wait for the Thread to finish before exiting the method. Currently you set stop to true, then exit the method, print B and end your script before the spinner thread is able to wake up and print the last backspace to delete the * character.
Also, the break in stopm.synchronize{ break if stop } only exits the inner block, not the loop, so you need to use catch/throw or something.
However, you don't need the mutex. This works for me in 1.9.3:
def show_wait_spinner
exit = false
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
break if exit
}
}
yield
exit = true
spinner.join
end
Adding $stdout.sync = true at the top makes it work in 1.8.7.
I am using Ruby.
I have a string with multiple lines. I want to print that multiple-lines-string in the same place multiple times.
The reason I want to do that is because the string represents information that is going to be updated several times.
How can I accomplish this?
As an option:
3.times do |i|
print str = "string#{i}\nstring#{i}\nstring#{i}\n"
sleep 1 # just for test
system('clear')
end
You could use uses curses to handle your output but that would probably be overkill for something simple like this.
The usual way is to print out a bunch of backspaces to reposition the output cursor at the beginning of your last string; be aware that "\b" doesn't necessarily overwrite anything so you'll have to overwrite the end with spaces to be safe. Something like this:
messages = [
'Professionally simplify seamless systems with prospective benefits.',
'Dramatically cultivate worldwide testing procedures for robust potentialities.',
'Intrinsicly provide access to future-proof testing procedures after superior supply chains.',
'Globally matrix multidisciplinary outsourcing vis-a-vis distributed paradigms.',
'Compellingly fashion visionary content via functionalized web services.',
'Phosfluorescently e-enable e-markets rather than internal or "organic" sources.'
]
reposition = ''
clear_the_end = ''
(0 ... messages.length).each do |i|
if(i > 0)
clear_the_end = ' ' * [0, messages[i - 1].length - messages[i].length].max
end
$stdout.syswrite(reposition + messages[i] + clear_the_end)
reposition = "\b" * (messages[i].length + clear_the_end.length)
sleep 1
end
puts
You'll want to use syswrite to avoid buffering and the usual "\n" that puts appends. This sort of thing should work in any terminal that you're likely to come across.
You could also use a carriage return ("\r") instead of a bunch of backspaces:
# `reposition = "\b" * (messages[i].length + clear_the_end.length)` becomes
resposition = "\r"
But you'll still need all the clear_the_end fiddling to make sure you overwrite all of the last line.
This will work however in Terminal.app on Mac OS X Lion the 1 character of each line is cleared on exit. I think this is a bug of Terminal.app.
CR = "\r"
CLEAR = "\e[0J"
RESET = CR + CLEAR
lines_count = 3
3.times do |i|
puts "First line #{i}"
puts "Second line #{i}"
puts "Third line #{i}"
sleep(1)
$stdout.flush
lines_count.times do
print "\e[1F"
print "\e[1K"
end
end
print "\e[#{lines_count}E"