[PATCH 07/13] Add documentation to the simpleline library for TUI

Martin Sivak msivak at redhat.com
Wed Aug 8 14:22:24 UTC 2012


You mean the :param? It is the syntax for the tool Python itself uses (Sphinx) as I pointed out in the cover letter.
We are not generating any docs atm and I just looked at all the tools available - most notably Doxygen, epydoc and Sphinx and this seems to be the best way.. it is nice, makes type explicit and supports Python and C/C++ :) 

Martin

----- Original Message -----
> Any reason for the different syntax of the docstrings?
> 
> On Wed, 2012-08-08 at 10:52 +0200, Martin Sivak wrote:
> > ---
> >  pyanaconda/ui/tui/simpleline/base.py    |  294
> >  +++++++++++++++++++++++++------
> >  pyanaconda/ui/tui/simpleline/widgets.py |   87 ++++++++-
> >  2 files changed, 323 insertions(+), 58 deletions(-)
> > 
> > diff --git a/pyanaconda/ui/tui/simpleline/base.py
> > b/pyanaconda/ui/tui/simpleline/base.py
> > index 573f28b..0c9f750 100644
> > --- a/pyanaconda/ui/tui/simpleline/base.py
> > +++ b/pyanaconda/ui/tui/simpleline/base.py
> > @@ -19,19 +19,47 @@
> >  # Red Hat Author(s): Martin Sivak <msivak at redhat.com>
> >  #
> >  
> > -__all__ = ["ExitMainLoop", "App", "UIScreen", "Widget"]
> > +__all__ = ["App", "UIScreen", "Widget"]
> >  
> >  import readline
> >  
> >  class ExitAllMainLoops(Exception):
> > +    """This exceptions ends the whole App mainloop structure.
> > App.run() quits
> > +       after it is processed."""
> >      pass
> >  
> >  class ExitMainLoop(Exception):
> > +    """This eceptions ends the outermost mainloop. Used internally
> > when dialogs
> > +       close."""
> >      pass
> >  
> > 
> >  class App(object):
> > +    """This is the main class for TUI screen handling. It is
> > responsible for
> > +       mainloop control and keeping track of the screen stack.
> > +
> > +       Screens are organized in stack structure so it is possible
> > to return
> > +       to caller when dialog or sub-screen closes.
> > +
> > +       It supports four window transitions:
> > +       - show new screen replacing the current one (linear
> > progression)
> > +       - show new screen keeping the current one in stack (hub &
> > spoke)
> > +       - show new screen and wait for it to end (dialog)
> > +       - close current window and return to the next one in stack
> > +       """
> > +
> >      def __init__(self, title, yes_or_no_question = None, width =
> >      80):
> > +        """
> > +        :param title: application title for whenewer we need to
> > display app name
> > +        :type title: unicode
> > +
> > +        :param yes_or_no_question: UIScreen object class used for
> > Quit dialog
> > +        :type yes_or_no_question: class UIScreen accepting
> > additional message arg
> > +
> > +        :param width: screen width for rendering purposes
> > +        :type width: int
> > +        """
> > +
> >          self._header = title
> >          self._spacer = "\n".join(2*[width*"="])
> >          self._width = width
> > @@ -40,36 +68,90 @@ class App(object):
> >          # screen stack contains triplets
> >          #  UIScreen to show
> >          #  arguments for it's show method
> > -        #  value indicating whether new mainloop is needed - None
> > = do nothing, True = execute, False = already running, exit when
> > window closes
> > +        #  value indicating whether new mainloop is needed
> > +        #   - None = do nothing
> > +        #   - True = execute new loop
> > +        #   - False = already running loop, exit when window
> > closes
> >          self._screens = []
> >  
> >      def switch_screen(self, ui, args = None):
> > -        """Schedules a screen to replace the current one."""
> > +        """Schedules a screen to replace the current one.
> > +
> > +        :param ui: screen to show
> > +        :type ui: instance of UIScreen
> > +
> > +        :param args: optional argument to pass to ui's refresh
> > method (can be used to select what item should be displayed or so)
> > +        :type args: anything
> > +
> > +        """
> > +
> >          oldscr, oldattr, oldloop = self._screens.pop()
> > +
> > +        # we have to keep the oldloop value so we stop
> > +        # dialog's mainloop if it ever uses switch_screen
> >          self._screens.append((ui, args, oldloop))
> >          self.redraw()
> >  
> >      def switch_screen_with_return(self, ui, args = None):
> > -        """Schedules a screen to show, but keeps the current one
> > in stack to return to, when the new one is closed."""
> > +        """Schedules a screen to show, but keeps the current one
> > in stack
> > +           to return to, when the new one is closed.
> > +
> > +        :param ui: screen to show
> > +        :type ui: UIScreen instance
> > +
> > +        :param args: optional argument, please see switch_screen
> > for details
> > +        :type args: anything
> > +        """
> > +
> >          self._screens.append((ui, args, None))
> >          self.redraw()
> >  
> >      def switch_screen_modal(self, ui, args = None):
> > -        """Starts a new screen right away, so the caller can
> > collect data back. When the new screen is closed, the caller is
> > redisplayed."""
> > +        """Starts a new screen right away, so the caller can
> > collect data back.
> > +        When the new screen is closed, the caller is redisplayed.
> > +
> > +        This method does not return until the new screen is
> > closed.
> > +
> > +        :param ui: screen to show
> > +        :type ui: UIScreen instance
> > +
> > +        :param args: optional argument, please see switch_screen
> > for details
> > +        :type args: anything
> > +        """
> > +
> > +        # set the third item to True so new loop gets started
> >          self._screens.append((ui, args, True))
> >          self._do_redraw()
> >  
> >      def schedule_screen(self, ui, args = None):
> > -        """Add screen to the bottom of the stack."""
> > +        """Add screen to the bottom of the stack. This is mostly
> > usefull
> > +        at the beginning to prepare the first screen hierarchy to
> > display.
> > +
> > +        :param ui: screen to show
> > +        :type ui: UIScreen instance
> > +
> > +        :param args: optional argument, please see switch_screen
> > for details
> > +        :type args: anything
> > +        """
> >          self._screens.insert(0, (ui, args, None))
> >  
> >      def close_screen(self, scr = None):
> > +        """Close the currently displayed screen and exit it's main
> > loop
> > +        if necessary. Next screen from the stack is then
> > displayed.
> > +
> > +        :param scr: if an UIScreen instance is passed it is
> > checked to be the screen we are trying to close.
> > +        :type scr: UIScreen instance
> > +        """
> > +
> >          oldscr, oldattr, oldloop = self._screens.pop()
> >          if scr is not None:
> >              assert oldscr == scr
> >  
> > +        # this cannot happen, if we are closing the window,
> > +        # the loop must have been running or not be there at all
> > +        assert oldloop != True
> > +
> >          # we are in modal window, end it's loop
> > -        assert oldloop != True # this cannot happen, if we are
> > closing the window, the loop must be running or not there
> >          if oldloop == False:
> >              raise ExitMainLoop()
> >  
> > @@ -80,19 +162,32 @@ class App(object):
> >  
> >      def _do_redraw(self):
> >          """Draws the current screen and returns True if user input
> >          is requested.
> > -           If modal screen is requested, starts a new loop and
> > initiates redraw after it ends."""
> > +           If modal screen is requested, starts a new loop and
> > initiates redraw after it ends.
> > +
> > +           :return: this method returns True if user input
> > processing is requested
> > +           :rtype: bool
> > +           """
> > +
> > +        # there is nothing to display, exit
> >          if not self._screens:
> >              raise ExitMainLoop()
> >  
> > +        # get the screen from the top of the stack
> >          screen, args, newloop = self._screens[-1]
> >  
> > +        # new mainloop is requested
> >          if newloop == True:
> > +            # change the record to indicate mainloop is running
> >              self._screens.pop()
> >              self._screens.append((screen, args, False))
> > +            # start the mainloop
> >              self.mainloop()
> > +            # after the mainloop ends, set the redraw flag
> > +            # and skip the input processing once, to redisplay the
> > screen first
> >              self.redraw()
> > -            input_needed = False # we have to skip input once, to
> > redisplay the screen first
> > +            input_needed = False
> >          else:
> > +            # get the widget tree from the screen and show it in
> > the screen
> >              input_needed = screen.refresh(args)
> >              screen.window.show_all()
> >              self._redraw = False
> > @@ -100,57 +195,99 @@ class App(object):
> >          return input_needed
> >  
> >      def run(self):
> > +        """This methods starts the application. Do not use
> > self.mainloop() directly
> > +        as run() handles all the required exceptions needed to
> > keep nested mainloops
> > +        working."""
> > +
> >          try:
> >              self.mainloop()
> >          except ExitAllMainLoops:
> >              pass
> >  
> >      def mainloop(self):
> > +        """Single mainloop. Do not use directly, start the
> > application using run()."""
> > +
> > +        # ask for redraw by default
> >          self._redraw = True
> > +
> > +        # inital state
> >          last_screen = None
> >          error_counter = 0
> > +
> > +        # run until there is nothing else to display
> >          while self._screens:
> > +            # if redraw is needed, separate the content on the
> > screen from the
> > +            # stuff we are about to display now
> >              if self._redraw:
> >                  print self._spacer
> >  
> >              try:
> > +                # draw the screen if redraw is needed or the
> > screen changed
> > +                # (unlikely to happen separately, but just be
> > sure)
> >                  if self._redraw or last_screen !=
> >                  self._screens[-1]:
> > +                    # we have fresh screen, reset error counter
> > +                    error_counter = 0
> >                      if not self._do_redraw():
> > +                        # if no input processing is requested, go
> > for another cycle
> >                          continue
> >  
> >                  last_screen = self._screens[-1][0]
> >  
> > +                # get the screen's prompt
> >                  prompt = last_screen.prompt()
> > +
> > +                # None means prompt handled the input by itself
> > +                # ask for redraw and continue
> >                  if prompt is None:
> >                      self.redraw()
> >                      continue
> >  
> > +                # get the input from user
> >                  c = raw_input(prompt)
> >  
> > +                # process the input, if it wasn't processed
> > (valid)
> > +                # increment the error counter
> >                  if not self.input(c):
> >                      error_counter += 1
> >  
> > +                # redraw the screen after 5 bad inputs
> >                  if error_counter >= 5:
> >                      self.redraw()
> >  
> > -                if self._redraw:
> > -                    error_counter = 0
> > +            # end just this loop
> >              except ExitMainLoop:
> >                  break
> > +
> > +            # propagate higher to end all loops
> > +            # not really needed here, but we might need
> > +            # more processing in the future
> >              except ExitAllMainLoops:
> >                  raise
> >  
> >      def input(self, key):
> > -        """Method called to process unhandled input key
> > presses."""
> > +        """Method called internally to process unhandled input key
> > presses.
> > +        Also handles the main quit and close commands.
> > +
> > +        :param key: the string entered by user
> > +        :type key: unicode
> > +
> > +        :return: True if key was processed, False if it was not
> > recognized
> > +        :rtype: True|False
> > +
> > +        """
> > +
> > +        # delegate the handling to active screen first
> >          if self._screens:
> >              key = self._screens[-1][0].input(key)
> >              if key is None:
> >                  return True
> >  
> > +        # global close command
> >          if self._screens and (key == 'c'):
> >              self.close_screen()
> >              return True
> >  
> > +        # global quit command
> >          elif self._screens and (key == 'q'):
> >              if self.quit_question:
> >                  d = self.quit_question(self, u"Do you really want
> >                  to quit?")
> > @@ -162,6 +299,7 @@ class App(object):
> >          return False
> >  
> >      def redraw(self):
> > +        """Set the redraw flag so the screen is refreshed as soon
> > as possible."""
> >          self._redraw = True
> >  
> >      @property
> > @@ -169,29 +307,51 @@ class App(object):
> >          return self._header
> >  
> >      @property
> > -    def store(self):
> > -        return self._store
> > -
> > -    @property
> >      def width(self):
> > +        """Return the total width of screen space we have
> > available."""
> >          return self._width
> >  
> >  class UIScreen(object):
> > +    """Base class representing one TUI Screen. Shares some API
> > with anaconda's GUI
> > +    to make it easy for devs to create similar UI with the
> > familiar API."""
> > +
> > +    # title line of the screen
> >      title = u"Screen.."
> >  
> >      def __init__(self, app):
> > +        """
> > +        :param app: reference to application main class
> > +        :type app: instance of class App
> > +        """
> > +
> >          self._app = app
> > +
> > +        # list that holds the content to be printed out
> >          self._window = []
> >  
> >      def refresh(self, args = None):
> > -        """Method which prepares the screen to self._window. If
> > user input is requested, return True."""
> > +        """Method which prepares the content desired on the screen
> > to self._window.
> > +
> > +        :param args: optional argument passed from switch_screen
> > calls
> > +        :type args: anything
> > +
> > +        :return: has to return True if input processing is
> > requested, otherwise
> > +                 the screen will get printed and the main loop
> > will continue
> > +        :rtype: True|False
> > +        """
> > +
> >          self._window = [self.title, u""]
> > +        return True
> >  
> >      @property
> >      def window(self):
> > +        """Return reference to the window instance. In TUI, just
> > return self."""
> >          return self
> >  
> >      def show_all(self):
> > +        """Prepares all elements of self._window for output and
> > then prints
> > +        them on the screen."""
> > +
> >          for w in self._window:
> >              if hasattr(w, "render"):
> >                  w.render(self.app.width)
> > @@ -200,32 +360,49 @@ class UIScreen(object):
> >      show = show_all
> >  
> >      def hide(self):
> > +        """This does nothing in TUI, it is here to make API
> > similar."""
> >          pass
> >  
> >      def input(self, key):
> > -        """Method called to process input. If the input is not
> > handled here, return it."""
> > +        """Method called to process input. If the input is not
> > handled here, return it.
> > +
> > +        :param key: input string to process
> > +        :type key: unicode
> > +
> > +        :return: return True or None if key was handled, False if
> > the screen should not
> > +                 process input on the App and key if you want it
> > to.
> > +        :rtype: True|False|None|unicode
> > +        """
> > +
> >          return key
> >  
> >      def prompt(self):
> > -        """Return the text to be shown as prompt or handle the
> > prompt and return None."""
> > +        """Return the text to be shown as prompt or handle the
> > prompt and return None.
> > +
> > +        :return: returns text to be shown next to the prompt for
> > input or None
> > +                 to skip further input processing
> > +        :rtype: unicode|None
> > +"""
> >          return u"\tPlease make your choice from above ['q' to
> >          quit]: "
> >  
> >      @property
> >      def app(self):
> > +        """The reference to this Screen's assigned App
> > instance."""
> >          return self._app
> >  
> >      def close(self):
> > +        """Close the current screen."""
> >          self.app.close_screen(self)
> >  
> >  class Widget(object):
> >      def __init__(self, max_width = None, default = None):
> >          """Initializes base Widgets buffer.
> >  
> > -           @param max_width server as a hint about screen size to
> > write method with default arguments
> > -           @type max_width int
> > +           :param max_width: server as a hint about screen size to
> > write method with default arguments
> > +           :type max_width: int
> >  
> > -           @param default string containing the default content to
> > fill the buffer with
> > -           @type default string
> > +           :param default: string containing the default content
> > to fill the buffer with
> > +           :type default: string
> >             """
> >  
> >          self._buffer = []
> > @@ -236,10 +413,13 @@ class Widget(object):
> >  
> >      @property
> >      def height(self):
> > +        """The current height of the internal buffer."""
> >          return len(self._buffer)
> >  
> >      @property
> >      def width(self):
> > +        """The current width of the internal buffer
> > +           (id of the first empty column)."""
> >          return reduce(lambda acc,l: max(acc, len(l)),
> >          self._buffer, 0)
> >  
> >      def clear(self):
> > @@ -255,8 +435,8 @@ class Widget(object):
> >      def render(self, width = None):
> >          """This method has to redraw the widget's self._buffer.
> >  
> > -           @param width the width of buffer requested by the
> > caller
> > -           @type width int
> > +           :param width: the width of buffer requested by the
> > caller
> > +           :type width: int
> >  
> >             This method will commonly call render of child widgets
> >             and then draw and write
> >             methods to copy their contents to self._buffer
> > @@ -264,10 +444,18 @@ class Widget(object):
> >          self.clear()
> >  
> >      def __unicode__(self):
> > +        """Method to render the screen when printing as unicode
> > string."""
> >          return u"\n".join([u"".join(l) for l in self._buffer])
> >  
> >      def setxy(self, row, col):
> > -        """Sets cursor position."""
> > +        """Sets cursor position.
> > +
> > +        :param row: row id, starts with 0 at the top of the screen
> > +        :type row: int
> > +
> > +        :param col: column id, starts with 0 on the left side of
> > the screen
> > +        :type col: int
> > +        """
> >          self._cursor = (row, col)
> >  
> >      @property
> > @@ -281,32 +469,33 @@ class Widget(object):
> >      def draw(self, w, row = None, col = None, block = False):
> >          """This method copies w widget's content to this widget's
> >          buffer at row, col position.
> >  
> > -           @param w widget to take content from
> > -           @type w class Widget
> > +           :param w: widget to take content from
> > +           :type w: class Widget
> >  
> > -           @param row row number to start at (default is at the
> > cursor position)
> > -           @type row int
> > +           :param row: row number to start at (default is at the
> > cursor position)
> > +           :type row: int
> >  
> > -           @param col column number to start at (default is at the
> > cursor position)
> > -           @type col int
> > +           :param col: column number to start at (default is at
> > the cursor position)
> > +           :type col: int
> >  
> > -           @param block when printing newline, start at column col
> > (True) or at column 0 (False)
> > -           @type boolean
> > +           :param block: when printing newline, start at column
> > col (True) or at column 0 (False)
> > +           :type block: boolean
> >             """
> >  
> > -
> > +        # if the starting row is not present, start at the cursor
> > position
> >          if row is None:
> >              row = self._cursor[0]
> >  
> > +        # if the starting column is not present, start at the
> > cursor position
> >          if col is None:
> >              col = self._cursor[1]
> >  
> > -        # fill up rows
> > +        # fill up rows to accomodate for w.height
> >          if self.height < row + w.height:
> >              for i in range(row + w.height - self.height):
> >                  self._buffer.append(list())
> >  
> > -        # append columns
> > +        # append columns to accomodate for w.width
> >          for l in range(row, row + w.height):
> >              l_len = len(self._buffer[l])
> >              w_len = len(w.content[l - row])
> > @@ -314,6 +503,7 @@ class Widget(object):
> >                  self._buffer[l] += ((col + w_len - l_len) *
> >                  list(u" "))
> >              self._buffer[l][col:col + w_len] = w.content[l -
> >              row][:]
> >  
> > +        # move the cursor to new spot
> >          if block:
> >              self._cursor = (row + w.height, col)
> >          else:
> > @@ -322,20 +512,20 @@ class Widget(object):
> >      def write(self, text, row = None, col = None, width = None,
> >      block = False):
> >          """This method emulates typing machine writing to this
> >          widget's buffer.
> >  
> > -           @param text text to type
> > -           @type text unicode
> > +           :param text: text to type
> > +           :type text: unicode
> >  
> > -           @param row row number to start at (default is at the
> > cursor position)
> > -           @type row int
> > +           :param row: row number to start at (default is at the
> > cursor position)
> > +           :type row: int
> >  
> > -           @param col column number to start at (default is at the
> > cursor position)
> > -           @type col int
> > +           :param col: column number to start at (default is at
> > the cursor position)
> > +           :type col: int
> >  
> > -           @param width wrap at "col" + "width" column (default is
> > at self._max_width)
> > -           @type width int
> > +           :param width: wrap at "col" + "width" column (default
> > is at self._max_width)
> > +           :type width: int
> >  
> > -           @param block when printing newline, start at column col
> > (True) or at column 0 (False)
> > -           @type boolean
> > +           :param block: when printing newline, start at column
> > col (True) or at column 0 (False)
> > +           :type block: boolean
> >             """
> >  
> >          if row is None:
> > @@ -384,12 +574,12 @@ class Widget(object):
> >  
> >          self._cursor = (x, y)
> >  
> > -class HelloWorld(UIScreen):
> > -    def show(self, args = None):
> > -        print """Hello World\nquit by typing 'quit'"""
> > -        return True
> > -
> >  if __name__ == "__main__":
> > +    class HelloWorld(UIScreen):
> > +        def show(self, args = None):
> > +            print """Hello World\nquit by typing 'quit'"""
> > +            return True
> > +
> >      a = App("Hello World")
> >      s = HelloWorld(a, None)
> >      a.schedule_screen(s)
> > diff --git a/pyanaconda/ui/tui/simpleline/widgets.py
> > b/pyanaconda/ui/tui/simpleline/widgets.py
> > index 8dde043..d6ef5c6 100644
> > --- a/pyanaconda/ui/tui/simpleline/widgets.py
> > +++ b/pyanaconda/ui/tui/simpleline/widgets.py
> > @@ -26,20 +26,49 @@ __all__ = ["TextWidget", "ColumnWidget",
> > "CheckboxWidget", "CenterWidget"]
> >  import base
> >  
> >  class TextWidget(base.Widget):
> > +    """Class to handle wrapped text output."""
> > +
> >      def __init__(self, text):
> > +        """
> > +        :param text: text to format
> > +        :type text: unicode
> > +        """
> > +
> >          base.Widget.__init__(self)
> >          self._text = text
> >  
> >      def render(self, width):
> > +        """Renders the text widget limited to width number of
> > columns
> > +        (wraps to the next line when the text is longer).
> > +
> > +        :param width: maximum width allocated to the string
> > +        :type width: int
> > +
> > +        :raises
> > +        """
> > +
> >          base.Widget.render(self, width)
> >          self.write(self._text, width = width)
> >  
> >  class CenterWidget(base.Widget):
> > +    """Class to handle horizontal centering of content."""
> > +
> >      def __init__(self, w):
> > +        """
> > +        :param w: widget to center
> > +        :type w: base.Widget
> > +        """
> >          base.Widget.__init__(self)
> >          self._w = w
> >  
> >      def render(self, width):
> > +        """
> > +        Render the centered widget to internal buffer.
> > +
> > +        :param width: maximum width the widget should use
> > +        :type width: int
> > +        """
> > +
> >          base.Widget.render(self, width)
> >          self._w.render(width)
> >          self.draw(self._w, col = (width - self._w.width) / 2)
> > @@ -48,11 +77,11 @@ class ColumnWidget(base.Widget):
> >      def __init__(self, columns, spacing = 0):
> >          """Create text columns
> >  
> > -           @param columns list containing (column width, [list of
> > widgets to put into this column])
> > -           @type columns [(int, [...]), ...]
> > +           :param columns: list containing (column width, [list of
> > widgets to put into this column])
> > +           :type columns: [(int, [...]), ...]
> >  
> > -           @param spacing number of spaces to use between columns
> > -           @type int
> > +           :param spacing: number of spaces to use between columns
> > +           :type spacing: int
> >             """
> >  
> >          base.Widget.__init__(self)
> > @@ -60,26 +89,60 @@ class ColumnWidget(base.Widget):
> >          self._columns = columns
> >  
> >      def render(self, width):
> > +        """Render the widget to it's internal buffer
> > +
> > +        :param width: the maximum width the widget can use
> > +        :type width: int
> > +
> > +        :return: nothing
> > +        :rtype: None
> > +        """
> > +
> >          base.Widget.render(self, width)
> >  
> > +        # the lefmost empty column
> >          x = 0
> > +
> > +        # iterate over tuples (column width, column content)
> >          for col_width,col in self._columns:
> > +
> > +            # set cursor to first line and leftmost empty column
> >              self.setxy(0, x)
> >  
> > +            # if requested width is None, limit the maximum to
> > width
> > +            # and set minimum to 0
> >              if col_width is None:
> >                  col_max_width = width - self.cursor[1]
> >                  col_width = 0
> >              else:
> >                  col_max_width = col_width
> >  
> > +            # render and draw contents of column
> >              for item in col:
> >                  item.render(col_max_width)
> >                  self.draw(item, block = True)
> >  
> > +            # recompute the leftmost empty column
> >              x = max((x + col_width), self.width) + self._spacing
> >  
> >  class CheckboxWidget(base.Widget):
> > -    def __init__(self, key = None, title = None, text = None,
> > completed = None):
> > +    """Widget to show checkbox with (un)checked box, name and
> > description."""
> > +
> > +    def __init__(self, key = "x", title = None, text = None,
> > completed = None):
> > +        """
> > +        :param key: tick character to be used inside [ ]
> > +        :type key: character
> > +
> > +        :param title: the title next to the [ ] box
> > +        :type title: unicode
> > +
> > +        :param text: the description text to be shown on the
> > second row in ()
> > +        :type text: unicode
> > +
> > +        :param completed: is the checkbox ticked or not?
> > +        :type completed: True|False
> > +        """
> > +
> >          base.Widget.__init__(self)
> >          self._key = key
> >          self._title = title
> > @@ -87,37 +150,49 @@ class CheckboxWidget(base.Widget):
> >          self._completed = completed
> >  
> >      def render(self, width):
> > +        """Render the widget to internal buffer. It should be max
> > width
> > +           characters wide."""
> >          base.Widget.render(self, width)
> >  
> >          if self.completed:
> > -            checkchar = "x"
> > +            checkchar = self._key
> >          else:
> >              checkchar = " "
> >  
> > +        # prepare the checkbox
> >          checkbox = TextWidget("[%s]" % checkchar)
> >  
> >          data = []
> >  
> > +        # append lines
> >          if self.title:
> >              data.append(TextWidget(self.title))
> >  
> >          if self.text:
> >              data.append(TextWidget("(%s)" % self.text))
> >  
> > +        # the checkbox has two columns
> > +        # [x] is one and is 3 chars wide
> > +        # text is second and can occupy width - 3 - 1 (for space)
> > chars
> >          cols = ColumnWidget([(3, [checkbox]), (width - 4, data)],
> >          1)
> >          cols.render(width)
> > +
> > +        # transfer the column widget rendered stuff to internal
> > buffer
> >          self.draw(cols)
> >  
> >      @property
> >      def title(self):
> > +        """Returns the first line (main title) of the checkbox."""
> >          return self._title
> >  
> >      @property
> >      def completed(self):
> > +        """Returns the state of the checkbox, checked is True."""
> >          return self._completed
> >  
> >      @property
> >      def text(self):
> > +        """Contains the description text from the second line."""
> >          return self._text
> >  
> >  if __name__ == "__main__":
> 
> --
> Vratislav Podzimek
> 
> Anaconda Rider | Red Hat, Inc. | Brno - Czech Republic
> 
> _______________________________________________
> anaconda-patches mailing list
> anaconda-patches at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches
> 


More information about the anaconda-patches mailing list