From 05884c9108f8a55d6bfbfa0c4a6bc550ccd1b583 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Fri, 5 Apr 2019 15:01:42 +0800 Subject: [PATCH 1/2] add flush and reset --- src/multi.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/multi.rs b/src/multi.rs index 8dc33e19..0e54e9ba 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -218,6 +218,96 @@ impl MultiBar { printfl!(self.handle, "{}", out); } } + + /// flush all bars. + /// + /// see also `listen()` + /// + /// This is a non-blocking operation. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// + /// // ... + /// // create some bars here + /// // ... + /// + /// mb.flush(false); + /// + /// // ... + /// // change bars here + /// // ... + /// + /// mb.flush(true); + /// + /// // ... + /// ``` + pub fn flush(&mut self, move_up: bool) { + let mut nbars = self.nbars; + if nbars > 0 { + + // receive message + loop { + let msg = if let Ok(msg) = self.chan.1.try_recv() { + msg + } else { + break; + }; + if msg.done { + nbars -= 1; + continue; + } + self.lines[msg.level] = msg.string; + break; + } + + // and draw + let mut out = String::new(); + if move_up { + out += &move_cursor_up(self.nlines); + } + for l in self.lines.iter() { + out.push_str(&format!("\r{}\n", l)); + } + printfl!(self.handle, "{}", out); + } + } + + /// reset output cursor. + /// + /// see also `flush()` + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use pbr::MultiBar; + /// + /// let mut mb = MultiBar::new(); + /// + /// // ... + /// // create some bars here + /// // ... + /// + /// mb.flush(false); + /// + /// // ... + /// // change bars here + /// // ... + /// + /// mb.reset(); + /// mb.flush(false); + /// + /// // ... + /// ``` + pub fn reset(&mut self) { + printfl!(self.handle, "{}", move_cursor_up(self.nlines)); + } } pub struct Pipe { From 325e9683c38ff56125857508815027be20ed3664 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Fri, 5 Apr 2019 18:45:20 +0800 Subject: [PATCH 2/2] add FinishMethod --- src/lib.rs | 2 +- src/multi.rs | 138 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 85 insertions(+), 55 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 236f7035..f2556fe8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,7 +117,7 @@ mod tty; mod pb; mod multi; pub use pb::{ProgressBar, Units}; -pub use multi::{MultiBar, Pipe}; +pub use multi::{MultiBar, Pipe, FinishMethod}; use std::io::{Write, Stdout, stdout}; pub struct PbIter diff --git a/src/multi.rs b/src/multi.rs index 0e54e9ba..536a1dff 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -4,17 +4,28 @@ use tty::move_cursor_up; use std::io::{Stdout, Result, Write}; use std::sync::mpsc; use std::sync::mpsc::{Sender, Receiver}; +use std::collections::HashMap; + +pub enum FinishMethod { + Graduate, + Remove, + Keep, +} pub struct MultiBar { nlines: usize, lines: Vec, - nbars: usize, + bar_id: u64, + + bars: HashMap, chan: (Sender, Receiver), handle: T, + + finish: FinishMethod, } impl MultiBar { @@ -80,13 +91,36 @@ impl MultiBar { pub fn on(handle: T) -> MultiBar { MultiBar { nlines: 0, - nbars: 0, + bar_id: 0, lines: Vec::new(), + bars: HashMap::new(), chan: mpsc::channel(), handle: handle, + finish: FinishMethod::Keep, } } + /// Set finish method + /// FinishMethod::Keep => keep the bar when finishing + /// FinishMethod::Remove => remove the bar when finishing + /// FinishMethod::Graduate => graduate the bar when finishing + /// + /// # Examples + /// + /// ```no_run + /// use pbr::{MultiBar, FinishMethod}; + /// use std::io::stderr; + /// + /// let mut mb = MultiBar::on(stderr()); + /// mb.set_finish(FinishMethod::Remove); + /// // ... + /// // see full example in `MultiBar::new` + /// // ... + /// ``` + pub fn set_finish(&mut self, finish: FinishMethod) { + self.finish = finish + } + /// println used to add text lines between the bars. /// for example: you could add a header to your application, /// or text separators between bars. @@ -151,18 +185,40 @@ impl MultiBar { /// mb.listen(); /// ``` pub fn create_bar(&mut self, total: u64) -> ProgressBar { - self.println(""); - self.nbars += 1; + let bar_id = self.bar_id; + self.bars.insert(bar_id, self.nlines); let mut p = ProgressBar::on(Pipe { - level: self.nlines - 1, + bar_id: bar_id, chan: self.chan.0.clone(), }, total); + self.println(&format!("bar_{}", bar_id)); + self.bar_id += 1; p.is_multibar = true; p.add(0); p } + fn bar_finish(&mut self, bar_id: u64) -> String { + let result = match self.finish { + FinishMethod::Keep => String::new(), + FinishMethod::Remove | FinishMethod::Graduate => { + for (&i, v) in self.bars.iter_mut() { + if i > bar_id { + *v -= 1; + } + } + self.nlines -= 1; + self.lines.remove(self.bars[&bar_id]) + } + }; + self.bars.remove(&bar_id); + if let FinishMethod::Graduate = self.finish { + format!("\r{}\n", result) + } else { + String::new() + } + } /// listen start listen to all bars changes. /// @@ -194,28 +250,32 @@ impl MultiBar { /// ``` pub fn listen(&mut self) { let mut first = true; - let mut nbars = self.nbars; - while nbars > 0 { + let mut nlines = self.nlines; + let mut graduate = String::new(); + while self.bars.len() > 0 { // receive message let msg = self.chan.1.recv().unwrap(); if msg.done { - nbars -= 1; + graduate.push_str(&self.bar_finish(msg.bar_id)); continue; } - self.lines[msg.level] = msg.string; + self.lines[self.bars[&msg.bar_id]] = msg.string; // and draw let mut out = String::new(); if !first { - out += &move_cursor_up(self.nlines); + out += &move_cursor_up(nlines); } else { first = false; } + out.push_str(&graduate); + graduate = String::new(); for l in self.lines.iter() { out.push_str(&format!("\r{}\n", l)); } printfl!(self.handle, "{}", out); + nlines = self.nlines; } } @@ -237,19 +297,19 @@ impl MultiBar { /// // create some bars here /// // ... /// - /// mb.flush(false); + /// let nlines = mb.flush(None); /// /// // ... /// // change bars here /// // ... /// - /// mb.flush(true); + /// mb.flush(Some(nlines)); /// /// // ... /// ``` - pub fn flush(&mut self, move_up: bool) { - let mut nbars = self.nbars; - if nbars > 0 { + pub fn flush(&mut self, nlines: Option) -> usize { + if self.bars.len() > 0 { + let mut graduate = String::new(); // receive message loop { @@ -259,59 +319,29 @@ impl MultiBar { break; }; if msg.done { - nbars -= 1; + graduate.push_str(&self.bar_finish(msg.bar_id)); continue; } - self.lines[msg.level] = msg.string; - break; + self.lines[self.bars[&msg.bar_id]] = msg.string; } // and draw let mut out = String::new(); - if move_up { - out += &move_cursor_up(self.nlines); + if nlines.is_some() { + out += &move_cursor_up(nlines.unwrap()); } + out.push_str(&graduate); for l in self.lines.iter() { out.push_str(&format!("\r{}\n", l)); } printfl!(self.handle, "{}", out); } - } - - /// reset output cursor. - /// - /// see also `flush()` - /// - /// # Examples - /// - /// ```no_run - /// use std::thread; - /// use pbr::MultiBar; - /// - /// let mut mb = MultiBar::new(); - /// - /// // ... - /// // create some bars here - /// // ... - /// - /// mb.flush(false); - /// - /// // ... - /// // change bars here - /// // ... - /// - /// mb.reset(); - /// mb.flush(false); - /// - /// // ... - /// ``` - pub fn reset(&mut self) { - printfl!(self.handle, "{}", move_cursor_up(self.nlines)); + self.nlines } } pub struct Pipe { - level: usize, + bar_id: u64, chan: Sender, } @@ -322,7 +352,7 @@ impl Write for Pipe { .send(WriteMsg { // finish method emit empty string done: s == "", - level: self.level, + bar_id: self.bar_id, string: s, }) .unwrap(); @@ -338,6 +368,6 @@ impl Write for Pipe { // between MultiBar and its bars struct WriteMsg { done: bool, - level: usize, + bar_id: u64, string: String, }