Source code for dials.util.command_line
from __future__ import annotations
import sys
import time
from dials.util import debug_console
interactive_console = debug_console
[docs]
class ProgressBarTimer:
"""A simple timer for the progress bar."""
[docs]
def __init__(self):
"""Init the progress bar timer."""
self._last_time = time.time()
self._last_perc = 0
self._update_period = 0.5
self._n_seconds_left = -1
[docs]
def get_elapsed_time(self):
return time.time() - self._last_time
[docs]
def update(self, percent):
"""Update the timer."""
# Get the current time diff between last time
curr_time = time.time()
diff_time = curr_time - self._last_time
# Only update after certain period or at 100%
if percent < 0:
percent = 0
if percent > 100:
percent = 100
if diff_time >= self._update_period or percent >= 100:
# Check the difference in percentage and calculate
# number of seconds remaining
diff_perc = percent - self._last_perc
if diff_perc == 0:
self._n_seconds_left = 0
else:
self._n_seconds_left = diff_time * (100 - percent) / diff_perc
# Return number of seconds
return self._n_seconds_left
[docs]
class ProgressBar:
"""A command line progress bar."""
[docs]
def __init__(
self,
title=None,
spinner=True,
bar=True,
estimate_time=True,
indent=0,
length=80,
):
"""Init the progress bar parameters."""
# Set the parameters
self._title = title
self._indent = indent
self._spinner = spinner
self._estimate_time = estimate_time
self._bar = bar
self._length = length
self._timer = ProgressBarTimer()
# Print 0 percent
self.update(0)
[docs]
def update(self, fpercent):
"""Update the progress bar with a percentage."""
from math import ceil
# do not update if not a tty
if not sys.stdout.isatty():
return
# Get integer percentage
percent = int(fpercent)
if percent < 0:
percent = 0
if percent > 100:
percent = 100
# Add a percentage counter
right_str = ""
left_str = ""
if sys.stdout.isatty():
left_str = "\r"
left_str += " " * self._indent
# Add a title if given
if self._title:
left_str += self._title + ": "
left_str += f"{percent: >3}%"
# Add a spinner
if self._spinner:
left_str += " "
left_str += "[ {} ]".format(r"-\|/"[percent % 4])
# Add a timer
if self._estimate_time:
n_seconds_left = self._timer.update(fpercent)
if n_seconds_left < 0:
n_seconds_left = "?"
else:
n_seconds_left = int(ceil(n_seconds_left))
right_str = " " + f"est: {n_seconds_left}s" + right_str
# Add a bar
if self._bar:
bar_length = self._length - (len(left_str) + len(right_str)) - 5
n_char = int(percent * bar_length / 100)
n_space = bar_length - n_char
left_str += " "
left_str += f"[ {'=' * n_char}>{' ' * n_space} ]"
# Append strings
progress_str = left_str + right_str
# Print progress string to stdout
sys.stdout.write(progress_str)
sys.stdout.flush()
[docs]
def finished(self, string=None):
"""The progress bar is finished."""
if string:
self._title = string
else:
string = ""
""" Print the 'end of comand' string."""
if self._estimate_time:
# Get the time string
time_string = f"{self._timer.get_elapsed_time():.2f}s"
# Truncate the string
max_length = self._length - self._indent - len(time_string) - 1
string = string[:max_length]
# Add an indent and a load of dots and then the time string
dot_length = 1 + max_length - len(string)
string = (" " * self._indent) + string
string = string + "." * (dot_length)
string = string + time_string
else:
# Truncate the string
max_length = self._length - self._indent
string = string[:max_length]
# Add a load of dots
dot_length = max_length - len(string)
string = (" " * self._indent) + string
string = string + "." * (dot_length)
# Write the string to stdout
if sys.stdout.isatty():
string = "\r" + string + "\n"
else:
string = string + "\n"
sys.stdout.write(string)
sys.stdout.flush()
[docs]
class Command:
"""Class to nicely print out a command with timing info."""
# Variables available in class methods
indent = 0
max_length = 80
print_time = True
[docs]
@classmethod
def start(cls, string):
"""Print the 'start command' string."""
# from termcolor import colored
# Get the command start time
cls._start_time = time.time()
# do not output if not a tty
if not sys.stdout.isatty():
return
# Truncate the string to the maximum length
max_length = cls.max_length - cls.indent - 3
string = string[:max_length]
string = (" " * cls.indent) + string + "..."
# Write the string to stdout
sys.stdout.write(string)
sys.stdout.flush()
[docs]
@classmethod
def end(cls, string):
"""Print the 'end of command' string."""
# from termcolor import colored
# Check if we want to print the time or not
if cls.print_time:
# Get the time string
time_string = f"{time.time() - cls._start_time:.2f}s"
# Truncate the string
max_length = cls.max_length - cls.indent - len(time_string) - 1
string = string[:max_length]
# Add an indent and a load of dots and then the time string
dot_length = 1 + max_length - len(string)
string = (" " * cls.indent) + string
string = string + "." * (dot_length)
string = string + time_string
else:
# Truncate the string
max_length = cls.max_length - cls.indent
string = string[:max_length]
# Add a load of dots
dot_length = max_length - len(string)
string = (" " * cls.indent) + string
string = string + "." * (dot_length)
# Write the string to stdout
if sys.stdout.isatty():
string = "\r" + string + "\n"
else:
string = string + "\n"
sys.stdout.write(string)
sys.stdout.flush()
try:
import termcolor
except ImportError:
termcolor = None
[docs]
def coloured(text, *args, **kwargs):
if not sys.stdout.isatty() or termcolor is None:
return text
return termcolor.colored(text, *args, **kwargs)
[docs]
def heading(text):
return coloured(text, attrs=["bold"])
if __name__ == "__main__":
p = ProgressBar()
for j in range(100):
p.update(j)
time.sleep(0.05)
p.finished()
Command.start("Starting to do a command")
time.sleep(1)
Command.end("Ending the command")