tty-progressbar 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +302 -57
- data/examples/failure.rb +14 -0
- data/examples/iterator.rb +7 -0
- data/examples/multi/main_bar.rb +17 -0
- data/examples/multi/simple.rb +15 -0
- data/examples/threaded.rb +16 -0
- data/lib/tty-progressbar.rb +3 -2
- data/lib/tty/progressbar.rb +221 -58
- data/lib/tty/progressbar/configuration.rb +4 -0
- data/lib/tty/progressbar/converter.rb +2 -1
- data/lib/tty/progressbar/formatter.rb +2 -1
- data/lib/tty/progressbar/formatter/bar.rb +3 -1
- data/lib/tty/progressbar/formatter/byte_rate.rb +2 -1
- data/lib/tty/progressbar/formatter/current.rb +2 -1
- data/lib/tty/progressbar/formatter/current_byte.rb +2 -1
- data/lib/tty/progressbar/formatter/elapsed.rb +2 -1
- data/lib/tty/progressbar/formatter/estimated.rb +2 -1
- data/lib/tty/progressbar/formatter/mean_byte.rb +2 -1
- data/lib/tty/progressbar/formatter/mean_rate.rb +2 -1
- data/lib/tty/progressbar/formatter/percent.rb +2 -1
- data/lib/tty/progressbar/formatter/rate.rb +2 -1
- data/lib/tty/progressbar/formatter/total.rb +2 -1
- data/lib/tty/progressbar/formatter/total_byte.rb +2 -1
- data/lib/tty/progressbar/meter.rb +2 -1
- data/lib/tty/progressbar/multi.rb +221 -0
- data/lib/tty/progressbar/version.rb +1 -1
- data/spec/unit/events_spec.rb +35 -0
- data/spec/unit/formatter/elapsed_spec.rb +0 -1
- data/spec/unit/head_spec.rb +34 -0
- data/spec/unit/inspect_spec.rb +1 -1
- data/spec/unit/iterate_spec.rb +48 -0
- data/spec/unit/multi/advance_spec.rb +139 -0
- data/spec/unit/multi/events_spec.rb +64 -0
- data/spec/unit/multi/finish_spec.rb +19 -0
- data/spec/unit/multi/line_inset_spec.rb +67 -0
- data/spec/unit/multi/register_spec.rb +36 -0
- data/spec/unit/multi/stop_spec.rb +17 -0
- data/spec/unit/new_spec.rb +6 -0
- data/spec/unit/reset_spec.rb +0 -2
- data/spec/unit/stop_spec.rb +21 -0
- data/spec/unit/update_spec.rb +24 -0
- data/tty-progressbar.gemspec +1 -0
- metadata +44 -2
@@ -14,6 +14,8 @@ module TTY
|
|
14
14
|
|
15
15
|
attr_accessor :complete
|
16
16
|
|
17
|
+
attr_accessor :head
|
18
|
+
|
17
19
|
attr_accessor :hide_cursor
|
18
20
|
|
19
21
|
attr_accessor :clear
|
@@ -30,10 +32,12 @@ module TTY
|
|
30
32
|
@no_width = options.fetch(:no_width) { false }
|
31
33
|
@incomplete = options.fetch(:incomplete) { ' ' }
|
32
34
|
@complete = options.fetch(:complete) { '=' }
|
35
|
+
@head = options.fetch(:head) { @complete || '=' }
|
33
36
|
@hide_cursor = options.fetch(:hide_cursor) { false }
|
34
37
|
@clear = options.fetch(:clear) { false }
|
35
38
|
@output = options.fetch(:output) { $stderr }
|
36
39
|
@frequency = options.fetch(:frequency) { 0 } # 0Hz
|
40
|
+
@interval = options.fetch(:interval) { 1 } # 1 sec
|
37
41
|
end
|
38
42
|
|
39
43
|
def total=(value)
|
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module TTY
|
4
5
|
class ProgressBar
|
@@ -35,6 +36,7 @@ module TTY
|
|
35
36
|
complete_length = (width * @progress.ratio).round
|
36
37
|
complete = Array.new(complete_length, @progress.complete)
|
37
38
|
incomplete = Array.new(width - complete_length, @progress.incomplete)
|
39
|
+
complete[-1] = @progress.head if complete_length > 0
|
38
40
|
|
39
41
|
bar = ''
|
40
42
|
bar += complete.join
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'monitor'
|
6
|
+
|
7
|
+
require_relative '../progressbar'
|
8
|
+
|
9
|
+
module TTY
|
10
|
+
class ProgressBar
|
11
|
+
# Used for managing multiple terminal progress bars
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class Multi
|
15
|
+
include Enumerable
|
16
|
+
include MonitorMixin
|
17
|
+
|
18
|
+
extend Forwardable
|
19
|
+
|
20
|
+
def_delegators :@bars, :each, :empty?, :length, :[]
|
21
|
+
|
22
|
+
DEFAULT_INSET = {
|
23
|
+
top: Gem.win_platform? ? '+ ' : "\u250c ",
|
24
|
+
middle: Gem.win_platform? ? '|-- ' : "\u251c\u2500\u2500 ",
|
25
|
+
bottom: Gem.win_platform? ? '|__ ' : "\u2514\u2500\u2500 "
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# Number of currently occupied rows in terminal display
|
29
|
+
attr_reader :rows
|
30
|
+
|
31
|
+
# Create a multibar
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# bars = TTY::ProgressBar::Multi.new
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# bars = TTY::ProgressBar::Multi.new("main [:bar]")
|
38
|
+
#
|
39
|
+
# @param [String] format
|
40
|
+
# the formatting string to display this bar
|
41
|
+
#
|
42
|
+
# @param [Hash] options
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def initialize(*args)
|
46
|
+
super()
|
47
|
+
@options = args.last.is_a?(::Hash) ? args.pop : {}
|
48
|
+
format = args.empty? ? nil : args.pop
|
49
|
+
@inset_opts = @options.delete(:style) { DEFAULT_INSET }
|
50
|
+
@bars = []
|
51
|
+
@rows = 0
|
52
|
+
@top_bar = nil
|
53
|
+
@top_bar = register(format) if format
|
54
|
+
|
55
|
+
@callbacks = {
|
56
|
+
progress: [],
|
57
|
+
stopped: [],
|
58
|
+
done: []
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Register a new progress bar
|
63
|
+
#
|
64
|
+
# @param [String] format
|
65
|
+
# the formatting string to display the bar
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def register(format, options = {})
|
69
|
+
bar = TTY::ProgressBar.new(format, @options.merge(options))
|
70
|
+
|
71
|
+
synchronize do
|
72
|
+
bar.attach_to(self)
|
73
|
+
@bars << bar
|
74
|
+
|
75
|
+
if @top_bar
|
76
|
+
@top_bar.update(total: total, width: total)
|
77
|
+
observe(bar)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
bar
|
82
|
+
end
|
83
|
+
|
84
|
+
# Increase row count
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def next_row
|
88
|
+
synchronize do
|
89
|
+
@rows += 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Observe a bar for emitted events
|
94
|
+
#
|
95
|
+
# @param [TTY::ProgressBar] bar
|
96
|
+
# the bar to observe for events
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
def observe(bar)
|
100
|
+
bar.on(:progress) { top_bar.current = current; emit(:progress) }
|
101
|
+
.on(:done) { top_bar.finish; emit(:done) if complete? }
|
102
|
+
.on(:stopped) { top_bar.stop; emit(:stopped) if stopped? }
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get the top level bar if it exists
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def top_bar
|
109
|
+
raise "No top level progress bar" unless @top_bar
|
110
|
+
|
111
|
+
@top_bar
|
112
|
+
end
|
113
|
+
|
114
|
+
def start
|
115
|
+
raise "No top level progress bar" unless @top_bar
|
116
|
+
|
117
|
+
@top_bar.start
|
118
|
+
end
|
119
|
+
|
120
|
+
# Calculate total maximum progress of all bars
|
121
|
+
#
|
122
|
+
# @return [Integer]
|
123
|
+
#
|
124
|
+
# @api public
|
125
|
+
def total
|
126
|
+
(@bars - [@top_bar]).dup.map(&:total).reduce(&:+)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Calculate total current progress of all bars
|
130
|
+
#
|
131
|
+
# @return [Integer]
|
132
|
+
#
|
133
|
+
# @api public
|
134
|
+
def current
|
135
|
+
(@bars - [@top_bar]).dup.map(&:current).reduce(&:+)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Check if all progress bars are complete
|
139
|
+
#
|
140
|
+
# @return [Boolean]
|
141
|
+
#
|
142
|
+
# @api public
|
143
|
+
def complete?
|
144
|
+
(@bars - [@top_bar]).dup.all?(&:complete?)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check if any of the registered progress bars is stopped
|
148
|
+
#
|
149
|
+
# @return [Boolean]
|
150
|
+
#
|
151
|
+
# @api public
|
152
|
+
def stopped?
|
153
|
+
(@bars - [@top_bar]).dup.any?(&:stopped?)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Stop all progress bars
|
157
|
+
#
|
158
|
+
# @api public
|
159
|
+
def stop
|
160
|
+
@bars.dup.each(&:stop)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Finish all progress bars
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
def finish
|
167
|
+
@top_bar.finish if @top_bar
|
168
|
+
@bars.dup.each(&:finish)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Find the number of characters to move into the line
|
172
|
+
# before printing the bar
|
173
|
+
#
|
174
|
+
# @param [TTY::ProgressBar] bar
|
175
|
+
# the progress bar for which line inset is calculated
|
176
|
+
#
|
177
|
+
# @return [String]
|
178
|
+
# the inset
|
179
|
+
#
|
180
|
+
# @api public
|
181
|
+
def line_inset(bar)
|
182
|
+
return '' if @top_bar.nil?
|
183
|
+
|
184
|
+
case bar.row
|
185
|
+
when @top_bar.row
|
186
|
+
@inset_opts[:top]
|
187
|
+
when rows
|
188
|
+
@inset_opts[:bottom]
|
189
|
+
else
|
190
|
+
@inset_opts[:middle]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Listen on event
|
195
|
+
#
|
196
|
+
# @param [Symbol] name
|
197
|
+
# the event name to listen on
|
198
|
+
#
|
199
|
+
# @api public
|
200
|
+
def on(name, &callback)
|
201
|
+
unless @callbacks.key?(name)
|
202
|
+
raise ArgumentError, "The event #{name} does not exist. "\
|
203
|
+
" Use :progress, :stopped, or :done instead"
|
204
|
+
end
|
205
|
+
@callbacks[name] << callback
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
# Fire an event by name
|
212
|
+
#
|
213
|
+
# @api private
|
214
|
+
def emit(name, *args)
|
215
|
+
@callbacks[name].each do |callback|
|
216
|
+
callback.(*args)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end # Multi
|
220
|
+
end # ProgressBar
|
221
|
+
end # TTY
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::ProgressBar, 'events' do
|
4
|
+
let(:output) { StringIO.new('', 'w+') }
|
5
|
+
|
6
|
+
it "emits :progress event when advancing" do
|
7
|
+
events = []
|
8
|
+
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
|
9
|
+
bar.on(:progress) { events << :progress }
|
10
|
+
|
11
|
+
bar.advance
|
12
|
+
|
13
|
+
expect(events).to eq([:progress])
|
14
|
+
end
|
15
|
+
|
16
|
+
it "emits :done event when finished" do
|
17
|
+
events = []
|
18
|
+
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
|
19
|
+
bar.on(:done) { events << :done }
|
20
|
+
|
21
|
+
bar.finish
|
22
|
+
|
23
|
+
expect(events).to eq([:done])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "emits :stopped event" do
|
27
|
+
events = []
|
28
|
+
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
|
29
|
+
bar.on(:stopped) { events << :stopped }
|
30
|
+
|
31
|
+
bar.stop
|
32
|
+
|
33
|
+
expect(events).to eq([:stopped])
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::ProgressBar, ':head' do
|
4
|
+
let(:output) { StringIO.new('', 'w+')}
|
5
|
+
|
6
|
+
it "animates head" do
|
7
|
+
progress = TTY::ProgressBar.new("[:bar]", output: output, head: '>', total: 5)
|
8
|
+
5.times { progress.advance }
|
9
|
+
output.rewind
|
10
|
+
expect(output.read).to eq([
|
11
|
+
"\e[1G[> ]",
|
12
|
+
"\e[1G[=> ]",
|
13
|
+
"\e[1G[==> ]",
|
14
|
+
"\e[1G[===> ]",
|
15
|
+
"\e[1G[====>]\n"
|
16
|
+
].join)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "customises all output characters" do
|
20
|
+
progress = TTY::ProgressBar.new("[:bar]",
|
21
|
+
output: output,
|
22
|
+
head: 'ᗧ',
|
23
|
+
complete: '-', incomplete: '.', total: 5)
|
24
|
+
5.times { progress.advance }
|
25
|
+
output.rewind
|
26
|
+
expect(output.read).to eq([
|
27
|
+
"\e[1G[ᗧ....]",
|
28
|
+
"\e[1G[-ᗧ...]",
|
29
|
+
"\e[1G[--ᗧ..]",
|
30
|
+
"\e[1G[---ᗧ.]",
|
31
|
+
"\e[1G[----ᗧ]\n"
|
32
|
+
].join)
|
33
|
+
end
|
34
|
+
end
|