103
103
if not self._task_stack:
104
104
# finished top-level task
105
105
self._progress_view.clear()
108
class TextProgressView(object):
109
"""Display of progress bar and other information on a tty.
111
This shows one line of text, including possibly a network indicator, spinner,
112
progress bar, message, etc.
114
One instance of this is created and held by the UI, and fed updates when a
115
task wants to be painted.
117
Transports feed data to this through the ui_factory object.
120
def __init__(self, term_file):
121
self._term_file = term_file
122
# true when there's output on the screen we may need to clear
123
self._have_output = False
124
# XXX: We could listen for SIGWINCH and update the terminal width...
125
self._width = osutils.terminal_width()
126
self._last_transport_msg = ''
128
# time we last repainted the screen
129
self._last_repaint = 0
130
# time we last got information about transport activity
131
self._transport_update_time = 0
132
self._task_fraction = None
133
self._last_task = None
134
self._total_byte_count = 0
135
self._bytes_since_update = 0
137
def _show_line(self, s):
139
self._term_file.write('\r%-*.*s\r' % (n, n, s))
142
if self._have_output:
144
self._have_output = False
146
def _render_bar(self):
147
# return a string for the progress bar itself
148
if (self._last_task is not None) and self._last_task.show_bar:
149
spin_str = r'/-\|'[self._spin_pos % 4]
151
f = self._task_fraction or 0
153
# number of markers highlighted in bar
154
markers = int(round(float(cols) * f)) - 1
155
bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
157
elif (self._last_task is None) or self._last_task.show_spinner:
158
spin_str = r'/-\|'[self._spin_pos % 4]
160
return spin_str + ' '
164
def _format_task(self, task):
165
if not task.show_count:
167
elif task.total_cnt is not None:
168
s = ' %d/%d' % (task.current_cnt, task.total_cnt)
169
elif task.current_cnt is not None:
170
s = ' %d' % (task.current_cnt)
173
self._task_fraction = task._overall_completion_fraction()
174
# compose all the parent messages
177
while t._parent_task:
184
bar_string = self._render_bar()
186
task_msg = self._format_task(self._last_task)
189
trans = self._last_transport_msg
190
if trans and task_msg:
197
self._have_output = True
199
def show_progress(self, task):
200
self._last_task = task
202
if now < self._last_repaint + 0.1:
204
if now > self._transport_update_time + 5:
205
# no recent activity; expire it
206
self._last_transport_msg = ''
207
self._last_repaint = now
210
def show_transport_activity(self, byte_count):
211
"""Called by transports as they do IO.
213
This may update a progress bar, spinner, or similar display.
214
By default it does nothing.
216
# XXX: Probably there should be a transport activity model, and that
217
# too should be seen by the progress view, rather than being poked in
219
self._total_byte_count += byte_count
220
self._bytes_since_update += byte_count
222
if self._transport_update_time is None:
223
self._transport_update_time = now
224
elif now >= (self._transport_update_time + 0.2):
225
# guard against clock stepping backwards, and don't update too
227
rate = self._bytes_since_update / (now - self._transport_update_time)
228
msg = ("%6dkB @ %4dkB/s" %
229
(self._total_byte_count>>10, int(rate)>>10,))
230
self._transport_update_time = now
231
self._last_repaint = now
232
self._bytes_since_update = 0
233
self._last_transport_msg = msg