Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/tqdm/tk.py @ 68:5028fdace37b
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 16:23:26 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
67:0e9998148a16 | 68:5028fdace37b |
---|---|
1 """ | |
2 Tkinter GUI progressbar decorator for iterators. | |
3 | |
4 Usage: | |
5 >>> from tqdm.tk import trange, tqdm | |
6 >>> for i in trange(10): | |
7 ... ... | |
8 """ | |
9 import re | |
10 import sys | |
11 import tkinter | |
12 import tkinter.ttk as ttk | |
13 from warnings import warn | |
14 | |
15 from .std import TqdmExperimentalWarning, TqdmWarning | |
16 from .std import tqdm as std_tqdm | |
17 | |
18 __author__ = {"github.com/": ["richardsheridan", "casperdcl"]} | |
19 __all__ = ['tqdm_tk', 'ttkrange', 'tqdm', 'trange'] | |
20 | |
21 | |
22 class tqdm_tk(std_tqdm): # pragma: no cover | |
23 """ | |
24 Experimental Tkinter GUI version of tqdm! | |
25 | |
26 Note: Window interactivity suffers if `tqdm_tk` is not running within | |
27 a Tkinter mainloop and values are generated infrequently. In this case, | |
28 consider calling `tqdm_tk.refresh()` frequently in the Tk thread. | |
29 """ | |
30 | |
31 # TODO: @classmethod: write()? | |
32 | |
33 def __init__(self, *args, **kwargs): | |
34 """ | |
35 This class accepts the following parameters *in addition* to | |
36 the parameters accepted by `tqdm`. | |
37 | |
38 Parameters | |
39 ---------- | |
40 grab : bool, optional | |
41 Grab the input across all windows of the process. | |
42 tk_parent : `tkinter.Wm`, optional | |
43 Parent Tk window. | |
44 cancel_callback : Callable, optional | |
45 Create a cancel button and set `cancel_callback` to be called | |
46 when the cancel or window close button is clicked. | |
47 """ | |
48 kwargs = kwargs.copy() | |
49 kwargs['gui'] = True | |
50 # convert disable = None to False | |
51 kwargs['disable'] = bool(kwargs.get('disable', False)) | |
52 self._warn_leave = 'leave' in kwargs | |
53 grab = kwargs.pop('grab', False) | |
54 tk_parent = kwargs.pop('tk_parent', None) | |
55 self._cancel_callback = kwargs.pop('cancel_callback', None) | |
56 super().__init__(*args, **kwargs) | |
57 | |
58 if self.disable: | |
59 return | |
60 | |
61 if tk_parent is None: # Discover parent widget | |
62 try: | |
63 tk_parent = tkinter._default_root | |
64 except AttributeError: | |
65 raise AttributeError( | |
66 "`tk_parent` required when using `tkinter.NoDefaultRoot()`") | |
67 if tk_parent is None: # use new default root window as display | |
68 self._tk_window = tkinter.Tk() | |
69 else: # some other windows already exist | |
70 self._tk_window = tkinter.Toplevel() | |
71 else: | |
72 self._tk_window = tkinter.Toplevel(tk_parent) | |
73 | |
74 warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2) | |
75 self._tk_dispatching = self._tk_dispatching_helper() | |
76 | |
77 self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) | |
78 self._tk_window.wm_title(self.desc) | |
79 self._tk_window.wm_attributes("-topmost", 1) | |
80 self._tk_window.after(0, lambda: self._tk_window.wm_attributes("-topmost", 0)) | |
81 self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) | |
82 self._tk_text_var = tkinter.StringVar(self._tk_window) | |
83 pbar_frame = ttk.Frame(self._tk_window, padding=5) | |
84 pbar_frame.pack() | |
85 _tk_label = ttk.Label(pbar_frame, textvariable=self._tk_text_var, | |
86 wraplength=600, anchor="center", justify="center") | |
87 _tk_label.pack() | |
88 self._tk_pbar = ttk.Progressbar( | |
89 pbar_frame, variable=self._tk_n_var, length=450) | |
90 if self.total is not None: | |
91 self._tk_pbar.configure(maximum=self.total) | |
92 else: | |
93 self._tk_pbar.configure(mode="indeterminate") | |
94 self._tk_pbar.pack() | |
95 if self._cancel_callback is not None: | |
96 _tk_button = ttk.Button(pbar_frame, text="Cancel", command=self.cancel) | |
97 _tk_button.pack() | |
98 if grab: | |
99 self._tk_window.grab_set() | |
100 | |
101 def close(self): | |
102 if self.disable: | |
103 return | |
104 | |
105 self.disable = True | |
106 | |
107 with self.get_lock(): | |
108 self._instances.remove(self) | |
109 | |
110 def _close(): | |
111 self._tk_window.after('idle', self._tk_window.destroy) | |
112 if not self._tk_dispatching: | |
113 self._tk_window.update() | |
114 | |
115 self._tk_window.protocol("WM_DELETE_WINDOW", _close) | |
116 | |
117 # if leave is set but we are self-dispatching, the left window is | |
118 # totally unresponsive unless the user manually dispatches | |
119 if not self.leave: | |
120 _close() | |
121 elif not self._tk_dispatching: | |
122 if self._warn_leave: | |
123 warn("leave flag ignored if not in tkinter mainloop", | |
124 TqdmWarning, stacklevel=2) | |
125 _close() | |
126 | |
127 def clear(self, *_, **__): | |
128 pass | |
129 | |
130 def display(self, *_, **__): | |
131 self._tk_n_var.set(self.n) | |
132 d = self.format_dict | |
133 # remove {bar} | |
134 d['bar_format'] = (d['bar_format'] or "{l_bar}<bar/>{r_bar}").replace( | |
135 "{bar}", "<bar/>") | |
136 msg = self.format_meter(**d) | |
137 if '<bar/>' in msg: | |
138 msg = "".join(re.split(r'\|?<bar/>\|?', msg, maxsplit=1)) | |
139 self._tk_text_var.set(msg) | |
140 if not self._tk_dispatching: | |
141 self._tk_window.update() | |
142 | |
143 def set_description(self, desc=None, refresh=True): | |
144 self.set_description_str(desc, refresh) | |
145 | |
146 def set_description_str(self, desc=None, refresh=True): | |
147 self.desc = desc | |
148 if not self.disable: | |
149 self._tk_window.wm_title(desc) | |
150 if refresh and not self._tk_dispatching: | |
151 self._tk_window.update() | |
152 | |
153 def cancel(self): | |
154 """ | |
155 `cancel_callback()` followed by `close()` | |
156 when close/cancel buttons clicked. | |
157 """ | |
158 if self._cancel_callback is not None: | |
159 self._cancel_callback() | |
160 self.close() | |
161 | |
162 def reset(self, total=None): | |
163 """ | |
164 Resets to 0 iterations for repeated use. | |
165 | |
166 Parameters | |
167 ---------- | |
168 total : int or float, optional. Total to use for the new bar. | |
169 """ | |
170 if hasattr(self, '_tk_pbar'): | |
171 if total is None: | |
172 self._tk_pbar.configure(maximum=100, mode="indeterminate") | |
173 else: | |
174 self._tk_pbar.configure(maximum=total, mode="determinate") | |
175 super().reset(total=total) | |
176 | |
177 @staticmethod | |
178 def _tk_dispatching_helper(): | |
179 """determine if Tkinter mainloop is dispatching events""" | |
180 codes = {tkinter.mainloop.__code__, tkinter.Misc.mainloop.__code__} | |
181 for frame in sys._current_frames().values(): | |
182 while frame: | |
183 if frame.f_code in codes: | |
184 return True | |
185 frame = frame.f_back | |
186 return False | |
187 | |
188 | |
189 def ttkrange(*args, **kwargs): | |
190 """Shortcut for `tqdm.tk.tqdm(range(*args), **kwargs)`.""" | |
191 return tqdm_tk(range(*args), **kwargs) | |
192 | |
193 | |
194 # Aliases | |
195 tqdm = tqdm_tk | |
196 trange = ttkrange |