/ adafruit_progressbar.py
adafruit_progressbar.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2020 Brent Rubell for Adafruit Industries
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_progressbar`
 24  ================================================================================
 25  
 26  Dynamic progress bar widget for CircuitPython displays
 27  
 28  
 29  * Author(s): Brent Rubell
 30  
 31  Implementation Notes
 32  --------------------
 33  
 34  **Software and Dependencies:**
 35  
 36  * Adafruit CircuitPython firmware for the supported boards:
 37    https://github.com/adafruit/circuitpython/releases
 38  
 39  """
 40  
 41  # imports
 42  import displayio
 43  
 44  __version__ = "0.0.0-auto.0"
 45  __repo__ = "https://github.com/brentru/Adafruit_CircuitPython_ProgressBar.git"
 46  
 47  # pylint: disable=too-many-arguments, too-few-public-methods
 48  class ProgressBar(displayio.TileGrid):
 49      """A dynamic progress bar widget.
 50  
 51      :param int x: The x-position of the top left corner.
 52      :param int y: The y-position of the top left corner.
 53      :param int width: The width of the progress bar.
 54      :param int height: The height of the progress bar.
 55      :param float progress: The percentage of the progress bar.
 56      :param bar_color: The color of the progress bar. Can be a hex
 57                                  value for color.
 58      :param int outline_color: The outline of the progress bar. Can be a hex
 59                              value for color.
 60      :param int stroke: Used for the outline_color
 61  
 62      """
 63  
 64      # pylint: disable=invalid-name
 65      def __init__(
 66          self,
 67          x,
 68          y,
 69          width,
 70          height,
 71          progress=0.0,
 72          bar_color=0x00FF00,
 73          outline_color=0xFFFFFF,
 74          stroke=1,
 75      ):
 76          assert isinstance(progress, float), "Progress must be a floating point value."
 77          self._bitmap = displayio.Bitmap(width, height, 3)
 78          self._palette = displayio.Palette(3)
 79          self._palette[0] = 0x0
 80          self._palette[1] = outline_color
 81          self._palette[2] = bar_color
 82  
 83          self._width = width
 84          self._height = height
 85  
 86          self._progress_val = 0.0
 87          self.progress = self._progress_val
 88          self.progress = progress
 89  
 90          # draw outline rectangle
 91          for _w in range(width):
 92              for line in range(stroke):
 93                  self._bitmap[_w, line] = 1
 94                  self._bitmap[_w, height - 1 - line] = 1
 95          for _h in range(height):
 96              for line in range(stroke):
 97                  self._bitmap[line, _h] = 1
 98                  self._bitmap[width - 1 - line, _h] = 1
 99          super().__init__(self._bitmap, pixel_shader=self._palette, x=x, y=y)
100  
101      @property
102      def progress(self):
103          """The percentage of the progress bar expressed as a
104          floating point number.
105  
106          """
107          return self._progress_val
108  
109      @progress.setter
110      def progress(self, value):
111          """Draws the progress bar
112  
113          :param float value: Progress bar value.
114          """
115          assert value <= 1.0, "Progress value may not be > 100%"
116          assert isinstance(
117              value, float
118          ), "Progress value must be a floating point value."
119          if self._progress_val > value:
120              # uncolorize range from width*value+margin to width-margin
121              # from right to left
122              _prev_pixel = max(2, int(self._width * self._progress_val - 2))
123              _new_pixel = max(int(self._width * value - 2), 2)
124              for _w in range(_prev_pixel, _new_pixel - 1, -1):
125                  for _h in range(2, self._height - 2):
126                      self._bitmap[_w, _h] = 0
127          else:
128              # fill from the previous x pixel to the new x pixel
129              _prev_pixel = max(2, int(self._width * self._progress_val - 3))
130              _new_pixel = min(int(self._width * value - 2), int(self._width * 1.0 - 3))
131              for _w in range(_prev_pixel, _new_pixel + 1):
132                  for _h in range(2, self._height - 2):
133                      self._bitmap[_w, _h] = 2
134          self._progress_val = value
135  
136      @property
137      def fill(self):
138          """The fill of the progress bar. Can be a hex value for a color or ``None`` for
139          transparent.
140  
141          """
142          return self._palette[0]
143  
144      @fill.setter
145      def fill(self, color):
146          """Sets the fill of the progress bar. Can be a hex value for a color or ``None`` for
147          transparent.
148  
149          """
150          if color is None:
151              self._palette[2] = 0
152              self._palette.make_transparent(0)
153          else:
154              self._palette[2] = color
155              self._palette.make_opaque(0)