我在Python中看到了针对进度条的不同解决方案,但简单的stdout解决方案对我的项目不起作用。我有多个类并使用“logging”模块将信息输出到STDOUT。我有一个功能,我想在一行显示进度条,每次刷新缓冲区。
简单进展的例子:
for i in range(100):
time.sleep(1)
sys.stdout.write("\r%d%%" %i)
sys.stdout.flush()
当我尝试通过STDOUT写入然后刷新缓冲区时,缓冲区未刷新或进度无法进行。我希望避免某种线程或复杂的过程来实现这一点。有人有一种优先的方法来实现这一目标吗?
答案 0 :(得分:10)
我无法为此找到一个好的解决方案,所以我写了enlighten progress bar来处理它。基本上它会更改终端的滚动区域,因此日志记录在进度条上方完成,而不是每次要写入STDOUT时都必须重绘进度条。这使您可以根据需要随时写入终端,而无需修改logging,print等。
import logging
import time
import enlighten
# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()
# Setup progress bar
manager = enlighten.get_manager()
pbar = manager.counter(total=100, desc='Ticks', unit='ticks')
for i in range(1, 101):
logger.info("Processing step %s" % i)
time.sleep(.2)
pbar.update()
答案 1 :(得分:6)
import logging
import time
from tqdm import tqdm
import io
class TqdmToLogger(io.StringIO):
"""
Output stream for TQDM which will output to logger module instead of
the StdOut.
"""
logger = None
level = None
buf = ''
def __init__(self,logger,level=None):
super(TqdmToLogger, self).__init__()
self.logger = logger
self.level = level or logging.INFO
def write(self,buf):
self.buf = buf.strip('\r\n\t ')
def flush(self):
self.logger.log(self.level, self.buf)
if __name__ == "__main__":
logging.basicConfig(format='%(asctime)s [%(levelname)-8s] %(message)s')
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
tqdm_out = TqdmToLogger(logger,level=logging.INFO)
for x in tqdm(range(100),file=tqdm_out,mininterval=30,):
time.sleep(.5)
输出
2016-12-19 15:35:06 [INFO ] 16%|#####9 | 768/4928 [07:04<40:50, 1.70it/s]
2016-12-19 15:36:07 [INFO ] 18%|######6 | 865/4928 [08:04<40:34, 1.67it/s]
答案 2 :(得分:4)
您可以将tqdm progress bar与a custom handler through logging as described here:
一起使用import logging
import time
import colorlog
from tqdm import tqdm
class TqdmHandler(logging.StreamHandler):
def __init__(self):
logging.StreamHandler.__init__(self)
def emit(self, record):
msg = self.format(record)
tqdm.write(msg)
if __name__ == "__main__":
for x in tqdm(range(100)):
logger = colorlog.getLogger("MYAPP")
logger.setLevel(logging.DEBUG)
handler = TqdmHandler()
handler.setFormatter(colorlog.ColoredFormatter(
'%(log_color)s%(name)s | %(asctime)s | %(levelname)s | %(message)s',
datefmt='%Y-%d-%d %H:%M:%S',
log_colors={
'DEBUG': 'cyan',
'INFO': 'white',
'SUCCESS:': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'},))
logger.addHandler(handler)
logger.debug("Inside subtask: "+str(x))
time.sleep(.5)
答案 3 :(得分:0)
如果知道,进度条总是要写入STDOUT,您应该只使用print
而不是记录器。请参阅文档in the Python logging tutorial documentation
答案 4 :(得分:0)
清理这些提案的代码,这是 IMO 的正确实现,除了 tqdm
(也发布 here)外没有任何外部依赖:
import logging
from tqdm import tqdm
class TqdmLoggingHandler(logging.StreamHandler):
"""Avoid tqdm progress bar interruption by logger's output to console"""
# see logging.StreamHandler.eval method:
# https://github.com/python/cpython/blob/d2e2534751fd675c4d5d3adc208bf4fc984da7bf/Lib/logging/__init__.py#L1082-L1091
# and tqdm.write method:
# https://github.com/tqdm/tqdm/blob/f86104a1f30c38e6f80bfd8fb16d5fcde1e7749f/tqdm/std.py#L614-L620
def emit(self, record):
try:
msg = self.format(record)
tqdm.write(msg, end=self.terminator)
except RecursionError:
raise
except Exception:
self.handleError(record)
测试:
import time
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
log.addHandler(TqdmLoggingHandler())
# ^-- Assumes this will be the unique handler emitting messages to sys.stdout.
# If other handlers output to sys.stdout (without tqdm.write),
# progress bar will be interrupted by those outputs
for i in tqdm(range(20)):
log.info(f"Looping {i}")
time.sleep(0.1)