progress-meter 1.0.0 → 1.0.0.1
raw patch · 3 files changed
+162/−26 lines, 3 files
Files
- CHANGELOG.md +11/−0
- README.md +150/−7
- progress-meter.cabal +1/−19
CHANGELOG.md view
@@ -1,3 +1,14 @@+# 1.0.0.1++ * Fixed non-Haddock documentation++# 1.0.0 (complete rewrite)++ * Highly simplified implementation++ * Replaced separately managed individual text chunks by the ability to+ choose a state type and zoom into it using a lens-inspired approach+ # 0.1.0 * Initial version.
README.md view
@@ -1,10 +1,153 @@-progress-meter+Progress-Meter ============== -This library can be used to display progress meters or other diagnostics-for concurrently running actions. It supports dynamic creation and-removal of new sub-meters as well as correct behaviour when printing-diagnostics that are not part of the progress meter and should just-scroll by.+This library can be used to display a progress bar or other live+diagnostics for your application. It supports partial updates from+multiple threads without interfering with each other, and it has the+correct behaviour when printing diagnostics that are not part of the+progress bar and should just scroll by. -The `System.ProgressMeter` module contains a tutorial.+The @System.Progress@ module contains a tutorial.+++Quickstart+----------++Use the `withProgress_` function to create a progress bar for the+duration of an action:++``` haskell+withProgress_+ :: s -- ^ Initial state value+ -> (s -> String) -- ^ State renderer+ -> (Meter' s -> IO r) -- ^ Action with a progress bar+ -> IO r+```++You need to choose a state type and a renderer for the state that is+used to display it in the terminal. Then you can use `setMeter` or+`modifyMeter` to update the state value, which triggers a redraw of the+progress bar:++``` haskell+setMeter :: Meter' s -> s -> IO ()+modifyMeter :: Meter' s -> (s -> s) -> IO ()+```++To perform regular output (logging or other diagnostics that should+scroll by) use the `putMsgLn` function:++``` haskell+putMsgLn :: Meter' s -> String -> IO ()+```++You can use the `zoomMeter` function to give individual threads a meter+that only changes part of the state:++``` haskell+zoomMeter :: ((a -> a) -> s -> s) -> Meter' s -> Meter' a+```+++Simple example+--------------++Taken from the tutorial the following uses a progress bar with state of+type `Int` and renders it to a simple percentage display:++``` haskell+import Control.Concurrent+import Data.Foldable+import System.Progress++main :: IO ()+main =+ withProgress_ 0 render $ \pm -> do+ for_ [1..99] $ \p -> do+ threadDelay 20000+ setMeter pm p+ threadDelay 3000000+ setMeter pm 100+ threadDelay 1000000++ where+ render :: Int -> String+ render x = "Progress: " ++ show x ++ "%"+```+++Complex example+---------------++The following is an example of how you can use `zoomMeter` to display+multiple progress indicators for concurrent threads and update each of+them independently. The program takes a bunch of command line arguments+and launches a separate worker thread for each. The overall progress+state is represented by a value of type `(Map FilePath Int)` that keeps+track of the percentage of each individual worker:++``` haskell+import Control.Concurrent+import Control.Concurrent.Async+import Control.Exception+import Control.Monad+import Data.Foldable+import Data.List (intercalate)+import qualified Data.Map.Strict as M+import System.Environment+import System.Progress+import System.Random+++-- Worker threads take a meter that points to an individual percentage.+-- The percentage is 'Maybe'-wrapped, because 'Nothing' represents+-- absence of the percentage.++thread :: Meter' (Maybe Int) -> FilePath -> IO ()+thread pm fp =+ -- Add the percentage for this file path at the beginning and make+ -- sure it disappears at the end+ bracket_ (setMeter pm (Just 0)) (setMeter pm Nothing) $ do++ -- Simulate real work by choosing a random delay for each thread+ delay <- randomRIO (100000, 200000)++ putMsgLn pm ("Started work on " ++ fp)++ -- Do the actual "work"+ for_ [1..99] $ \p -> do+ when (p == 50)+ (putMsgLn pm (fp ++ " is half-way done"))+ setMeter pm (Just p)+ threadDelay delay++ putMsgLn pm ("Done with " ++ fp)+++-- This function turns a meter that points to a 'Map k a' into a meter+-- that points to a specific key. 'Nothing' represents lack of that+-- particular key in the map.++zoomKey :: (Ord k) => k -> Meter' (M.Map k a) -> Meter' (Maybe a)+zoomKey k = zoomMeter (\f -> M.alter f k)+++main :: IO ()+main =+ -- The initial progress state is the empty map+ withProgress_ M.empty render $ \pm ->+ getArgs >>=++ -- The 'zoomMeter' function is used here to construct a meter+ -- that points to an individual key of the map:+ mapConcurrently_ (\fp -> thread (zoomKey fp pm) fp)++ where+ -- The renderer displays something like this:+ -- a: 21% | b: 50% | c: 8%+ render :: M.Map FilePath Int -> String+ render =+ intercalate " | " .+ map (\(fp, p) -> fp ++ ": " ++ show p ++ "%") .+ M.assocs+```
progress-meter.cabal view
@@ -1,5 +1,5 @@ name: progress-meter-version: 1.0.0+version: 1.0.0.1 category: System synopsis: Live diagnostics for concurrent activity @@ -32,12 +32,7 @@ description: Development dependencies manual: True -flag Examples- default: False- description: Build the example programs- manual: True - library build-depends: ansi-terminal >= 0.6 && < 0.8,@@ -51,16 +46,3 @@ hs-source-dirs: src exposed-modules: System.Progress---- executable pm-http-get--- if flag(examples)--- build-depends:--- base >= 4.8 && < 4.10,--- http-conduit,--- progress-meter--- else--- buildable: False--- default-language: Haskell2010--- ghc-options: -W -threaded--- hs-source-dirs: examples--- main-is: pm-http-get.hs