Exemplo n.º 1
0
    def render_view(self, render_prologue=True):
        """Returns a GroupView"""
        log = LoggerAdapter(logger, {'name_ext': 'GroupModel.render_view'})
        log.debug("Entering")

        #- get number of digits in the number of object models
        max_line_num_strlen = len(str(len(self.object_models)))
        log.debug("max_line_num_strlen: {}".format(max_line_num_strlen))
        next_color = None

        #- keep track of all the lengths of the attributes
        #- across objects (so we can line them all up when we render a view)
        attr_maxlens = dict()

        for n, object_model_x in enumerate(self.object_models):
            #- override object model colors with group colors, if set
            next_color = self.get_next_color()
            if next_color:
                log.debug(
                    "setting object model color to next_color: {}".format(
                        next_color))
                object_model_x.set_colors([next_color], match_delimiter=True)

            if render_prologue:
                #- add the group sequence index as leading output of the object view
                index_str = '{current_index: <{max_line_num_strlen}}: '.format(\
                    current_index=str(n), max_line_num_strlen=max_line_num_strlen)

                log.debug("index_str: '{}'".format(index_str))
                #- match proglogue color to object color
                prologue = ColoredText(index_str, next_color)
                object_model_x.prologue = prologue

            if self.align:
                #- code to line up the attribute lengths across objects
                for attribute_model_x in object_model_x.attribute_models:
                    attr_name = attribute_model_x.name
                    attr_width = attribute_model_x.get_width()
                    log.debug(
                        f"attr_name:'{attr_name}' | attr_width: {attr_width}")
                    try:
                        attr_maxlens[attr_name] = max(attr_maxlens[attr_name],
                                                      attr_width)

                    except KeyError:
                        attr_maxlens[attr_name] = attr_width

        object_views = list()
        for object_model_x in self.object_models:
            if self.align:
                #- dynamically align attribute lengths
                #- TODO: if each attribute has a defined length, skip this
                for attribute_model_x in object_model_x.attribute_models:
                    attribute_model_x.length = attr_maxlens[
                        attribute_model_x.name]
            object_views.append(object_model_x.render_view())

        groupView = GroupView(object_views=object_views)
        log.debug("Returning groupView: {}".format(groupView))
        return groupView
Exemplo n.º 2
0
    def __init__(self, text, prologue=None, epilogue=None, color=None):
        """
        text: the text that is the rendered attribute
        prologue: stuff you want to prepend to the rendered attribute
        epilogue: stuff you want to append to the rendered attribute
        """
        self.text = text
        self.render_method = print
        self.color = color

        if prologue is None:
            prologue = ''
        self.prologue = ColoredText(prologue)

        if epilogue is None:
            epilogue = ''
        self.epilogue = ColoredText(epilogue)
Exemplo n.º 3
0
 def get_render_output(self):
     outputs = list()
     if self.color:
         outputs.append(self.prologue.render())
         outputs.append(ColoredText(self.text, self.color).render())
         outputs.append(self.epilogue.render())
     else:
         outputs.append(self.prologue.plain())
         outputs.append(str(self.text))
         outputs.append(self.epilogue.plain())
     return ''.join(outputs)
Exemplo n.º 4
0
    def __init__(self, attribute_views, delimiters, prologue=None):
        """
        Input:
            attribute_views: list of AttributeView objects
            delimiter: ColoredText instance string used to separate AttributeViews when rendering
        """
        self.attribute_views = attribute_views

        self.delimiters = [None] * len(delimiters)
        if delimiters:
            for i, delim in enumerate(delimiters[:]):
                if isinstance(delim, str):
                    delimiters[i] = ColoredText(delim, color=None)

        self.delimiters = cycle(delimiters) if delimiters else repeat(
            ColoredText('', color=None))

        self.prologue = prologue if prologue else ColoredText('')
        self.render_method = print

        self.term_size = shutil.get_terminal_size()
