From 2de334d027f9198ed44f665e8148ca7e3c72c712 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Wed, 15 Jul 2020 17:26:06 -0400 Subject: [PATCH 01/24] Add tqdm_tk and ttk_range to tqdm.gui GUI interface with no graphs, but more performance and fewer dependencies --- tqdm/gui.py | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 2 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 2c90f413..16b83f1a 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -19,8 +19,8 @@ from .std import tqdm as std_tqdm # import compatibility functions and utilities from .utils import _range -__author__ = {"github.com/": ["casperdcl", "lrq3000"]} -__all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange'] +__author__ = {"github.com/": ["casperdcl", "lrq3000", "richardsheridan"]} +__all__ = ['tqdm_gui', 'tgrange', 'tqdm_tk', 'ttkrange', 'tqdm', 'trange'] class tqdm_gui(std_tqdm): # pragma: no cover @@ -188,6 +188,171 @@ def tgrange(*args, **kwargs): return tqdm_gui(_range(*args), **kwargs) +class tqdm_tk(tqdm_gui): + """ + Experimental Tkinter GUI version of tqdm! + """ + + # Monitor thread does not behave nicely with tkinter + monitor_interval = 0 + + def __init__( + self, + *args, + grab=False, + tk_parent=None, + bar_format=None, + **kwargs, + ): + import tkinter.ttk + + kwargs["gui"] = True + + # Tkinter specific default bar format + if bar_format is None: + kwargs["bar_format"] = ( + "{n_fmt}/{total_fmt}, {rate_noinv_fmt}\n" + "{elapsed} elapsed, {remaining} ETA\n\n" + "{percentage:3.0f}%" + ) + + # don't want to share __init__ with tqdm_gui + # preferably we would have a gui base class + super(tqdm_gui, self).__init__(*args, **kwargs) + + # Discover parent widget + if tk_parent is None: + # this will error if tkinter.NoDefaultRoot() called + try: + tkparent = tkinter._default_root + except AttributeError: + raise ValueError("tk_parent required when using NoDefaultRoot") + if tkparent is None: + # use new default root window as display + self.tk_window = tkinter.Tk() + else: + # some other windows already exist + self.tk_window = tkinter.Toplevel() + else: + self.tk_window = tkinter.Toplevel(tk_parent) + + if self.disable: + return + + warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 + ) + self.tk_dispatching = self.tk_dispatching_helper() + if not self.tk_dispatching: + # leave is problematic if the mainloop is not running + self.leave = False + + self.tk_window.protocol("WM_DELETE_WINDOW", self.close) + self.tk_window.wm_title("tqdm_tk") + self.tk_n_var = tkinter.DoubleVar(self.tk_window, value=0) + self.tk_desc_var = tkinter.StringVar(self.tk_window) + self.tk_desc_var.set(self.desc) + self.tk_text_var = tkinter.StringVar(self.tk_window) + pbar_frame = tkinter.ttk.Frame(self.tk_window, padding=5) + pbar_frame.pack() + self.tk_desc_label = tkinter.ttk.Label( + pbar_frame, + textvariable=self.tk_desc_var, + wraplength=600, + anchor="center", + justify="center", + ) + self.tk_desc_label.pack() + self.tk_label = tkinter.ttk.Label( + pbar_frame, + textvariable=self.tk_text_var, + wraplength=600, + anchor="center", + justify="center", + ) + self.tk_label.pack() + self.tk_pbar = tkinter.ttk.Progressbar( + pbar_frame, + variable=self.tk_n_var, + length=450, + ) + if self.total is not None: + self.tk_pbar.configure(maximum=self.total) + else: + self.tk_pbar.configure(mode="indeterminate") + self.tk_pbar.pack() + if grab: + self.tk_window.grab_set() + + def display(self): + self.tk_n_var.set(self.n) + self.tk_desc_var.set(self.desc) + self.tk_text_var.set( + self.format_meter( + n=self.n, + total=self.total, + elapsed=self._time() - self.start_t, + ncols=None, + prefix=self.desc, + ascii=self.ascii, + unit=self.unit, + unit_scale=self.unit_scale, + rate=1 / self.avg_time if self.avg_time else None, + bar_format=self.bar_format, + postfix=self.postfix, + unit_divisor=self.unit_divisor, + ) + ) + if not self.tk_dispatching: + self.tk_window.update() + + def reset(self, total=None): + if total is not None: + self.tk_pbar.configure(maximum=total) + super(tqdm_tk, self).reset(total) + + def close(self): + if self.disable: + return + + self.disable = True + + with self.get_lock(): + self._instances.remove(self) + + def _close(): + self.tk_window.after(0, self.tk_window.destroy) + if not self.tk_dispatching: + self.tk_window.update() + + self.tk_window.protocol("WM_DELETE_WINDOW", _close) + if not self.leave: + _close() + + def tk_dispatching_helper(self): + try: + return self.tk_window.dispatching() + except AttributeError: + pass + + import tkinter, sys + + codes = {tkinter.mainloop.__code__, tkinter.Misc.mainloop.__code__} + for frame in sys._current_frames().values(): + while frame: + if frame.f_code in codes: + return True + frame = frame.f_back + return False + + +def ttkrange(*args, **kwargs): + """ + A shortcut for `tqdm.gui.tqdm_tk(xrange(*args), **kwargs)`. + On Python3+, `range` is used instead of `xrange`. + """ + return tqdm_tk(_range(*args), **kwargs) + + # Aliases tqdm = tqdm_gui trange = tgrange From ee453f4f46f4ccd7ffa694f4c95ef9090ec463f1 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Wed, 15 Jul 2020 17:28:21 -0400 Subject: [PATCH 02/24] Optional cancel button Helpful when used in a larger tkinter app --- tqdm/gui.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 16b83f1a..ef3c4d0b 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -202,11 +202,13 @@ class tqdm_tk(tqdm_gui): grab=False, tk_parent=None, bar_format=None, + cancel_callback=None, **kwargs, ): import tkinter.ttk kwargs["gui"] = True + self._cancel_callback = cancel_callback # Tkinter specific default bar format if bar_format is None: @@ -246,7 +248,7 @@ class tqdm_tk(tqdm_gui): # leave is problematic if the mainloop is not running self.leave = False - self.tk_window.protocol("WM_DELETE_WINDOW", self.close) + self.tk_window.protocol("WM_DELETE_WINDOW", self.cancel) self.tk_window.wm_title("tqdm_tk") self.tk_n_var = tkinter.DoubleVar(self.tk_window, value=0) self.tk_desc_var = tkinter.StringVar(self.tk_window) @@ -280,6 +282,13 @@ class tqdm_tk(tqdm_gui): else: self.tk_pbar.configure(mode="indeterminate") self.tk_pbar.pack() + if self._cancel_callback is not None: + self.tk_button = tkinter.ttk.Button( + pbar_frame, + text="Cancel", + command=self.cancel, + ) + self.tk_button.pack() if grab: self.tk_window.grab_set() @@ -305,6 +314,11 @@ class tqdm_tk(tqdm_gui): if not self.tk_dispatching: self.tk_window.update() + def cancel(self): + if self._cancel_callback is not None: + self._cancel_callback() + self.close() + def reset(self, total=None): if total is not None: self.tk_pbar.configure(maximum=total) From 9a2bf6a19b72041cbf6d6a52ca79ca961ead8e55 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Wed, 15 Jul 2020 19:02:35 -0400 Subject: [PATCH 03/24] Code quality, py2 compat --- tqdm/gui.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index ef3c4d0b..e15a7791 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -205,7 +205,12 @@ class tqdm_tk(tqdm_gui): cancel_callback=None, **kwargs, ): - import tkinter.ttk + try: + import tkinter + import tkinter.ttk as ttk + except ImportError: + import Tkinter as tkinter + import Tkinter.ttk as ttk kwargs["gui"] = True self._cancel_callback = cancel_callback @@ -220,7 +225,7 @@ class tqdm_tk(tqdm_gui): # don't want to share __init__ with tqdm_gui # preferably we would have a gui base class - super(tqdm_gui, self).__init__(*args, **kwargs) + std_tqdm.__init__(self, *args, **kwargs) # Discover parent widget if tk_parent is None: @@ -254,9 +259,9 @@ class tqdm_tk(tqdm_gui): self.tk_desc_var = tkinter.StringVar(self.tk_window) self.tk_desc_var.set(self.desc) self.tk_text_var = tkinter.StringVar(self.tk_window) - pbar_frame = tkinter.ttk.Frame(self.tk_window, padding=5) + pbar_frame = ttk.Frame(self.tk_window, padding=5) pbar_frame.pack() - self.tk_desc_label = tkinter.ttk.Label( + self.tk_desc_label = ttk.Label( pbar_frame, textvariable=self.tk_desc_var, wraplength=600, @@ -264,7 +269,7 @@ class tqdm_tk(tqdm_gui): justify="center", ) self.tk_desc_label.pack() - self.tk_label = tkinter.ttk.Label( + self.tk_label = ttk.Label( pbar_frame, textvariable=self.tk_text_var, wraplength=600, @@ -272,7 +277,7 @@ class tqdm_tk(tqdm_gui): justify="center", ) self.tk_label.pack() - self.tk_pbar = tkinter.ttk.Progressbar( + self.tk_pbar = ttk.Progressbar( pbar_frame, variable=self.tk_n_var, length=450, @@ -283,7 +288,7 @@ class tqdm_tk(tqdm_gui): self.tk_pbar.configure(mode="indeterminate") self.tk_pbar.pack() if self._cancel_callback is not None: - self.tk_button = tkinter.ttk.Button( + self.tk_button = ttk.Button( pbar_frame, text="Cancel", command=self.cancel, @@ -315,6 +320,7 @@ class tqdm_tk(tqdm_gui): self.tk_window.update() def cancel(self): + """Call cancel_callback and close the progress bar""" if self._cancel_callback is not None: self._cancel_callback() self.close() @@ -343,14 +349,21 @@ class tqdm_tk(tqdm_gui): _close() def tk_dispatching_helper(self): + """determine if Tkinter mainloop is dispatching events""" try: + # Landing in CPython 3.10 return self.tk_window.dispatching() except AttributeError: pass - import tkinter, sys + try: + import tkinter + except ImportError: + import Tkinter as tkinter + import sys - codes = {tkinter.mainloop.__code__, tkinter.Misc.mainloop.__code__} + codes = set((tkinter.mainloop.__code__, + tkinter.Misc.mainloop.__code__)) for frame in sys._current_frames().values(): while frame: if frame.f_code in codes: From 3d9bad764ae7c0de54834234b0fc8d32aed319ea Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Thu, 16 Jul 2020 13:49:40 -0400 Subject: [PATCH 04/24] py27 kwargs compat, improve desc display --- tqdm/gui.py | 59 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index e15a7791..85c405ac 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -196,24 +196,31 @@ class tqdm_tk(tqdm_gui): # Monitor thread does not behave nicely with tkinter monitor_interval = 0 - def __init__( - self, - *args, - grab=False, - tk_parent=None, - bar_format=None, - cancel_callback=None, - **kwargs, - ): + def __init__(self, *args, **kwargs): try: import tkinter import tkinter.ttk as ttk except ImportError: import Tkinter as tkinter - import Tkinter.ttk as ttk + import ttk as ttk + try: + grab = kwargs.pop("grab") + except KeyError: + grab = False + try: + tk_parent = kwargs.pop("tk_parent") + except KeyError: + tk_parent = None + try: + self._cancel_callback = kwargs.pop("cancel_callback") + except KeyError: + self._cancel_callback = None + try: + bar_format = kwargs.pop("bar_format") + except KeyError: + bar_format = None kwargs["gui"] = True - self._cancel_callback = cancel_callback # Tkinter specific default bar format if bar_format is None: @@ -261,15 +268,11 @@ class tqdm_tk(tqdm_gui): self.tk_text_var = tkinter.StringVar(self.tk_window) pbar_frame = ttk.Frame(self.tk_window, padding=5) pbar_frame.pack() - self.tk_desc_label = ttk.Label( - pbar_frame, - textvariable=self.tk_desc_var, - wraplength=600, - anchor="center", - justify="center", - ) - self.tk_desc_label.pack() - self.tk_label = ttk.Label( + self.tk_desc_frame = ttk.Frame(pbar_frame) + self.tk_desc_frame.pack() + self.tk_desc_label = None + self.ttk_label = ttk.Label + self.tk_label = self.ttk_label( pbar_frame, textvariable=self.tk_text_var, wraplength=600, @@ -299,7 +302,21 @@ class tqdm_tk(tqdm_gui): def display(self): self.tk_n_var.set(self.n) - self.tk_desc_var.set(self.desc) + if self.desc: + if self.tk_desc_label is None: + self.tk_desc_label = self.ttk_label( + self.tk_desc_frame, + textvariable=self.tk_desc_var, + wraplength=600, + anchor="center", + justify="center", + ) + self.tk_desc_label.pack() + self.tk_desc_var.set(self.desc) + else: + if self.tk_desc_label is not None: + self.tk_desc_label.destroy() + self.tk_desc_label = None self.tk_text_var.set( self.format_meter( n=self.n, From dea31091d762e95af586d2403098629d04cb7bb5 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Thu, 16 Jul 2020 14:34:40 -0400 Subject: [PATCH 05/24] subclass std_tqdm instead Codebase is less fragmented this way too. Not clear why __iter__ and update are overridden in tqdm_gui... --- tqdm/gui.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 85c405ac..f995442d 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -188,7 +188,7 @@ def tgrange(*args, **kwargs): return tqdm_gui(_range(*args), **kwargs) -class tqdm_tk(tqdm_gui): +class tqdm_tk(std_tqdm): """ Experimental Tkinter GUI version of tqdm! """ @@ -230,9 +230,11 @@ class tqdm_tk(tqdm_gui): "{percentage:3.0f}%" ) - # don't want to share __init__ with tqdm_gui - # preferably we would have a gui base class - std_tqdm.__init__(self, *args, **kwargs) + # This signals std_tqdm that it's a GUI but no need to crash + # Maybe there is a better way? + self.sp = object() + + super(tqdm_tk, self).__init__(*args, **kwargs) # Discover parent widget if tk_parent is None: @@ -300,6 +302,20 @@ class tqdm_tk(tqdm_gui): if grab: self.tk_window.grab_set() + def refresh(self, nolock=True, lock_args=None): + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + Ignored, behaves as if always set true + lock_args : tuple, optional + Ignored + """ + nolock = True # necessary to force true or is default true enough? + return super(tqdm_tk, self).refresh(nolock, lock_args) + def display(self): self.tk_n_var.set(self.n) if self.desc: From 5620dff37e3998bbb57e44f7ac9de213043aac6c Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 10:06:23 -0400 Subject: [PATCH 06/24] re-enable monitor thread --- tqdm/gui.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index f995442d..dc7693ed 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -193,9 +193,6 @@ class tqdm_tk(std_tqdm): Experimental Tkinter GUI version of tqdm! """ - # Monitor thread does not behave nicely with tkinter - monitor_interval = 0 - def __init__(self, *args, **kwargs): try: import tkinter From c40230edcd5581a5a1081c7318dbaa6314b1ee1c Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 10:06:59 -0400 Subject: [PATCH 07/24] Initially bring window to front --- tqdm/gui.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tqdm/gui.py b/tqdm/gui.py index dc7693ed..95ae930b 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -261,6 +261,11 @@ class tqdm_tk(std_tqdm): self.tk_window.protocol("WM_DELETE_WINDOW", self.cancel) self.tk_window.wm_title("tqdm_tk") + self.tk_window.wm_attributes("-topmost", 1) + self.tk_window.after( + 0, + lambda: self.tk_window.wm_attributes("-topmost", 0), + ) self.tk_n_var = tkinter.DoubleVar(self.tk_window, value=0) self.tk_desc_var = tkinter.StringVar(self.tk_window) self.tk_desc_var.set(self.desc) From 82cf6213e51b7768734b7f4453e817e911249816 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 12:13:23 -0400 Subject: [PATCH 08/24] early construction of tk_desc_label late construction can lead to a race condition in multithreaded `update` type environments that duplicates the object --- tqdm/gui.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 95ae930b..69b82c38 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -274,8 +274,20 @@ class tqdm_tk(std_tqdm): pbar_frame.pack() self.tk_desc_frame = ttk.Frame(pbar_frame) self.tk_desc_frame.pack() - self.tk_desc_label = None + self.tk_desc_var.set(self.desc) + # avoid importing ttk in display method self.ttk_label = ttk.Label + if self.desc: + self.tk_desc_label = self.ttk_label( + self.tk_desc_frame, + textvariable=self.tk_desc_var, + wraplength=600, + anchor="center", + justify="center", + ) + self.tk_desc_label.pack() + else: + self.tk_desc_label = None self.tk_label = self.ttk_label( pbar_frame, textvariable=self.tk_text_var, From f349fadbb66afb3d30cc5adaaddf78900e5acac9 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 17:16:51 -0400 Subject: [PATCH 09/24] avoid tkinter imports and startup if initially disabled --- tqdm/gui.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 69b82c38..c45223b2 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -194,12 +194,6 @@ class tqdm_tk(std_tqdm): """ def __init__(self, *args, **kwargs): - try: - import tkinter - import tkinter.ttk as ttk - except ImportError: - import Tkinter as tkinter - import ttk as ttk try: grab = kwargs.pop("grab") except KeyError: @@ -233,6 +227,16 @@ class tqdm_tk(std_tqdm): super(tqdm_tk, self).__init__(*args, **kwargs) + if self.disable: + return + + try: + import tkinter + import tkinter.ttk as ttk + except ImportError: + import Tkinter as tkinter + import ttk as ttk + # Discover parent widget if tk_parent is None: # this will error if tkinter.NoDefaultRoot() called @@ -249,9 +253,6 @@ class tqdm_tk(std_tqdm): else: self.tk_window = tkinter.Toplevel(tk_parent) - if self.disable: - return - warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 ) self.tk_dispatching = self.tk_dispatching_helper() @@ -373,7 +374,7 @@ class tqdm_tk(std_tqdm): self.close() def reset(self, total=None): - if total is not None: + if total is not None and not self.disable: self.tk_pbar.configure(maximum=total) super(tqdm_tk, self).reset(total) From 244132daeb7cca8d33dff293cca68a02404582db Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 17:25:41 -0400 Subject: [PATCH 10/24] make tkinter objects "private" --- tqdm/gui.py | 110 ++++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index c45223b2..00e46b02 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -246,76 +246,76 @@ class tqdm_tk(std_tqdm): raise ValueError("tk_parent required when using NoDefaultRoot") if tkparent is None: # use new default root window as display - self.tk_window = tkinter.Tk() + self._tk_window = tkinter.Tk() else: # some other windows already exist - self.tk_window = tkinter.Toplevel() + self._tk_window = tkinter.Toplevel() else: - self.tk_window = tkinter.Toplevel(tk_parent) + self._tk_window = tkinter.Toplevel(tk_parent) warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 ) - self.tk_dispatching = self.tk_dispatching_helper() - if not self.tk_dispatching: + self._tk_dispatching = self._tk_dispatching_helper() + if not self._tk_dispatching: # leave is problematic if the mainloop is not running self.leave = False - self.tk_window.protocol("WM_DELETE_WINDOW", self.cancel) - self.tk_window.wm_title("tqdm_tk") - self.tk_window.wm_attributes("-topmost", 1) - self.tk_window.after( + self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) + self._tk_window.wm_title("tqdm_tk") + self._tk_window.wm_attributes("-topmost", 1) + self._tk_window.after( 0, - lambda: self.tk_window.wm_attributes("-topmost", 0), + lambda: self._tk_window.wm_attributes("-topmost", 0), ) - self.tk_n_var = tkinter.DoubleVar(self.tk_window, value=0) - self.tk_desc_var = tkinter.StringVar(self.tk_window) - self.tk_desc_var.set(self.desc) - self.tk_text_var = tkinter.StringVar(self.tk_window) - pbar_frame = ttk.Frame(self.tk_window, padding=5) + self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) + self._tk_desc_var = tkinter.StringVar(self._tk_window) + self._tk_desc_var.set(self.desc) + self._tk_text_var = tkinter.StringVar(self._tk_window) + pbar_frame = ttk.Frame(self._tk_window, padding=5) pbar_frame.pack() - self.tk_desc_frame = ttk.Frame(pbar_frame) - self.tk_desc_frame.pack() - self.tk_desc_var.set(self.desc) + self._tk_desc_frame = ttk.Frame(pbar_frame) + self._tk_desc_frame.pack() + self._tk_desc_var.set(self.desc) # avoid importing ttk in display method - self.ttk_label = ttk.Label + self._ttk_label = ttk.Label if self.desc: - self.tk_desc_label = self.ttk_label( - self.tk_desc_frame, - textvariable=self.tk_desc_var, + self._tk_desc_label = self._ttk_label( + self._tk_desc_frame, + textvariable=self._tk_desc_var, wraplength=600, anchor="center", justify="center", ) - self.tk_desc_label.pack() + self._tk_desc_label.pack() else: - self.tk_desc_label = None - self.tk_label = self.ttk_label( + self._tk_desc_label = None + self._tk_label = self._ttk_label( pbar_frame, - textvariable=self.tk_text_var, + textvariable=self._tk_text_var, wraplength=600, anchor="center", justify="center", ) - self.tk_label.pack() - self.tk_pbar = ttk.Progressbar( + self._tk_label.pack() + self._tk_pbar = ttk.Progressbar( pbar_frame, - variable=self.tk_n_var, + variable=self._tk_n_var, length=450, ) if self.total is not None: - self.tk_pbar.configure(maximum=self.total) + self._tk_pbar.configure(maximum=self.total) else: - self.tk_pbar.configure(mode="indeterminate") - self.tk_pbar.pack() + self._tk_pbar.configure(mode="indeterminate") + self._tk_pbar.pack() if self._cancel_callback is not None: - self.tk_button = ttk.Button( + self._tk_button = ttk.Button( pbar_frame, text="Cancel", command=self.cancel, ) - self.tk_button.pack() + self._tk_button.pack() if grab: - self.tk_window.grab_set() + self._tk_window.grab_set() def refresh(self, nolock=True, lock_args=None): """ @@ -332,23 +332,23 @@ class tqdm_tk(std_tqdm): return super(tqdm_tk, self).refresh(nolock, lock_args) def display(self): - self.tk_n_var.set(self.n) + self._tk_n_var.set(self.n) if self.desc: - if self.tk_desc_label is None: - self.tk_desc_label = self.ttk_label( - self.tk_desc_frame, - textvariable=self.tk_desc_var, + if self._tk_desc_label is None: + self._tk_desc_label = self._ttk_label( + self._tk_desc_frame, + textvariable=self._tk_desc_var, wraplength=600, anchor="center", justify="center", ) - self.tk_desc_label.pack() - self.tk_desc_var.set(self.desc) + self._tk_desc_label.pack() + self._tk_desc_var.set(self.desc) else: - if self.tk_desc_label is not None: - self.tk_desc_label.destroy() - self.tk_desc_label = None - self.tk_text_var.set( + if self._tk_desc_label is not None: + self._tk_desc_label.destroy() + self._tk_desc_label = None + self._tk_text_var.set( self.format_meter( n=self.n, total=self.total, @@ -364,8 +364,8 @@ class tqdm_tk(std_tqdm): unit_divisor=self.unit_divisor, ) ) - if not self.tk_dispatching: - self.tk_window.update() + if not self._tk_dispatching: + self._tk_window.update() def cancel(self): """Call cancel_callback and close the progress bar""" @@ -375,7 +375,7 @@ class tqdm_tk(std_tqdm): def reset(self, total=None): if total is not None and not self.disable: - self.tk_pbar.configure(maximum=total) + self._tk_pbar.configure(maximum=total) super(tqdm_tk, self).reset(total) def close(self): @@ -388,19 +388,19 @@ class tqdm_tk(std_tqdm): self._instances.remove(self) def _close(): - self.tk_window.after(0, self.tk_window.destroy) - if not self.tk_dispatching: - self.tk_window.update() + self._tk_window.after(0, self._tk_window.destroy) + if not self._tk_dispatching: + self._tk_window.update() - self.tk_window.protocol("WM_DELETE_WINDOW", _close) + self._tk_window.protocol("WM_DELETE_WINDOW", _close) if not self.leave: _close() - def tk_dispatching_helper(self): + def _tk_dispatching_helper(self): """determine if Tkinter mainloop is dispatching events""" try: # Landing in CPython 3.10 - return self.tk_window.dispatching() + return self._tk_window.dispatching() except AttributeError: pass From 3ebb867043a7b95761fef0ee0ea9074b2b3de998 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 17:38:18 -0400 Subject: [PATCH 11/24] make desc appear only in title bar --- tqdm/gui.py | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 00e46b02..ed03b868 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -261,7 +261,7 @@ class tqdm_tk(std_tqdm): self.leave = False self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) - self._tk_window.wm_title("tqdm_tk") + self._tk_window.wm_title(self.desc) self._tk_window.wm_attributes("-topmost", 1) self._tk_window.after( 0, @@ -276,20 +276,7 @@ class tqdm_tk(std_tqdm): self._tk_desc_frame = ttk.Frame(pbar_frame) self._tk_desc_frame.pack() self._tk_desc_var.set(self.desc) - # avoid importing ttk in display method - self._ttk_label = ttk.Label - if self.desc: - self._tk_desc_label = self._ttk_label( - self._tk_desc_frame, - textvariable=self._tk_desc_var, - wraplength=600, - anchor="center", - justify="center", - ) - self._tk_desc_label.pack() - else: - self._tk_desc_label = None - self._tk_label = self._ttk_label( + self._tk_label = ttk.Label( pbar_frame, textvariable=self._tk_text_var, wraplength=600, @@ -317,6 +304,16 @@ class tqdm_tk(std_tqdm): if grab: self._tk_window.grab_set() + def set_description(self, desc=None, refresh=True): + self.set_description_str(desc, refresh) + + def set_description_str(self, desc=None, refresh=True): + self.desc = desc + if not self.disable: + self._tk_window.wm_title(desc) + if refresh and not self._tk_dispatching: + self._tk_window.update() + def refresh(self, nolock=True, lock_args=None): """ Force refresh the display of this bar. @@ -333,21 +330,6 @@ class tqdm_tk(std_tqdm): def display(self): self._tk_n_var.set(self.n) - if self.desc: - if self._tk_desc_label is None: - self._tk_desc_label = self._ttk_label( - self._tk_desc_frame, - textvariable=self._tk_desc_var, - wraplength=600, - anchor="center", - justify="center", - ) - self._tk_desc_label.pack() - self._tk_desc_var.set(self.desc) - else: - if self._tk_desc_label is not None: - self._tk_desc_label.destroy() - self._tk_desc_label = None self._tk_text_var.set( self.format_meter( n=self.n, From 22c4c7bc0279bf32cd616aec170808f6fdd4372f Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 18:05:11 -0400 Subject: [PATCH 12/24] better docstring for tqdm_tk, tqdm_tk.cancel --- tqdm/gui.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index ed03b868..bdb80844 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -191,6 +191,10 @@ def tgrange(*args, **kwargs): class tqdm_tk(std_tqdm): """ Experimental Tkinter GUI version of tqdm! + + Note: Window interactivity suffers if `tqdm_tk` is not running within + a Tkinter mainloop and values are generated infrequently. In this case, + consider calling `tqdm_tk.update(0)` frequently in the Tk thread. """ def __init__(self, *args, **kwargs): @@ -350,7 +354,9 @@ class tqdm_tk(std_tqdm): self._tk_window.update() def cancel(self): - """Call cancel_callback and close the progress bar""" + """Call cancel_callback and then close the progress bar + + Called when the window close or cancel buttons are clicked.""" if self._cancel_callback is not None: self._cancel_callback() self.close() From ff7cdccb890befa3553d21013d7a9df28484284f Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Mon, 20 Jul 2020 18:18:31 -0400 Subject: [PATCH 13/24] make tqdm_tk the default tqdm_gui --- tqdm/gui.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index bdb80844..6b98f4de 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -20,12 +20,13 @@ from .std import tqdm as std_tqdm from .utils import _range __author__ = {"github.com/": ["casperdcl", "lrq3000", "richardsheridan"]} -__all__ = ['tqdm_gui', 'tgrange', 'tqdm_tk', 'ttkrange', 'tqdm', 'trange'] +__all__ = ['tqdm', 'trange', 'tqdm_gui', 'tgrange', 'tqdm_mpl', 'tmplrange', + 'tqdm_tk', 'ttkrange'] -class tqdm_gui(std_tqdm): # pragma: no cover +class tqdm_mpl(std_tqdm): # pragma: no cover """ - Experimental GUI version of tqdm! + Experimental Matplotlib GUI version of tqdm! """ # TODO: @classmethod: write() on GUI? @@ -38,7 +39,7 @@ class tqdm_gui(std_tqdm): # pragma: no cover kwargs = kwargs.copy() kwargs['gui'] = True colour = kwargs.pop('colour', 'g') - super(tqdm_gui, self).__init__(*args, **kwargs) + super(tqdm_mpl, self).__init__(*args, **kwargs) # Initialize the GUI display if self.disable or not kwargs['gui']: @@ -180,15 +181,15 @@ class tqdm_gui(std_tqdm): # pragma: no cover self.plt.pause(1e-9) -def tgrange(*args, **kwargs): +def tmplrange(*args, **kwargs): """ - A shortcut for `tqdm.gui.tqdm(xrange(*args), **kwargs)`. + A shortcut for `tqdm.gui.tqdm_mpl(xrange(*args), **kwargs)`. On Python3+, `range` is used instead of `xrange`. """ - return tqdm_gui(_range(*args), **kwargs) + return tqdm_mpl(_range(*args), **kwargs) -class tqdm_tk(std_tqdm): +class tqdm_tk(std_tqdm): # pragma: no cover """ Experimental Tkinter GUI version of tqdm! @@ -417,5 +418,5 @@ def ttkrange(*args, **kwargs): # Aliases -tqdm = tqdm_gui -trange = tgrange +tqdm = tqdm_gui = tqdm_tk +trange = tgrange = ttkrange From 4303ba3e6ae8dd58e870c80854c1e9985c1ebb28 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Tue, 21 Jul 2020 07:12:46 -0400 Subject: [PATCH 14/24] clean up __init__ --- tqdm/gui.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 6b98f4de..ce35cdb1 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -246,10 +246,10 @@ class tqdm_tk(std_tqdm): # pragma: no cover if tk_parent is None: # this will error if tkinter.NoDefaultRoot() called try: - tkparent = tkinter._default_root + tk_parent = tkinter._default_root except AttributeError: raise ValueError("tk_parent required when using NoDefaultRoot") - if tkparent is None: + if tk_parent is None: # use new default root window as display self._tk_window = tkinter.Tk() else: @@ -273,22 +273,17 @@ class tqdm_tk(std_tqdm): # pragma: no cover lambda: self._tk_window.wm_attributes("-topmost", 0), ) self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) - self._tk_desc_var = tkinter.StringVar(self._tk_window) - self._tk_desc_var.set(self.desc) self._tk_text_var = tkinter.StringVar(self._tk_window) pbar_frame = ttk.Frame(self._tk_window, padding=5) pbar_frame.pack() - self._tk_desc_frame = ttk.Frame(pbar_frame) - self._tk_desc_frame.pack() - self._tk_desc_var.set(self.desc) - self._tk_label = ttk.Label( + _tk_label = ttk.Label( pbar_frame, textvariable=self._tk_text_var, wraplength=600, anchor="center", justify="center", ) - self._tk_label.pack() + _tk_label.pack() self._tk_pbar = ttk.Progressbar( pbar_frame, variable=self._tk_n_var, @@ -300,12 +295,12 @@ class tqdm_tk(std_tqdm): # pragma: no cover self._tk_pbar.configure(mode="indeterminate") self._tk_pbar.pack() if self._cancel_callback is not None: - self._tk_button = ttk.Button( + _tk_button = ttk.Button( pbar_frame, text="Cancel", command=self.cancel, ) - self._tk_button.pack() + _tk_button.pack() if grab: self._tk_window.grab_set() From 10e9d3b39e45983ee071e57eb2a1b4237e7361bc Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Tue, 21 Jul 2020 07:17:01 -0400 Subject: [PATCH 15/24] reset can change determinate/indeterminate mode --- tqdm/gui.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index ce35cdb1..259725a6 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -358,8 +358,19 @@ class tqdm_tk(std_tqdm): # pragma: no cover self.close() def reset(self, total=None): - if total is not None and not self.disable: - self._tk_pbar.configure(maximum=total) + """ + Resets to 0 iterations for repeated use. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + If not set, transform progress bar to indeterminate mode. + """ + if not self.disable: + if total is None: + self._tk_pbar.configure(maximum=100, mode="indeterminate") + else: + self._tk_pbar.configure(maximum=total, mode="determinate") super(tqdm_tk, self).reset(total) def close(self): From bf33319d5602ccb13bd25aa3c1484f4eb7279c60 Mon Sep 17 00:00:00 2001 From: Richard Sheridan Date: Tue, 21 Jul 2020 07:39:03 -0400 Subject: [PATCH 16/24] document additional params --- tqdm/gui.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tqdm/gui.py b/tqdm/gui.py index 259725a6..bfbe999b 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -199,6 +199,20 @@ class tqdm_tk(std_tqdm): # pragma: no cover """ def __init__(self, *args, **kwargs): + """ + This class accepts the following parameters *in addition* to + the parameters accepted by tqdm. + + Parameters + ---------- + grab : bool, optional + Grab the input across all windows of the process. + tk_parent : tkinter.Wm, optional + Parent Tk window. + cancel_callback : Callable, optional + Create a cancel button and set cancel_callback to be called + when the cancel or window close button is clicked. + """ try: grab = kwargs.pop("grab") except KeyError: From c7c88fb08a7bd1c7f154f7f7d1e7f9fe44339f67 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Sun, 25 Oct 2020 10:05:32 -0400 Subject: [PATCH 17/24] updates based on experience from usage improve docstrings and comments remove expected future cpython code destroy after idle tweak leave behavior --- tqdm/gui.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index bfbe999b..8c4c32d9 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -14,7 +14,7 @@ from __future__ import absolute_import, division from warnings import warn # to inherit from the tqdm class -from .std import TqdmExperimentalWarning +from .std import TqdmExperimentalWarning, TqdmWarning from .std import tqdm as std_tqdm # import compatibility functions and utilities from .utils import _range @@ -195,7 +195,7 @@ class tqdm_tk(std_tqdm): # pragma: no cover Note: Window interactivity suffers if `tqdm_tk` is not running within a Tkinter mainloop and values are generated infrequently. In this case, - consider calling `tqdm_tk.update(0)` frequently in the Tk thread. + consider calling `tqdm_tk.refresh()` frequently in the Tk thread. """ def __init__(self, *args, **kwargs): @@ -213,6 +213,8 @@ class tqdm_tk(std_tqdm): # pragma: no cover Create a cancel button and set cancel_callback to be called when the cancel or window close button is clicked. """ + # only meaningful to set this warning if someone sets the flag + self._warn_leave = "leave" in kwargs try: grab = kwargs.pop("grab") except KeyError: @@ -230,8 +232,6 @@ class tqdm_tk(std_tqdm): # pragma: no cover except KeyError: bar_format = None - kwargs["gui"] = True - # Tkinter specific default bar format if bar_format is None: kwargs["bar_format"] = ( @@ -243,6 +243,7 @@ class tqdm_tk(std_tqdm): # pragma: no cover # This signals std_tqdm that it's a GUI but no need to crash # Maybe there is a better way? self.sp = object() + kwargs["gui"] = True super(tqdm_tk, self).__init__(*args, **kwargs) @@ -276,7 +277,6 @@ class tqdm_tk(std_tqdm): # pragma: no cover ) self._tk_dispatching = self._tk_dispatching_helper() if not self._tk_dispatching: - # leave is problematic if the mainloop is not running self.leave = False self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) @@ -335,7 +335,7 @@ class tqdm_tk(std_tqdm): # pragma: no cover Parameters ---------- nolock : bool, optional - Ignored, behaves as if always set true + Ignored, behaves as if always set True lock_args : tuple, optional Ignored """ @@ -397,22 +397,24 @@ class tqdm_tk(std_tqdm): # pragma: no cover self._instances.remove(self) def _close(): - self._tk_window.after(0, self._tk_window.destroy) + self._tk_window.after('idle', self._tk_window.destroy) if not self._tk_dispatching: self._tk_window.update() self._tk_window.protocol("WM_DELETE_WINDOW", _close) + + # if leave is set but we are self-dispatching, the left window is + # totally unresponsive unless the user manually dispatches if not self.leave: _close() + elif not self._tk_dispatching: + if self._warn_leave: + warn('leave flag ignored if not in tkinter mainloop', + TqdmWarning, stacklevel=2) + _close() def _tk_dispatching_helper(self): """determine if Tkinter mainloop is dispatching events""" - try: - # Landing in CPython 3.10 - return self._tk_window.dispatching() - except AttributeError: - pass - try: import tkinter except ImportError: From bc1bd7e27c57335f10467cae4a86249cc08fde0c Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Mon, 26 Oct 2020 16:04:53 -0400 Subject: [PATCH 18/24] satisfy codacy --- tqdm/gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 8c4c32d9..5a134c16 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -413,7 +413,8 @@ class tqdm_tk(std_tqdm): # pragma: no cover TqdmWarning, stacklevel=2) _close() - def _tk_dispatching_helper(self): + @staticmethod + def _tk_dispatching_helper(): """determine if Tkinter mainloop is dispatching events""" try: import tkinter From 059e02185cd68be63c245579f1b16972763e3e1d Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Mon, 26 Oct 2020 16:09:49 -0400 Subject: [PATCH 19/24] remove redundant flag toggle --- tqdm/gui.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 5a134c16..cb84042b 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -276,8 +276,6 @@ class tqdm_tk(std_tqdm): # pragma: no cover warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 ) self._tk_dispatching = self._tk_dispatching_helper() - if not self._tk_dispatching: - self.leave = False self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) self._tk_window.wm_title(self.desc) From 63b884d7b626c9ba4ff1af9957efe5f1365f70aa Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 9 Jan 2021 22:22:12 +0000 Subject: [PATCH 20/24] restore original tqdm.gui --- tqdm/gui.py | 270 ++------------------------------------------------ tqdm/tk.py | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+), 260 deletions(-) create mode 100644 tqdm/tk.py diff --git a/tqdm/gui.py b/tqdm/gui.py index cb84042b..0becd0c1 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -14,17 +14,16 @@ from __future__ import absolute_import, division from warnings import warn # to inherit from the tqdm class -from .std import TqdmExperimentalWarning, TqdmWarning +from .std import TqdmExperimentalWarning from .std import tqdm as std_tqdm # import compatibility functions and utilities from .utils import _range -__author__ = {"github.com/": ["casperdcl", "lrq3000", "richardsheridan"]} -__all__ = ['tqdm', 'trange', 'tqdm_gui', 'tgrange', 'tqdm_mpl', 'tmplrange', - 'tqdm_tk', 'ttkrange'] +__author__ = {"github.com/": ["casperdcl", "lrq3000"]} +__all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange'] -class tqdm_mpl(std_tqdm): # pragma: no cover +class tqdm_gui(std_tqdm): # pragma: no cover """ Experimental Matplotlib GUI version of tqdm! """ @@ -39,7 +38,7 @@ class tqdm_mpl(std_tqdm): # pragma: no cover kwargs = kwargs.copy() kwargs['gui'] = True colour = kwargs.pop('colour', 'g') - super(tqdm_mpl, self).__init__(*args, **kwargs) + super(tqdm_gui, self).__init__(*args, **kwargs) # Initialize the GUI display if self.disable or not kwargs['gui']: @@ -181,263 +180,14 @@ class tqdm_mpl(std_tqdm): # pragma: no cover self.plt.pause(1e-9) -def tmplrange(*args, **kwargs): +def tgrange(*args, **kwargs): """ - A shortcut for `tqdm.gui.tqdm_mpl(xrange(*args), **kwargs)`. + A shortcut for `tqdm.gui.tqdm(xrange(*args), **kwargs)`. On Python3+, `range` is used instead of `xrange`. """ - return tqdm_mpl(_range(*args), **kwargs) - - -class tqdm_tk(std_tqdm): # pragma: no cover - """ - Experimental Tkinter GUI version of tqdm! - - Note: Window interactivity suffers if `tqdm_tk` is not running within - a Tkinter mainloop and values are generated infrequently. In this case, - consider calling `tqdm_tk.refresh()` frequently in the Tk thread. - """ - - def __init__(self, *args, **kwargs): - """ - This class accepts the following parameters *in addition* to - the parameters accepted by tqdm. - - Parameters - ---------- - grab : bool, optional - Grab the input across all windows of the process. - tk_parent : tkinter.Wm, optional - Parent Tk window. - cancel_callback : Callable, optional - Create a cancel button and set cancel_callback to be called - when the cancel or window close button is clicked. - """ - # only meaningful to set this warning if someone sets the flag - self._warn_leave = "leave" in kwargs - try: - grab = kwargs.pop("grab") - except KeyError: - grab = False - try: - tk_parent = kwargs.pop("tk_parent") - except KeyError: - tk_parent = None - try: - self._cancel_callback = kwargs.pop("cancel_callback") - except KeyError: - self._cancel_callback = None - try: - bar_format = kwargs.pop("bar_format") - except KeyError: - bar_format = None - - # Tkinter specific default bar format - if bar_format is None: - kwargs["bar_format"] = ( - "{n_fmt}/{total_fmt}, {rate_noinv_fmt}\n" - "{elapsed} elapsed, {remaining} ETA\n\n" - "{percentage:3.0f}%" - ) - - # This signals std_tqdm that it's a GUI but no need to crash - # Maybe there is a better way? - self.sp = object() - kwargs["gui"] = True - - super(tqdm_tk, self).__init__(*args, **kwargs) - - if self.disable: - return - - try: - import tkinter - import tkinter.ttk as ttk - except ImportError: - import Tkinter as tkinter - import ttk as ttk - - # Discover parent widget - if tk_parent is None: - # this will error if tkinter.NoDefaultRoot() called - try: - tk_parent = tkinter._default_root - except AttributeError: - raise ValueError("tk_parent required when using NoDefaultRoot") - if tk_parent is None: - # use new default root window as display - self._tk_window = tkinter.Tk() - else: - # some other windows already exist - self._tk_window = tkinter.Toplevel() - else: - self._tk_window = tkinter.Toplevel(tk_parent) - - warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 - ) - self._tk_dispatching = self._tk_dispatching_helper() - - self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) - self._tk_window.wm_title(self.desc) - self._tk_window.wm_attributes("-topmost", 1) - self._tk_window.after( - 0, - lambda: self._tk_window.wm_attributes("-topmost", 0), - ) - self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) - self._tk_text_var = tkinter.StringVar(self._tk_window) - pbar_frame = ttk.Frame(self._tk_window, padding=5) - pbar_frame.pack() - _tk_label = ttk.Label( - pbar_frame, - textvariable=self._tk_text_var, - wraplength=600, - anchor="center", - justify="center", - ) - _tk_label.pack() - self._tk_pbar = ttk.Progressbar( - pbar_frame, - variable=self._tk_n_var, - length=450, - ) - if self.total is not None: - self._tk_pbar.configure(maximum=self.total) - else: - self._tk_pbar.configure(mode="indeterminate") - self._tk_pbar.pack() - if self._cancel_callback is not None: - _tk_button = ttk.Button( - pbar_frame, - text="Cancel", - command=self.cancel, - ) - _tk_button.pack() - if grab: - self._tk_window.grab_set() - - def set_description(self, desc=None, refresh=True): - self.set_description_str(desc, refresh) - - def set_description_str(self, desc=None, refresh=True): - self.desc = desc - if not self.disable: - self._tk_window.wm_title(desc) - if refresh and not self._tk_dispatching: - self._tk_window.update() - - def refresh(self, nolock=True, lock_args=None): - """ - Force refresh the display of this bar. - - Parameters - ---------- - nolock : bool, optional - Ignored, behaves as if always set True - lock_args : tuple, optional - Ignored - """ - nolock = True # necessary to force true or is default true enough? - return super(tqdm_tk, self).refresh(nolock, lock_args) - - def display(self): - self._tk_n_var.set(self.n) - self._tk_text_var.set( - self.format_meter( - n=self.n, - total=self.total, - elapsed=self._time() - self.start_t, - ncols=None, - prefix=self.desc, - ascii=self.ascii, - unit=self.unit, - unit_scale=self.unit_scale, - rate=1 / self.avg_time if self.avg_time else None, - bar_format=self.bar_format, - postfix=self.postfix, - unit_divisor=self.unit_divisor, - ) - ) - if not self._tk_dispatching: - self._tk_window.update() - - def cancel(self): - """Call cancel_callback and then close the progress bar - - Called when the window close or cancel buttons are clicked.""" - if self._cancel_callback is not None: - self._cancel_callback() - self.close() - - def reset(self, total=None): - """ - Resets to 0 iterations for repeated use. - - Parameters - ---------- - total : int or float, optional. Total to use for the new bar. - If not set, transform progress bar to indeterminate mode. - """ - if not self.disable: - if total is None: - self._tk_pbar.configure(maximum=100, mode="indeterminate") - else: - self._tk_pbar.configure(maximum=total, mode="determinate") - super(tqdm_tk, self).reset(total) - - def close(self): - if self.disable: - return - - self.disable = True - - with self.get_lock(): - self._instances.remove(self) - - def _close(): - self._tk_window.after('idle', self._tk_window.destroy) - if not self._tk_dispatching: - self._tk_window.update() - - self._tk_window.protocol("WM_DELETE_WINDOW", _close) - - # if leave is set but we are self-dispatching, the left window is - # totally unresponsive unless the user manually dispatches - if not self.leave: - _close() - elif not self._tk_dispatching: - if self._warn_leave: - warn('leave flag ignored if not in tkinter mainloop', - TqdmWarning, stacklevel=2) - _close() - - @staticmethod - def _tk_dispatching_helper(): - """determine if Tkinter mainloop is dispatching events""" - try: - import tkinter - except ImportError: - import Tkinter as tkinter - import sys - - codes = set((tkinter.mainloop.__code__, - tkinter.Misc.mainloop.__code__)) - for frame in sys._current_frames().values(): - while frame: - if frame.f_code in codes: - return True - frame = frame.f_back - return False - - -def ttkrange(*args, **kwargs): - """ - A shortcut for `tqdm.gui.tqdm_tk(xrange(*args), **kwargs)`. - On Python3+, `range` is used instead of `xrange`. - """ - return tqdm_tk(_range(*args), **kwargs) + return tqdm_gui(_range(*args), **kwargs) # Aliases -tqdm = tqdm_gui = tqdm_tk -trange = tgrange = ttkrange +tqdm = tqdm_gui +trange = tgrange diff --git a/tqdm/tk.py b/tqdm/tk.py new file mode 100644 index 00000000..e82e7202 --- /dev/null +++ b/tqdm/tk.py @@ -0,0 +1,279 @@ +""" +GUI progressbar decorator for iterators. +Includes a default `range` iterator printing to `stderr`. + +Usage: +>>> from tqdm.gui import trange, tqdm +>>> for i in trange(10): +... ... +""" +# future division is important to divide integers and get as +# a result precise floating numbers (instead of truncated int) +from __future__ import absolute_import, division + +from warnings import warn + +# to inherit from the tqdm class +from .std import TqdmExperimentalWarning, TqdmWarning +from .std import tqdm as std_tqdm +# import compatibility functions and utilities +from .utils import _range + +__author__ = {"github.com/": ["richardsheridan", "casperdcl"]} +__all__ = ['tqdm_tk', 'ttkrange', 'tqdm', 'trange'] + + +class tqdm_tk(std_tqdm): # pragma: no cover + """ + Experimental Tkinter GUI version of tqdm! + + Note: Window interactivity suffers if `tqdm_tk` is not running within + a Tkinter mainloop and values are generated infrequently. In this case, + consider calling `tqdm_tk.refresh()` frequently in the Tk thread. + """ + + # TODO: @classmethod: write() on GUI? + + def __init__(self, *args, **kwargs): + """ + This class accepts the following parameters *in addition* to + the parameters accepted by tqdm. + + Parameters + ---------- + grab : bool, optional + Grab the input across all windows of the process. + tk_parent : tkinter.Wm, optional + Parent Tk window. + cancel_callback : Callable, optional + Create a cancel button and set cancel_callback to be called + when the cancel or window close button is clicked. + """ + # only meaningful to set this warning if someone sets the flag + self._warn_leave = "leave" in kwargs + try: + grab = kwargs.pop("grab") + except KeyError: + grab = False + try: + tk_parent = kwargs.pop("tk_parent") + except KeyError: + tk_parent = None + try: + self._cancel_callback = kwargs.pop("cancel_callback") + except KeyError: + self._cancel_callback = None + try: + bar_format = kwargs.pop("bar_format") + except KeyError: + bar_format = None + + # Tkinter specific default bar format + if bar_format is None: + kwargs["bar_format"] = ( + "{n_fmt}/{total_fmt}, {rate_noinv_fmt}\n" + "{elapsed} elapsed, {remaining} ETA\n\n" + "{percentage:3.0f}%" + ) + + # This signals std_tqdm that it's a GUI but no need to crash + # Maybe there is a better way? + self.sp = object() + kwargs["gui"] = True + + super(tqdm_tk, self).__init__(*args, **kwargs) + + if self.disable: + return + + try: + import tkinter + import tkinter.ttk as ttk + except ImportError: + import Tkinter as tkinter + import ttk as ttk + + # Discover parent widget + if tk_parent is None: + # this will error if tkinter.NoDefaultRoot() called + try: + tk_parent = tkinter._default_root + except AttributeError: + raise ValueError("tk_parent required when using NoDefaultRoot") + if tk_parent is None: + # use new default root window as display + self._tk_window = tkinter.Tk() + else: + # some other windows already exist + self._tk_window = tkinter.Toplevel() + else: + self._tk_window = tkinter.Toplevel(tk_parent) + + warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 + ) + self._tk_dispatching = self._tk_dispatching_helper() + + self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) + self._tk_window.wm_title(self.desc) + self._tk_window.wm_attributes("-topmost", 1) + self._tk_window.after( + 0, + lambda: self._tk_window.wm_attributes("-topmost", 0), + ) + self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) + self._tk_text_var = tkinter.StringVar(self._tk_window) + pbar_frame = ttk.Frame(self._tk_window, padding=5) + pbar_frame.pack() + _tk_label = ttk.Label( + pbar_frame, + textvariable=self._tk_text_var, + wraplength=600, + anchor="center", + justify="center", + ) + _tk_label.pack() + self._tk_pbar = ttk.Progressbar( + pbar_frame, + variable=self._tk_n_var, + length=450, + ) + if self.total is not None: + self._tk_pbar.configure(maximum=self.total) + else: + self._tk_pbar.configure(mode="indeterminate") + self._tk_pbar.pack() + if self._cancel_callback is not None: + _tk_button = ttk.Button( + pbar_frame, + text="Cancel", + command=self.cancel, + ) + _tk_button.pack() + if grab: + self._tk_window.grab_set() + + def set_description(self, desc=None, refresh=True): + self.set_description_str(desc, refresh) + + def set_description_str(self, desc=None, refresh=True): + self.desc = desc + if not self.disable: + self._tk_window.wm_title(desc) + if refresh and not self._tk_dispatching: + self._tk_window.update() + + def refresh(self, nolock=True, lock_args=None): + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + Ignored, behaves as if always set True + lock_args : tuple, optional + Ignored + """ + nolock = True # necessary to force true or is default true enough? + return super(tqdm_tk, self).refresh(nolock, lock_args) + + def display(self): + self._tk_n_var.set(self.n) + self._tk_text_var.set( + self.format_meter( + n=self.n, + total=self.total, + elapsed=self._time() - self.start_t, + ncols=None, + prefix=self.desc, + ascii=self.ascii, + unit=self.unit, + unit_scale=self.unit_scale, + rate=1 / self.avg_time if self.avg_time else None, + bar_format=self.bar_format, + postfix=self.postfix, + unit_divisor=self.unit_divisor, + ) + ) + if not self._tk_dispatching: + self._tk_window.update() + + def cancel(self): + """Call cancel_callback and then close the progress bar + + Called when the window close or cancel buttons are clicked.""" + if self._cancel_callback is not None: + self._cancel_callback() + self.close() + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + If not set, transform progress bar to indeterminate mode. + """ + if not self.disable: + if total is None: + self._tk_pbar.configure(maximum=100, mode="indeterminate") + else: + self._tk_pbar.configure(maximum=total, mode="determinate") + super(tqdm_tk, self).reset(total) + + def close(self): + if self.disable: + return + + self.disable = True + + with self.get_lock(): + self._instances.remove(self) + + def _close(): + self._tk_window.after('idle', self._tk_window.destroy) + if not self._tk_dispatching: + self._tk_window.update() + + self._tk_window.protocol("WM_DELETE_WINDOW", _close) + + # if leave is set but we are self-dispatching, the left window is + # totally unresponsive unless the user manually dispatches + if not self.leave: + _close() + elif not self._tk_dispatching: + if self._warn_leave: + warn('leave flag ignored if not in tkinter mainloop', + TqdmWarning, stacklevel=2) + _close() + + @staticmethod + def _tk_dispatching_helper(): + """determine if Tkinter mainloop is dispatching events""" + try: + import tkinter + except ImportError: + import Tkinter as tkinter + import sys + + codes = set((tkinter.mainloop.__code__, + tkinter.Misc.mainloop.__code__)) + for frame in sys._current_frames().values(): + while frame: + if frame.f_code in codes: + return True + frame = frame.f_back + return False + + +def ttkrange(*args, **kwargs): + """ + A shortcut for `tqdm.gui.tqdm_tk(xrange(*args), **kwargs)`. + On Python3+, `range` is used instead of `xrange`. + """ + return tqdm_tk(_range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_tk +trange = ttkrange From bffe77c50888359b28d49a507e4cc5e2e0fdacb4 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 9 Jan 2021 23:24:11 +0000 Subject: [PATCH 21/24] tk: tidy --- tqdm/gui.py | 19 ++-- tqdm/tk.py | 248 ++++++++++++++++++++-------------------------------- 2 files changed, 102 insertions(+), 165 deletions(-) diff --git a/tqdm/gui.py b/tqdm/gui.py index 0becd0c1..0d8714b2 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -40,11 +40,10 @@ class tqdm_gui(std_tqdm): # pragma: no cover colour = kwargs.pop('colour', 'g') super(tqdm_gui, self).__init__(*args, **kwargs) - # Initialize the GUI display - if self.disable or not kwargs['gui']: + if self.disable: return - warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2) + warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2) self.mpl = mpl self.plt = plt @@ -69,8 +68,8 @@ class tqdm_gui(std_tqdm): # pragma: no cover ax.set_ylim(0, 0.001) if total is not None: ax.set_xlim(0, 100) - ax.set_xlabel('percent') - self.fig.legend((self.line1, self.line2), ('cur', 'est'), + ax.set_xlabel("percent") + self.fig.legend((self.line1, self.line2), ("cur", "est"), loc='center right') # progressbar self.hspan = plt.axhspan(0, 0.001, xmin=0, xmax=0, color=colour) @@ -78,11 +77,11 @@ class tqdm_gui(std_tqdm): # pragma: no cover # ax.set_xlim(-60, 0) ax.set_xlim(0, 60) ax.invert_xaxis() - ax.set_xlabel('seconds') - ax.legend(('cur', 'est'), loc='lower left') + ax.set_xlabel("seconds") + ax.legend(("cur", "est"), loc='lower left') ax.grid() # ax.set_xlabel('seconds') - ax.set_ylabel((self.unit if self.unit else 'it') + '/s') + ax.set_ylabel((self.unit if self.unit else "it") + "/s") if self.unit_scale: plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0)) ax.yaxis.get_offset_text().set_x(-0.15) @@ -93,8 +92,6 @@ class tqdm_gui(std_tqdm): # pragma: no cover self.ax = ax def close(self): - # if not self.gui: - # return super(tqdm_gui, self).close() if self.disable: return @@ -175,7 +172,7 @@ class tqdm_gui(std_tqdm): # pragma: no cover line2.set_data(t_ago, zdata) d = self.format_dict - d["ncols"] = 0 + d['ncols'] = 0 ax.set_title(self.format_meter(**d), fontname="DejaVu Sans Mono", fontsize=11) self.plt.pause(1e-9) diff --git a/tqdm/tk.py b/tqdm/tk.py index e82e7202..a12332c3 100644 --- a/tqdm/tk.py +++ b/tqdm/tk.py @@ -7,16 +7,20 @@ Usage: >>> for i in trange(10): ... ... """ -# future division is important to divide integers and get as -# a result precise floating numbers (instead of truncated int) from __future__ import absolute_import, division +import sys from warnings import warn -# to inherit from the tqdm class +try: + import tkinter + import tkinter.ttk as ttk +except ImportError: + import Tkinter as tkinter + import ttk as ttk + from .std import TqdmExperimentalWarning, TqdmWarning from .std import tqdm as std_tqdm -# import compatibility functions and utilities from .utils import _range __author__ = {"github.com/": ["richardsheridan", "casperdcl"]} @@ -32,195 +36,82 @@ class tqdm_tk(std_tqdm): # pragma: no cover consider calling `tqdm_tk.refresh()` frequently in the Tk thread. """ - # TODO: @classmethod: write() on GUI? + # TODO: @classmethod: write()? def __init__(self, *args, **kwargs): """ This class accepts the following parameters *in addition* to - the parameters accepted by tqdm. + the parameters accepted by `tqdm`. Parameters ---------- grab : bool, optional Grab the input across all windows of the process. - tk_parent : tkinter.Wm, optional + tk_parent : `tkinter.Wm`, optional Parent Tk window. cancel_callback : Callable, optional - Create a cancel button and set cancel_callback to be called + Create a cancel button and set `cancel_callback` to be called when the cancel or window close button is clicked. """ - # only meaningful to set this warning if someone sets the flag - self._warn_leave = "leave" in kwargs - try: - grab = kwargs.pop("grab") - except KeyError: - grab = False - try: - tk_parent = kwargs.pop("tk_parent") - except KeyError: - tk_parent = None - try: - self._cancel_callback = kwargs.pop("cancel_callback") - except KeyError: - self._cancel_callback = None - try: - bar_format = kwargs.pop("bar_format") - except KeyError: - bar_format = None - - # Tkinter specific default bar format - if bar_format is None: - kwargs["bar_format"] = ( - "{n_fmt}/{total_fmt}, {rate_noinv_fmt}\n" - "{elapsed} elapsed, {remaining} ETA\n\n" - "{percentage:3.0f}%" - ) - - # This signals std_tqdm that it's a GUI but no need to crash - # Maybe there is a better way? - self.sp = object() - kwargs["gui"] = True - + kwargs = kwargs.copy() + kwargs['gui'] = True + kwargs['bar_format'] = kwargs.get( + 'bar_format', "{l_bar}{r_bar}").replace("{bar}", "") + # convert disable = None to False + kwargs['disable'] = bool(kwargs.get('disable', False)) + colour = kwargs.pop('colour', "blue") + self._warn_leave = 'leave' in kwargs + grab = kwargs.pop('grab', False) + tk_parent = kwargs.pop('tk_parent', None) + self._cancel_callback = kwargs.pop('cancel_callback', None) super(tqdm_tk, self).__init__(*args, **kwargs) if self.disable: return - try: - import tkinter - import tkinter.ttk as ttk - except ImportError: - import Tkinter as tkinter - import ttk as ttk - - # Discover parent widget - if tk_parent is None: - # this will error if tkinter.NoDefaultRoot() called + if tk_parent is None: # Discover parent widget try: tk_parent = tkinter._default_root except AttributeError: - raise ValueError("tk_parent required when using NoDefaultRoot") - if tk_parent is None: - # use new default root window as display + raise AttributeError( + "`tk_parent` required when using `tkinter.NoDefaultRoot()`") + if tk_parent is None: # use new default root window as display self._tk_window = tkinter.Tk() - else: - # some other windows already exist + else: # some other windows already exist self._tk_window = tkinter.Toplevel() else: self._tk_window = tkinter.Toplevel(tk_parent) - warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2 - ) + warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2) self._tk_dispatching = self._tk_dispatching_helper() self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) self._tk_window.wm_title(self.desc) self._tk_window.wm_attributes("-topmost", 1) - self._tk_window.after( - 0, - lambda: self._tk_window.wm_attributes("-topmost", 0), - ) + self._tk_window.after(0, lambda: self._tk_window.wm_attributes("-topmost", 0)) self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) self._tk_text_var = tkinter.StringVar(self._tk_window) pbar_frame = ttk.Frame(self._tk_window, padding=5) pbar_frame.pack() - _tk_label = ttk.Label( - pbar_frame, - textvariable=self._tk_text_var, - wraplength=600, - anchor="center", - justify="center", - ) + _tk_label = ttk.Label(pbar_frame, textvariable=self._tk_text_var, + wraplength=600, anchor="center", justify="center") _tk_label.pack() self._tk_pbar = ttk.Progressbar( - pbar_frame, - variable=self._tk_n_var, - length=450, - ) + pbar_frame, variable=self._tk_n_var, length=450) + # WIP: is this the best way to set colour? + # WIP: what about error/complete (green/red) as with notebook? + self._tk_pbar.tk_setPalette(colour) if self.total is not None: self._tk_pbar.configure(maximum=self.total) else: self._tk_pbar.configure(mode="indeterminate") self._tk_pbar.pack() if self._cancel_callback is not None: - _tk_button = ttk.Button( - pbar_frame, - text="Cancel", - command=self.cancel, - ) + _tk_button = ttk.Button(pbar_frame, text="Cancel", command=self.cancel) _tk_button.pack() if grab: self._tk_window.grab_set() - def set_description(self, desc=None, refresh=True): - self.set_description_str(desc, refresh) - - def set_description_str(self, desc=None, refresh=True): - self.desc = desc - if not self.disable: - self._tk_window.wm_title(desc) - if refresh and not self._tk_dispatching: - self._tk_window.update() - - def refresh(self, nolock=True, lock_args=None): - """ - Force refresh the display of this bar. - - Parameters - ---------- - nolock : bool, optional - Ignored, behaves as if always set True - lock_args : tuple, optional - Ignored - """ - nolock = True # necessary to force true or is default true enough? - return super(tqdm_tk, self).refresh(nolock, lock_args) - - def display(self): - self._tk_n_var.set(self.n) - self._tk_text_var.set( - self.format_meter( - n=self.n, - total=self.total, - elapsed=self._time() - self.start_t, - ncols=None, - prefix=self.desc, - ascii=self.ascii, - unit=self.unit, - unit_scale=self.unit_scale, - rate=1 / self.avg_time if self.avg_time else None, - bar_format=self.bar_format, - postfix=self.postfix, - unit_divisor=self.unit_divisor, - ) - ) - if not self._tk_dispatching: - self._tk_window.update() - - def cancel(self): - """Call cancel_callback and then close the progress bar - - Called when the window close or cancel buttons are clicked.""" - if self._cancel_callback is not None: - self._cancel_callback() - self.close() - - def reset(self, total=None): - """ - Resets to 0 iterations for repeated use. - - Parameters - ---------- - total : int or float, optional. Total to use for the new bar. - If not set, transform progress bar to indeterminate mode. - """ - if not self.disable: - if total is None: - self._tk_pbar.configure(maximum=100, mode="indeterminate") - else: - self._tk_pbar.configure(maximum=total, mode="determinate") - super(tqdm_tk, self).reset(total) - def close(self): if self.disable: return @@ -247,17 +138,66 @@ class tqdm_tk(std_tqdm): # pragma: no cover TqdmWarning, stacklevel=2) _close() + def clear(self, *_, **__): + pass + + def display(self): + self._tk_n_var.set(self.n) + d = self.format_dict + d['ncols'] = None + self._tk_text_var.set(self.format_meter(**d)) + if not self._tk_dispatching: + self._tk_window.update() + + def set_description(self, desc=None, refresh=True): + self.set_description_str(desc, refresh) + + def set_description_str(self, desc=None, refresh=True): + self.desc = desc + if not self.disable: + self._tk_window.wm_title(desc) + if refresh and not self._tk_dispatching: + self._tk_window.update() + + def refresh(self, nolock=True, **kwargs): # WIP: why is this needed? + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + Ignored, behaves as if always `True`. + """ + return super(tqdm_tk, self).refresh(nolock=True, **kwargs) + + def cancel(self): + """ + `cancel_callback()` followed by `close()` + when close/cancel buttons clicked. + """ + if self._cancel_callback is not None: + self._cancel_callback() + self.close() + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + if hasattr(self, '_tk_pbar'): + if total is None: + self._tk_pbar.configure(maximum=100, mode="indeterminate") + else: + self._tk_pbar.configure(maximum=total, mode="determinate") + super(tqdm_tk, self).reset(total=total) + @staticmethod def _tk_dispatching_helper(): """determine if Tkinter mainloop is dispatching events""" - try: - import tkinter - except ImportError: - import Tkinter as tkinter - import sys - - codes = set((tkinter.mainloop.__code__, - tkinter.Misc.mainloop.__code__)) + codes = {tkinter.mainloop.__code__, tkinter.Misc.mainloop.__code__} for frame in sys._current_frames().values(): while frame: if frame.f_code in codes: From 418f99a5c46e3e402c636b976bebe28bd470c385 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Sun, 10 Jan 2021 09:01:36 -0500 Subject: [PATCH 22/24] respond to comments --- tqdm/tk.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tqdm/tk.py b/tqdm/tk.py index a12332c3..04d5ca56 100644 --- a/tqdm/tk.py +++ b/tqdm/tk.py @@ -59,7 +59,6 @@ class tqdm_tk(std_tqdm): # pragma: no cover 'bar_format', "{l_bar}{r_bar}").replace("{bar}", "") # convert disable = None to False kwargs['disable'] = bool(kwargs.get('disable', False)) - colour = kwargs.pop('colour', "blue") self._warn_leave = 'leave' in kwargs grab = kwargs.pop('grab', False) tk_parent = kwargs.pop('tk_parent', None) @@ -98,9 +97,6 @@ class tqdm_tk(std_tqdm): # pragma: no cover _tk_label.pack() self._tk_pbar = ttk.Progressbar( pbar_frame, variable=self._tk_n_var, length=450) - # WIP: is this the best way to set colour? - # WIP: what about error/complete (green/red) as with notebook? - self._tk_pbar.tk_setPalette(colour) if self.total is not None: self._tk_pbar.configure(maximum=self.total) else: @@ -159,17 +155,6 @@ class tqdm_tk(std_tqdm): # pragma: no cover if refresh and not self._tk_dispatching: self._tk_window.update() - def refresh(self, nolock=True, **kwargs): # WIP: why is this needed? - """ - Force refresh the display of this bar. - - Parameters - ---------- - nolock : bool, optional - Ignored, behaves as if always `True`. - """ - return super(tqdm_tk, self).refresh(nolock=True, **kwargs) - def cancel(self): """ `cancel_callback()` followed by `close()` From 13ccf1d5f53c3225ba4352896846496db4d9490b Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Sun, 10 Jan 2021 09:02:31 -0500 Subject: [PATCH 23/24] cut out default pipes from {l_bar}{r_bar} --- tqdm/tk.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tqdm/tk.py b/tqdm/tk.py index 04d5ca56..505d368d 100644 --- a/tqdm/tk.py +++ b/tqdm/tk.py @@ -141,7 +141,11 @@ class tqdm_tk(std_tqdm): # pragma: no cover self._tk_n_var.set(self.n) d = self.format_dict d['ncols'] = None - self._tk_text_var.set(self.format_meter(**d)) + text = self.format_meter(**d) + # fixup only default bar format + if self.bar_format == "{l_bar}{r_bar}": + text = text.replace("||", "", 1) + self._tk_text_var.set(text) if not self._tk_dispatching: self._tk_window.update() From 6fb8cc8e1ca3e5594c44953f66bbce13abd08e46 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 10 Jan 2021 14:41:36 +0000 Subject: [PATCH 24/24] more pipe removal --- tqdm/tk.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tqdm/tk.py b/tqdm/tk.py index 505d368d..f8bb93e5 100644 --- a/tqdm/tk.py +++ b/tqdm/tk.py @@ -143,9 +143,8 @@ class tqdm_tk(std_tqdm): # pragma: no cover d['ncols'] = None text = self.format_meter(**d) # fixup only default bar format - if self.bar_format == "{l_bar}{r_bar}": - text = text.replace("||", "", 1) - self._tk_text_var.set(text) + self._tk_text_var.set( + text.replace("||", "", 1) if "{l_bar}{r_bar}" in self.bar_format else text) if not self._tk_dispatching: self._tk_window.update()