gui.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. """
  2. Matplotlib GUI progressbar decorator for iterators.
  3. Usage:
  4. >>> from tqdm.gui import trange, tqdm
  5. >>> for i in trange(10):
  6. ... ...
  7. """
  8. # future division is important to divide integers and get as
  9. # a result precise floating numbers (instead of truncated int)
  10. import re
  11. from warnings import warn
  12. # to inherit from the tqdm class
  13. from .std import TqdmExperimentalWarning
  14. from .std import tqdm as std_tqdm
  15. # import compatibility functions and utilities
  16. __author__ = {"github.com/": ["casperdcl", "lrq3000"]}
  17. __all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange']
  18. class tqdm_gui(std_tqdm): # pragma: no cover
  19. """Experimental Matplotlib GUI version of tqdm!"""
  20. # TODO: @classmethod: write() on GUI?
  21. def __init__(self, *args, **kwargs):
  22. from collections import deque
  23. import matplotlib as mpl
  24. import matplotlib.pyplot as plt
  25. kwargs = kwargs.copy()
  26. kwargs['gui'] = True
  27. colour = kwargs.pop('colour', 'g')
  28. super(tqdm_gui, self).__init__(*args, **kwargs)
  29. if self.disable:
  30. return
  31. warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
  32. self.mpl = mpl
  33. self.plt = plt
  34. # Remember if external environment uses toolbars
  35. self.toolbar = self.mpl.rcParams['toolbar']
  36. self.mpl.rcParams['toolbar'] = 'None'
  37. self.mininterval = max(self.mininterval, 0.5)
  38. self.fig, ax = plt.subplots(figsize=(9, 2.2))
  39. # self.fig.subplots_adjust(bottom=0.2)
  40. total = self.__len__() # avoids TypeError on None #971
  41. if total is not None:
  42. self.xdata = []
  43. self.ydata = []
  44. self.zdata = []
  45. else:
  46. self.xdata = deque([])
  47. self.ydata = deque([])
  48. self.zdata = deque([])
  49. self.line1, = ax.plot(self.xdata, self.ydata, color='b')
  50. self.line2, = ax.plot(self.xdata, self.zdata, color='k')
  51. ax.set_ylim(0, 0.001)
  52. if total is not None:
  53. ax.set_xlim(0, 100)
  54. ax.set_xlabel("percent")
  55. self.fig.legend((self.line1, self.line2), ("cur", "est"),
  56. loc='center right')
  57. # progressbar
  58. self.hspan = plt.axhspan(0, 0.001, xmin=0, xmax=0, color=colour)
  59. else:
  60. # ax.set_xlim(-60, 0)
  61. ax.set_xlim(0, 60)
  62. ax.invert_xaxis()
  63. ax.set_xlabel("seconds")
  64. ax.legend(("cur", "est"), loc='lower left')
  65. ax.grid()
  66. # ax.set_xlabel('seconds')
  67. ax.set_ylabel((self.unit if self.unit else "it") + "/s")
  68. if self.unit_scale:
  69. plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
  70. ax.yaxis.get_offset_text().set_x(-0.15)
  71. # Remember if external environment is interactive
  72. self.wasion = plt.isinteractive()
  73. plt.ion()
  74. self.ax = ax
  75. def close(self):
  76. if self.disable:
  77. return
  78. self.disable = True
  79. with self.get_lock():
  80. self._instances.remove(self)
  81. # Restore toolbars
  82. self.mpl.rcParams['toolbar'] = self.toolbar
  83. # Return to non-interactive mode
  84. if not self.wasion:
  85. self.plt.ioff()
  86. if self.leave:
  87. self.display()
  88. else:
  89. self.plt.close(self.fig)
  90. def clear(self, *_, **__):
  91. pass
  92. def display(self, *_, **__):
  93. n = self.n
  94. cur_t = self._time()
  95. elapsed = cur_t - self.start_t
  96. delta_it = n - self.last_print_n
  97. delta_t = cur_t - self.last_print_t
  98. # Inline due to multiple calls
  99. total = self.total
  100. xdata = self.xdata
  101. ydata = self.ydata
  102. zdata = self.zdata
  103. ax = self.ax
  104. line1 = self.line1
  105. line2 = self.line2
  106. # instantaneous rate
  107. y = delta_it / delta_t
  108. # overall rate
  109. z = n / elapsed
  110. # update line data
  111. xdata.append(n * 100.0 / total if total else cur_t)
  112. ydata.append(y)
  113. zdata.append(z)
  114. # Discard old values
  115. # xmin, xmax = ax.get_xlim()
  116. # if (not total) and elapsed > xmin * 1.1:
  117. if (not total) and elapsed > 66:
  118. xdata.popleft()
  119. ydata.popleft()
  120. zdata.popleft()
  121. ymin, ymax = ax.get_ylim()
  122. if y > ymax or z > ymax:
  123. ymax = 1.1 * y
  124. ax.set_ylim(ymin, ymax)
  125. ax.figure.canvas.draw()
  126. if total:
  127. line1.set_data(xdata, ydata)
  128. line2.set_data(xdata, zdata)
  129. try:
  130. poly_lims = self.hspan.get_xy()
  131. except AttributeError:
  132. self.hspan = self.plt.axhspan(0, 0.001, xmin=0, xmax=0, color='g')
  133. poly_lims = self.hspan.get_xy()
  134. poly_lims[0, 1] = ymin
  135. poly_lims[1, 1] = ymax
  136. poly_lims[2] = [n / total, ymax]
  137. poly_lims[3] = [poly_lims[2, 0], ymin]
  138. if len(poly_lims) > 4:
  139. poly_lims[4, 1] = ymin
  140. self.hspan.set_xy(poly_lims)
  141. else:
  142. t_ago = [cur_t - i for i in xdata]
  143. line1.set_data(t_ago, ydata)
  144. line2.set_data(t_ago, zdata)
  145. d = self.format_dict
  146. # remove {bar}
  147. d['bar_format'] = (d['bar_format'] or "{l_bar}<bar/>{r_bar}").replace(
  148. "{bar}", "<bar/>")
  149. msg = self.format_meter(**d)
  150. if '<bar/>' in msg:
  151. msg = "".join(re.split(r'\|?<bar/>\|?', msg, 1))
  152. ax.set_title(msg, fontname="DejaVu Sans Mono", fontsize=11)
  153. self.plt.pause(1e-9)
  154. def tgrange(*args, **kwargs):
  155. """Shortcut for `tqdm.gui.tqdm(range(*args), **kwargs)`."""
  156. return tqdm_gui(range(*args), **kwargs)
  157. # Aliases
  158. tqdm = tqdm_gui
  159. trange = tgrange