Repository: tqdm/tqdm (Python)

Issue:
Specify `tqdm_notebook` bar width
Thanks to the maintainers for this incredible tool!

I'm wondering how to specify the width of a progress bar in a Jupyter notebook, since the default is pretty small. The `tqdm_notebook` function doesn't seem to respect the `ncols` or `dynamic_ncols` keyword arguments of `tqdm`. 

Is there a way to do this? If not, then I guess this is a feature request 😸 

'tqdm' object has no attribute 'total' when using 'disable' and multiprocessing 'Pool'
The following MWE shows how to get the error message described in the title:

```python
from tqdm import tqdm
from multiprocessing import Pool

def f(arg):
    pass

if __name__ == '__main__':
    pool = Pool()
    list(tqdm(pool.imap(f, range(100)), disable=True))
```

- tqdm Version: 4.22.0
- python 3.6.0
- Win64

It works, when the `disable=True` is removed or set to `False`. But it should also work for the disabled case I would guess.
Add ncols to specify tqdm_notebook bar width
Fixes #276 but does not work yet as layout seems to be broken in current ipywidgets (see https://github.com/ipython/ipywidgets/issues/773).

I tried using either `widget.layout.width = 100%` and `Layout(width='100%')`, and in either case `ipywidgets` raises an error saying that both are unknown property/can't import Layout class...



Hints:
Unluckily, no it doesn't seem to be possible with ipywidgets, as there is no argument to specify the width of the progress bar. I guess the progress bar gets resized depending on cell size, thus on screen size, but I'm not sure about that.

If you want this feature you [may try to ask them directly](https://github.com/ipython/ipywidgets/issues/) ;)

My bad, it seems there's a way to specify the layout using CSS-like arguments (see [here layout.width](http://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Styling.html) and [here for autoresize on screen size](http://stackoverflow.com/questions/35880006/automatically-adjust-ipython-widgets-layout)). But it seems [it's not working correctly with the latest ipywidgets](https://github.com/ipython/ipywidgets/issues/773), so I'll try but I won't promise it will work :)

Awesome, thanks for looking into it!

Please try #292 on your setup, but on mine it doesn't work yet...

It works, but there is a bug on style. I will fix it.
Once #292 is merged, this issue will be resolved.
confirmed on linux py35


Patch:
diff --git a/tqdm/_tqdm.py b/tqdm/_tqdm.py
--- a/tqdm/_tqdm.py
+++ b/tqdm/_tqdm.py
@@ -853,7 +853,7 @@ def __len__(self):
         return self.total if self.iterable is None else \
             (self.iterable.shape[0] if hasattr(self.iterable, "shape")
              else len(self.iterable) if hasattr(self.iterable, "__len__")
-             else self.total)
+             else getattr(self, "total", None))
 
     def __enter__(self):
         return self
diff --git a/tqdm/_tqdm_notebook.py b/tqdm/_tqdm_notebook.py
--- a/tqdm/_tqdm_notebook.py
+++ b/tqdm/_tqdm_notebook.py
@@ -80,7 +80,7 @@ class tqdm_notebook(tqdm):
     """
 
     @staticmethod
-    def status_printer(_, total=None, desc=None):
+    def status_printer(_, total=None, desc=None, ncols=None):
         """
         Manage the printing of an IPython/Jupyter Notebook progress bar widget.
         """
@@ -104,6 +104,17 @@ def status_printer(_, total=None, desc=None):
         ptext = HTML()
         # Only way to place text to the right of the bar is to use a container
         container = HBox(children=[pbar, ptext])
+        # Prepare layout
+        if ncols is not None:  # use default style of ipywidgets
+            # ncols could be 100, "100px", "100%"
+            ncols = str(ncols)  # ipywidgets only accepts string
+            if ncols[-1].isnumeric():
+                # if last value is digit, assume the value is digit
+                ncols += 'px'
+            pbar.layout.flex = '2'
+            container.layout.width = ncols
+            container.layout.display = 'inline-flex'
+            container.layout.flex_flow = 'row wrap'
         display(container)
 
         def print_status(s='', close=False, bar_style=None, desc=None):
@@ -172,8 +183,13 @@ def __init__(self, *args, **kwargs):
         # Delete first pbar generated from super() (wrong total and text)
         # DEPRECATED by using gui=True
         # self.sp('', close=True)
+
+        # Get bar width
+        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)
+
         # Replace with IPython progress bar display (with correct total)
-        self.sp = self.status_printer(self.fp, self.total, self.desc)
+        self.sp = self.status_printer(
+            self.fp, self.total, self.desc, self.ncols)
         self.desc = None  # trick to place description before the bar
 
         # Print initial bar state
@@ -186,7 +202,7 @@ def __iter__(self, *args, **kwargs):
                 # return super(tqdm...) will not catch exception
                 yield obj
         # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt
-        except:
+        except:  # NOQA
             self.sp(bar_style='danger')
             raise
 


Test patch:
diff --git a/tqdm/tests/tests_synchronisation.py b/tqdm/tests/tests_synchronisation.py
--- a/tqdm/tests/tests_synchronisation.py
+++ b/tqdm/tests/tests_synchronisation.py
@@ -1,11 +1,11 @@
 from __future__ import division
-from tqdm import tqdm
-from tests_tqdm import with_setup, pretest, posttest, StringIO, closing
+from tqdm import tqdm, TMonitor
+from tests_tqdm import with_setup, pretest, posttest, SkipTest, \
+    StringIO, closing
 from tests_tqdm import DiscreteTimer, cpu_timify
 
 from time import sleep
 from threading import Event
-from tqdm import TMonitor
 
 
 class FakeSleep(object):
@@ -37,6 +37,10 @@ def create_fake_sleep_event():
     return create_fake_sleep_event
 
 
+def incr(x):
+    return x + 1
+
+
 @with_setup(pretest, posttest)
 def test_monitor_thread():
     """Test dummy monitoring thread"""
@@ -162,3 +166,16 @@ def test_monitoring_multi():
     # Check that class var monitor is deleted if no instance left
     tqdm.monitor_interval = 10
     assert tqdm.monitor is None
+
+
+@with_setup(pretest, posttest)
+def test_imap():
+    """Test multiprocessing.Pool"""
+    try:
+        from multiprocessing import Pool
+    except ImportError:
+        raise SkipTest
+
+    pool = Pool()
+    res = list(tqdm(pool.imap(incr, range(100)), disable=True))
+    assert res[-1] == 100