restore original tqdm.gui

This commit is contained in:
Casper da Costa-Luis 2021-01-09 22:22:12 +00:00
parent 059e02185c
commit 63b884d7b6
No known key found for this signature in database
GPG Key ID: 986B408043AE090D
2 changed files with 289 additions and 260 deletions

View File

@ -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

279
tqdm/tk.py Normal file
View File

@ -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