Exemplo n.º 5
0
 def get_render_data(self):
     """
     Description:
         return self as a collection of ColoredText instances
     """
     log = LoggerAdapter(logger, {'name_ext' : '{}.get_render_data'.format(\
             self.__class__.__name__)})
     outputs = list()
     outputs.append(self.prologue)
     outputs.append(ColoredText(self.text, self.color))
     outputs.append(self.epilogue)
     ard = AttributeRenderDatum(*outputs)
     log.debug("returning: {}".format(ard))
     return ard
Exemplo n.º 6
0
    def render_object_from_spec(self,
                                obj,
                                spec,
                                colors='_follow_object_spec_',
                                align=True):
        """
        Description:
            render an object by using an ObjectModelSpec to get the rendering specs
        Input:
            obj: the object to be rendered
            spec: the ObjectModelSpec to use
        Notes:
            Creates a GroupModel and renders that
            All the other render_object* methods end up calling this one
        """
        log = LoggerAdapter(logger, {'name_ext':'{}.render_object_from_spec'.format(\
                self.__class__.__name__)})
        log.debug("Entering: obj: {} | spec: {} | colors: {} | align: {}".format(\
                obj, spec, colors, align))
        #- go through and first create the ObjectModels
        obj_models = list()
        if self.is_render_iterable(obj):
            for i, obj_x in enumerate(obj):
                obj_models.append(
                    ObjectModel(obj_x, spec, colors=next(spec.colors)))
        else:
            prologue = ColoredText('')
            obj_models = [ObjectModel(obj, spec, colors=next(spec.colors))]

        if colors == '_follow_object_spec_':
            colors = copy.copy(spec.colors)

        #- collect the ObjectModels in a GroupModel
        group_model = GroupModel(object_models=obj_models,
                                 colors=colors,
                                 align=align)
        #-TODO: wrap GroupModel in CollectionModel here

        return group_model.render()
Exemplo n.º 7
0
    def render_view(self):
        """Return an ObjectView"""
        log = LoggerAdapter(logger, {'name_ext' : 'ObjectModel.render_view'})
        attribute_views = list()
        delimiters = list()
        for n,attribute_model_x in enumerate(self.attribute_models):
            if self.colors:
                #- override AttributeModel color with ObjectModel color
                color = self.get_next_color()
                attribute_model_x.set_color(color)
                log.debug("set AttributeModel color: {}: {}".format(color,\
                    attribute_model_x))

            attribute_views.append(attribute_model_x.render_view())

        num_delims = 0
        delimiters = list()

        for attr in range(len(self.attribute_models) - 1):
            delimiters.append(ColoredText(self.delimiter, next(self.delimiter_colors)))

        log.debug("Created {} delimiters".format(len(delimiters)))
        return ObjectView(attribute_views=attribute_views,\
            delimiters=delimiters, prologue=self.prologue)
Exemplo n.º 8
0
class AttributeView(ViewABC):
    """
    Description:
        Attribute Views are the lowest-level views.  They are nothing more than text that
        represents the value of an individual object's singular attribute wrapped in a
        ColoredText object.
    """
    def __init__(self, text, prologue=None, epilogue=None, color=None):
        """
        text: the text that is the rendered attribute
        prologue: stuff you want to prepend to the rendered attribute
        epilogue: stuff you want to append to the rendered attribute
        """
        self.text = text
        self.render_method = print
        self.color = color

        if prologue is None:
            prologue = ''
        self.prologue = ColoredText(prologue)

        if epilogue is None:
            epilogue = ''
        self.epilogue = ColoredText(epilogue)

    def get_render_output(self):
        outputs = list()
        if self.color:
            outputs.append(self.prologue.render())
            outputs.append(ColoredText(self.text, self.color).render())
            outputs.append(self.epilogue.render())
        else:
            outputs.append(self.prologue.plain())
            outputs.append(str(self.text))
            outputs.append(self.epilogue.plain())
        return ''.join(outputs)

    def get_render_data(self):
        """
        Description:
            return self as a collection of ColoredText instances
        """
        log = LoggerAdapter(logger, {'name_ext' : '{}.get_render_data'.format(\
                self.__class__.__name__)})
        outputs = list()
        outputs.append(self.prologue)
        outputs.append(ColoredText(self.text, self.color))
        outputs.append(self.epilogue)
        ard = AttributeRenderDatum(*outputs)
        log.debug("returning: {}".format(ard))
        return ard

    def get_width(self):
        """
        Description:
            text can be a newline-seperated stanza or a single string.
            the width of this attribute view is the length of the longest line in the text
        """
        lines = self.text.split('\n')
        if len(lines) > 1:
            return len(max(*lines, key=len))
        else:
            return len(lines[0])

    def render(self):
        self.render_method(self.get_render_output())

    def __repr__(self):
        outputs = list()
        outputs.append(self.__class__.__name__ + '(')
        outputs.append('text={}, '.format(self.text))
        outputs.append('color={}, '.format(self.color))
        outputs.append('prologue={}, '.format(self.prologue))
        outputs.append('epilogue={}, '.format(self.epilogue))
        return ''.join(outputs)
Exemplo n.º 9
0
 def test_init(self):
     ct = ColoredText(text='Hello World!', color='bright white on green')
     self.assertEqual(ct.to_str(), '\x1b[37m\x1b[42m\x1b[1mHello World!\x1b[0m')
Exemplo n.º 10
0
    def get_render_output(self, limit_to_screen=True):
        """
        Description:
            Get the output that will be passed to the render method. (the text that will
            be printed to a terminal)
        Input:
            limit_to_screen: boolean controlling whether or not we cut off the line at the
            line-length of the terminal
        """
        log = LoggerAdapter(logger,
                            {'name_ext': 'ObjectView.get_render_output'})

        #- figure out the width of each attribute view
        per_attr_widths = list()
        per_attr_colors = list()

        for n, attr_view in enumerate(self.attribute_views):
            attr_text = attr_view.get_render_data().attribute.plain()
            view_lines = attr_text.split('\n')
            if len(view_lines) <= 1:
                log.debug("single line attribute view")
                if n == 0:
                    log.debug(
                        f"adding prologue length to attr_width: {attr_text}")
                    attr_width = len(attr_text) + len(self.prologue.plain())
                else:
                    log.debug(
                        f"not adding prologue length to attr_width: {attr_text}"
                    )
                    attr_width = len(attr_text)
            else:
                log.debug("multi-line attribute view: ")
                log.debug("{}".format(view_lines))
                longest_line = max(*view_lines, key=len)
                attr_width = len(longest_line) + len(self.prologue.plain())
            log.debug("attr_width : {}".format(attr_width))
            per_attr_widths.append(attr_width)
            per_attr_colors.append(attr_view.color)
        log.debug("per_attr_widths: {}".format(per_attr_widths))

        #- build a list of lines to render for each attribute view
        #- one is just the text, so the length of the line is the
        #- length of the visible characters
        #- then we have the lines per attribute that also contain color code
        #- the text lines we use to determine the display length
        #- but the manipulation of the attribute view must
        #- be done on the lines to actually be rendered
        per_attr_text_lines = [[]] * len(self.attribute_views)
        per_attr_lines = [[]] * len(self.attribute_views)

        for n, attr_view in enumerate(self.attribute_views):
            attr_rd = attr_view.get_render_data()
            text_lines = attr_rd.attribute.plain().split('\n')
            #- first line has prologue
            if n == 0 and self.prologue:
                text_lines[0] = self.prologue.plain() + text_lines[0]

            per_attr_text_lines[n] = text_lines
            per_attr_lines[n] = [
                ColoredText(txt, attr_view.color) for txt in text_lines
            ]

        #- figure out how many lines this object will take up
        if per_attr_text_lines:
            object_view_lines = len(max(*per_attr_text_lines, key=len))
        else:
            #- TODO: this assumes that we are always rendering attributes
            #- for builtins, that won't work
            object_view_lines = 0
            log.debug(f"no attributes to render")
        log.debug("{} lines in this object view".format(object_view_lines))

        #- combine the lists of attribute lines into a single list of object lines
        render_text_lines = list()
        render_lines = list()

        for line_num in range(object_view_lines):
            text_line = ''
            render_line = list()

            for m, attr_text_lines in enumerate(per_attr_text_lines):
                if len(attr_text_lines) > line_num:
                    log.debug(f"m: {m} | line_num: {line_num}")
                    log.debug(f"per_attr_widths[m]: {per_attr_widths[m]}")
                    log.debug(("len(attr_text_lines[line_num]):"
                               f"{len(attr_text_lines[line_num])}"))

                    pad_len = per_attr_widths[m] - len(
                        attr_text_lines[line_num])

                    log.debug(f"initial pad_len: {pad_len}")

                    text_line += attr_text_lines[line_num]
                    text_line += ' ' * pad_len
                    cur_render_line = per_attr_lines[m][line_num]

                    log.debug("cur_text_line initial:")
                    log.debug(f"'{attr_text_lines[line_num]}'")
                    log.debug("cur_render_line initial: ")
                    log.debug(f"'{cur_render_line}'")

                    cur_render_line.text += ' ' * pad_len

                    log.debug("cur_render_line plus padding: ")
                    log.debug(f"'{cur_render_line}'")

                    render_line.append(cur_render_line)

                    if m < len(per_attr_text_lines) - 1:
                        delim = next(self.delimiters)
                        text_line += delim.plain()
                        render_line.append(delim)
                        log.debug("render_line plus pad and delim:")
                        log.debug("{}".format(render_line))
                else:
                    log.debug("adding padded stanza line")
                    text_line += ' ' * per_attr_widths[m]
                    log.debug("padded text line: ")
                    log.debug(f"'{text_line}'")
                    #- all attribute lines should have the same color for now
                    color = per_attr_lines[m][0].color
                    render_line.append(
                        ColoredText(' ' * per_attr_widths[m], color))
                    log.debug("padded render line: ")
                    log.debug("'{render_line}'")

                    if m < len(per_attr_text_lines) - 1:
                        delim = next(self.delimiters)
                        text_line += delim.plain()
                        render_line.append(delim)
                        log.debug("padded render_line plus delim:")
                        log.debug(f"{render_line}")

            render_text_lines.append(text_line)
            log.debug('rendered render_line:')
            log.debug(''.join([x.render() for x in render_line]))
            render_lines.append(render_line)

        if limit_to_screen:
            #- trim plaintext to fit screen width
            log.debug("screen size: {}".format(self.term_size.columns))
            for n, line in enumerate(render_text_lines[:]):
                if len(line) > self.term_size.columns:
                    length = len(line)
                    log.debug(
                        "trimming line #{} to fit screen (len={})".format(
                            n, length))
                    render_text_lines[n] = line[0:self.term_size.columns]

            #- trim colored text to fit screen width
            for n, render_line in enumerate(render_lines[:]):
                running_text_width = 0
                for m, item in enumerate(render_line[:]):
                    current_text_width = running_text_width
                    log.debug(
                        "current text width: {}".format(current_text_width))
                    running_text_width += len(item.plain())
                    if running_text_width > self.term_size.columns:
                        log.debug("detected overrun at {}-->{}".format(\
                            running_text_width, current_text_width))
                        log.debug("overrun item: {}".format(item))
                        extra = running_text_width - self.term_size.columns
                        item.text = item.text[0:-extra]
                        log.debug("trimmed item: {}".format(item))
                        render_line[m] = item
                        render_lines[n] = render_line

        render_output = ''
        for n, render_line in enumerate(render_lines):
            for item in render_line:
                render_output += item.render()
            if n < len(render_lines) - 1:
                render_output += '\n'

        return render_output