Ejemplo n.º 1
0
def get_font(section='appearance', option='font', font_size_delta=0):
    """Get console font properties depending on OS and user options"""
    font = FONT_CACHE.get((section, option))

    if font is None:
        families = CONF.get(section, option+"/family", None)

        if families is None:
            return QFont()

        family = get_family(families)
        weight = QFont.Normal
        italic = CONF.get(section, option+'/italic', False)

        if CONF.get(section, option+'/bold', False):
            weight = QFont.Bold

        size = CONF.get(section, option+'/size', 9) + font_size_delta
        font = QFont(family, size, weight)
        font.setItalic(italic)
        FONT_CACHE[(section, option)] = font

    size = CONF.get(section, option+'/size', 9) + font_size_delta
    font.setPointSize(size)
    return font
Ejemplo n.º 2
0
Archivo: gui.py Proyecto: cfanpc/spyder
def get_font(section='appearance', option='font', font_size_delta=0):
    """Get console font properties depending on OS and user options"""
    font = FONT_CACHE.get((section, option))

    if font is None:
        families = CONF.get(section, option+"/family", None)

        if families is None:
            return QFont()

        family = get_family(families)
        weight = QFont.Normal
        italic = CONF.get(section, option+'/italic', False)

        if CONF.get(section, option+'/bold', False):
            weight = QFont.Bold

        size = CONF.get(section, option+'/size', 9) + font_size_delta
        font = QFont(family, size, weight)
        font.setItalic(italic)
        FONT_CACHE[(section, option)] = font

    size = CONF.get(section, option+'/size', 9) + font_size_delta
    font.setPointSize(size)
    return font
Ejemplo n.º 3
0
def tuple_to_qfont(tup):
    """
    Create a QFont from tuple:
        (family [string], size [int], italic [bool], bold [bool])
    """
    if not isinstance(tup, tuple) or len(tup) != 4 \
       or not font_is_installed(tup[0]) \
       or not isinstance(tup[1], int) \
       or not isinstance(tup[2], bool) \
       or not isinstance(tup[3], bool):
        return None
    font = QFont()
    family, size, italic, bold = tup
    font.setFamily(family)
    font.setPointSize(size)
    font.setItalic(italic)
    font.setBold(bold)
    return font
Ejemplo n.º 4
0
def tuple_to_qfont(tup):
    """
    Create a QFont from tuple:
        (family [string], size [int], italic [bool], bold [bool])
    """
    if not isinstance(tup, tuple) or len(tup) != 4 \
       or not font_is_installed(tup[0]) \
       or not isinstance(tup[1], int) \
       or not isinstance(tup[2], bool) \
       or not isinstance(tup[3], bool):
        return None
    font = QFont()
    family, size, italic, bold = tup
    font.setFamily(family)
    font.setPointSize(size)
    font.setItalic(italic)
    font.setBold(bold)
    return font
Ejemplo n.º 5
0
    def render__label_qfont(self) -> QFont:
        qfont = QFont()
        qfont.setStyleHint(QFont.SansSerif)  # no-op on X11

        font = self.cfg.render.label_font
        if font.toString:
            qfont.fromString(font.toString)
            return qfont

        # Passing None or "" to QFont(family) results in qfont.family() = "", and
        # wrong font being selected (Abyssinica SIL, which appears early in the list).
        family = coalesce(font.family, qfont.defaultFamily())
        # Font file selection
        qfont.setFamily(family)
        qfont.setBold(font.bold)
        qfont.setItalic(font.italic)
        # Font size
        qfont.setPointSizeF(font.size)
        return qfont
Ejemplo n.º 6
0
 def get_preview_items(self, level):
     previewItem = QTableWidgetItem("Log message")
     previewItem.setBackground(QBrush(level.bg, Qt.SolidPattern))
     previewItem.setForeground(QBrush(level.fg, Qt.SolidPattern))
     previewItemDark = QTableWidgetItem("Log message")
     previewItemDark.setBackground(QBrush(level.bgDark, Qt.SolidPattern))
     previewItemDark.setForeground(QBrush(level.fgDark, Qt.SolidPattern))
     font = QFont(CONFIG.logger_table_font, CONFIG.logger_table_font_size)
     fontDark = QFont(font)
     if 'bold' in level.styles:
         font.setBold(True)
     if 'italic' in level.styles:
         font.setItalic(True)
     if 'underline' in level.styles:
         font.setUnderline(True)
     if 'bold' in level.stylesDark:
         fontDark.setBold(True)
     if 'italic' in level.stylesDark:
         fontDark.setItalic(True)
     if 'underline' in level.stylesDark:
         fontDark.setUnderline(True)
     previewItem.setFont(font)
     previewItemDark.setFont(fontDark)
     return previewItem, previewItemDark
Ejemplo n.º 7
0
class TreeModel(QAbstractItemModel):
    """
    Model that provides an interface to an objectree
    that is build of TreeItems.
    """
    def __init__(self, obj, obj_name='', attr_cols=None, parent=None):
        """
        Constructor

        :param obj: any Python object or variable
        :param obj_name: name of the object as it will appear in the root node
                         If empty, no root node will be drawn.
        :param attr_cols: list of AttributeColumn definitions
        :param parent: the parent widget
        """
        super(TreeModel, self).__init__(parent)
        self._attr_cols = attr_cols

        self.regular_font = QFont()  # Font for members (non-functions)
        # Font for __special_attributes__
        self.special_attribute_font = QFont()
        self.special_attribute_font.setItalic(False)

        self.regular_color = QBrush(QColor(ima.MAIN_FG_COLOR))
        self.callable_color = QBrush(QColor(
            ima.MAIN_FG_COLOR))  # for functions, methods, etc.

        # The following members will be initialized by populateTree
        # The rootItem is always invisible. If the obj_name
        # is the empty string, the inspectedItem
        # will be the rootItem (and therefore be invisible).
        # If the obj_name is given, an
        # invisible root item will be added and the
        # inspectedItem will be its only child.
        # In that case the inspected item will be visible.
        self._inspected_node_is_visible = None
        self._inspected_item = None
        self._root_item = None
        self.populateTree(obj, obj_name=obj_name)

    @property
    def inspectedNodeIsVisible(self):
        """
        Returns True if the inspected node is visible.
        In that case an invisible root node has been added.
        """
        return self._inspected_node_is_visible

    @property
    def rootItem(self):
        """ The root TreeItem.
        """
        return self._root_item

    @property
    def inspectedItem(self):
        """The TreeItem that contains the item under inspection."""
        return self._inspected_item

    def rootIndex(self):  # TODO: needed?
        """
        The index that returns the root element
        (same as an invalid index).
        """
        return QModelIndex()

    def inspectedIndex(self):
        """The model index that point to the inspectedItem."""
        if self.inspectedNodeIsVisible:
            return self.createIndex(0, 0, self._inspected_item)
        else:
            return self.rootIndex()

    def columnCount(self, _parent=None):
        """ Returns the number of columns in the tree """
        return len(self._attr_cols)

    def data(self, index, role):
        """Returns the tree item at the given index and role."""
        if not index.isValid():
            return None

        col = index.column()
        tree_item = index.internalPointer()
        obj = tree_item.obj

        if role == Qt.DisplayRole:
            try:
                attr = self._attr_cols[col].data_fn(tree_item)
                # Replace carriage returns and line feeds with unicode glyphs
                # so that all table rows fit on one line.
                # return attr.replace('\n',
                #                     unichr(0x240A)).replace('\r',
                #                     unichr(0x240D))
                return (attr.replace('\r\n', to_unichr(0x21B5)).replace(
                    '\n', to_unichr(0x21B5)).replace('\r', to_unichr(0x21B5)))
            except Exception as ex:
                # logger.exception(ex)
                return "**ERROR**: {}".format(ex)

        elif role == Qt.TextAlignmentRole:
            return self._attr_cols[col].alignment

        elif role == Qt.ForegroundRole:
            if tree_item.is_callable:
                return self.callable_color
            else:
                return self.regular_color

        elif role == Qt.FontRole:
            if tree_item.is_attribute:
                return self.special_attribute_font
            else:
                return self.regular_font
        elif role == Qt.EditRole:
            return obj
        else:
            return None

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags

        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._attr_cols[section].name
        else:
            return None

    def treeItem(self, index):
        if not index.isValid():
            return self.rootItem
        else:
            return index.internalPointer()

    def index(self, row, column, parent=None):

        if parent is None:
            logger.debug("parent is None")
            parent = QModelIndex()

        parentItem = self.treeItem(parent)

        if not self.hasIndex(row, column, parent):
            logger.debug("hasIndex "
                         "is False: ({}, {}) {!r}".format(
                             row, column, parentItem))
            # logger.warn("Parent index model"
            #             ": {!r} != {!r}".format(parent.model(), self))

            return QModelIndex()

        childItem = parentItem.child(row)
        # logger.debug("  {}".format(childItem.obj_path))
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            logger.warn("no childItem")
            return QModelIndex()

    def parent(self, index):
        if not index.isValid():
            return QModelIndex()

        child_item = index.internalPointer()
        parent_item = child_item.parent()

        if parent_item is None or parent_item == self.rootItem:
            return QModelIndex()

        return self.createIndex(parent_item.row(), 0, parent_item)

    def rowCount(self, parent=None):
        parent = QModelIndex() if parent is None else parent

        if parent.column() > 0:
            # This is taken from the PyQt simpletreemodel example.
            return 0
        else:
            return self.treeItem(parent).child_count()

    def hasChildren(self, parent=None):
        parent = QModelIndex() if parent is None else parent
        if parent.column() > 0:
            return 0
        else:
            return self.treeItem(parent).has_children

    def canFetchMore(self, parent=None):
        parent = QModelIndex() if parent is None else parent
        if parent.column() > 0:
            return 0
        else:
            result = not self.treeItem(parent).children_fetched
            # logger.debug("canFetchMore: {} = {}".format(parent, result))
            return result

    def fetchMore(self, parent=None):
        """
        Fetches the children given the model index of a parent node.
        Adds the children to the parent.
        """
        parent = QModelIndex() if parent is None else parent
        if parent.column() > 0:
            return

        parent_item = self.treeItem(parent)
        if parent_item.children_fetched:
            return

        tree_items = self._fetchObjectChildren(parent_item.obj,
                                               parent_item.obj_path)

        self.beginInsertRows(parent, 0, len(tree_items) - 1)
        for tree_item in tree_items:
            parent_item.append_child(tree_item)

        parent_item.children_fetched = True
        self.endInsertRows()

    def _fetchObjectChildren(self, obj, obj_path):
        """
        Fetches the children of a Python object.
        Returns: list of TreeItems
        """
        obj_children = []
        path_strings = []

        if isinstance(obj, (list, tuple)):
            obj_children = sorted(enumerate(obj))
            path_strings = [
                '{}[{}]'.format(obj_path, item[0]) if obj_path else item[0]
                for item in obj_children
            ]
        elif isinstance(obj, (set, frozenset)):
            obj_children = [('pop()', elem) for elem in obj]
            path_strings = [
                '{0}.pop()'.format(obj_path, item[0]) if obj_path else item[0]
                for item in obj_children
            ]
        elif hasattr(obj, 'items'):  # dictionaries and the likes.
            try:
                obj_children = list(obj.items())
            except Exception as ex:
                # Can happen if the items method expects an argument,
                # for instance the types.DictType.items
                # method expects a dictionary.
                logger.warn("No items expanded. "
                            "Objects items() call failed: {}".format(ex))
                obj_children = []

            # Sort keys, except when the object is an OrderedDict.
            if not isinstance(obj, OrderedDict):
                try:
                    obj_children = sorted(obj.items())
                except Exception as ex:
                    logger.debug("Unable to sort "
                                 "dictionary keys: {}".format(ex))

            path_strings = [
                '{}[{!r}]'.format(obj_path, item[0]) if obj_path else item[0]
                for item in obj_children
            ]

        assert len(obj_children) == len(path_strings), "sanity check"
        is_attr_list = [False] * len(obj_children)

        # Object attributes
        for attr_name, attr_value in sorted(inspect.getmembers(obj)):
            obj_children.append((attr_name, attr_value))
            path_strings.append(
                '{}.{}'.format(obj_path, attr_name) if obj_path else attr_name)
            is_attr_list.append(True)

        assert len(obj_children) == len(path_strings), "sanity check"
        tree_items = []
        for item, path_str, is_attr in zip(obj_children, path_strings,
                                           is_attr_list):
            name, child_obj = item
            tree_items.append(TreeItem(child_obj, name, path_str, is_attr))

        return tree_items

    def populateTree(self, obj, obj_name='', inspected_node_is_visible=None):
        """Fills the tree using a python object. Sets the rootItem."""
        logger.debug("populateTree with object id = 0x{:x}".format(id(obj)))
        if inspected_node_is_visible is None:
            inspected_node_is_visible = (obj_name != '')
        self._inspected_node_is_visible = inspected_node_is_visible

        if self._inspected_node_is_visible:
            self._root_item = TreeItem(None, _('<invisible_root>'),
                                       _('<invisible_root>'), None)
            self._root_item.children_fetched = True
            self._inspected_item = TreeItem(obj,
                                            obj_name,
                                            obj_name,
                                            is_attribute=None)
            self._root_item.append_child(self._inspected_item)
        else:
            # The root itself will be invisible
            self._root_item = TreeItem(obj,
                                       obj_name,
                                       obj_name,
                                       is_attribute=None)
            self._inspected_item = self._root_item

            # Fetch all items of the root so we can
            # select the first row in the constructor.
            root_index = self.index(0, 0)
            self.fetchMore(root_index)

    def _auxRefreshTree(self, tree_index):
        """
        Auxiliary function for refreshTree that recursively refreshes the
        tree nodes.

        If the underlying Python object has been changed, we don't want to
        delete the old tree model and create a new one from scratch because
        this loses all information about which nodes are fetched and expanded.
        Instead the old tree model is updated. Using the difflib from the
        standard library it is determined for a parent node which child nodes
        should be added or removed. This is done based on the node names only,
        not on the node contents (the underlying Python objects). Testing the
        underlying nodes for equality is potentially slow. It is faster to
        let the refreshNode function emit the dataChanged signal for all cells.
        """
        tree_item = self.treeItem(tree_index)
        logger.debug("_auxRefreshTree({}): {}{}".format(
            tree_index, tree_item.obj_path,
            "*" if tree_item.children_fetched else ""))

        if tree_item.children_fetched:

            old_items = tree_item.child_items
            new_items = self._fetchObjectChildren(tree_item.obj,
                                                  tree_item.obj_path)

            old_item_names = [(item.obj_name, item.is_attribute)
                              for item in old_items]
            new_item_names = [(item.obj_name, item.is_attribute)
                              for item in new_items]
            seqMatcher = SequenceMatcher(isjunk=None,
                                         a=old_item_names,
                                         b=new_item_names,
                                         autojunk=False)
            opcodes = seqMatcher.get_opcodes()

            logger.debug("(reversed) "
                         "opcodes: {}".format(list(reversed(opcodes))))

            for tag, i1, i2, j1, j2 in reversed(opcodes):

                if 1 or tag != 'equal':
                    logger.debug(
                        "  {:7s}, a[{}:{}] ({}), b[{}:{}] ({})".format(
                            tag, i1, i2, old_item_names[i1:i2], j1, j2,
                            new_item_names[j1:j2]))

                if tag == 'equal':
                    # Only when node names are equal is _auxRefreshTree
                    # called recursively.
                    assert i2 - i1 == j2 - j1, ("equal sanity "
                                                "check failed "
                                                "{} != {}".format(
                                                    i2 - i1, j2 - j1))
                    for old_row, new_row in zip(range(i1, i2), range(j1, j2)):
                        old_items[old_row].obj = new_items[new_row].obj
                        child_index = self.index(old_row, 0, parent=tree_index)
                        self._auxRefreshTree(child_index)

                elif tag == 'replace':
                    # Explicitly remove the old item and insert the new.
                    # The old item may have child nodes which indices must be
                    # removed by Qt, otherwise it crashes.
                    assert i2 - i1 == j2 - j1, ("replace sanity "
                                                "check failed "
                                                "{} != {}").format(
                                                    i2 - i1, j2 - j1)

                    # row number of first removed
                    first = i1
                    # row number of last element after insertion
                    last = i1 + i2 - 1
                    logger.debug("     calling "
                                 "beginRemoveRows({}, {}, {})".format(
                                     tree_index, first, last))
                    self.beginRemoveRows(tree_index, first, last)
                    del tree_item.child_items[i1:i2]
                    self.endRemoveRows()

                    # row number of first element after insertion
                    first = i1
                    # row number of last element after insertion
                    last = i1 + j2 - j1 - 1
                    logger.debug("     calling "
                                 "beginInsertRows({}, {}, {})".format(
                                     tree_index, first, last))
                    self.beginInsertRows(tree_index, first, last)
                    tree_item.insert_children(i1, new_items[j1:j2])
                    self.endInsertRows()

                elif tag == 'delete':
                    assert j1 == j2, ("delete"
                                      " sanity check "
                                      "failed. {} != {}".format(j1, j2))
                    # row number of first that will be removed
                    first = i1
                    # row number of last element after insertion
                    last = i1 + i2 - 1
                    logger.debug("     calling "
                                 "beginRemoveRows"
                                 "({}, {}, {})".format(tree_index, first,
                                                       last))
                    self.beginRemoveRows(tree_index, first, last)
                    del tree_item.child_items[i1:i2]
                    self.endRemoveRows()

                elif tag == 'insert':
                    assert i1 == i2, ("insert "
                                      "sanity check "
                                      "failed. {} != {}".format(i1, i2))
                    # row number of first element after insertion
                    first = i1
                    # row number of last element after insertion
                    last = i1 + j2 - j1 - 1
                    logger.debug("     "
                                 "calling beginInsertRows"
                                 "({}, {}, {})".format(tree_index, first,
                                                       last))
                    self.beginInsertRows(tree_index, first, last)
                    tree_item.insert_children(i1, new_items[j1:j2])
                    self.endInsertRows()
                else:
                    raise ValueError("Invalid tag: {}".format(tag))

    def refreshTree(self):
        """
        Refreshes the tree model from the underlying root object
        (which may have been changed).
        """
        logger.info("")
        logger.info("refreshTree: {}".format(self.rootItem))

        root_item = self.treeItem(self.rootIndex())
        logger.info("  root_item:      {} (idx={})".format(
            root_item, self.rootIndex()))
        inspected_item = self.treeItem(self.inspectedIndex())
        logger.info("  inspected_item: {} (idx={})".format(
            inspected_item, self.inspectedIndex()))

        assert (root_item is inspected_item) != self.inspectedNodeIsVisible, \
            "sanity check"

        self._auxRefreshTree(self.inspectedIndex())

        root_obj = self.rootItem.obj
        logger.debug("After _auxRefreshTree, "
                     "root_obj: {}".format(cut_off_str(root_obj, 80)))
        self.rootItem.pretty_print()

        # Emit the dataChanged signal for all cells.
        # This is faster than checking which nodes
        # have changed, which may be slow for some underlying Python objects.
        n_rows = self.rowCount()
        n_cols = self.columnCount()
        top_left = self.index(0, 0)
        bottom_right = self.index(n_rows - 1, n_cols - 1)

        logger.debug("bottom_right: ({}, {})".format(bottom_right.row(),
                                                     bottom_right.column()))
        self.dataChanged.emit(top_left, bottom_right)
Ejemplo n.º 8
0
def _get_font(family, size, bold=False, italic=False):
    weight = QFont.Bold if bold else QFont.Normal
    font = QFont(family, size, weight)
    if italic:
        font.setItalic(True)
    return font
Ejemplo n.º 9
0
    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None

        result = None
        record = self.records[index.row()]
        if getattr(record, '_cutelog', False):
            return self.data_internal(index, record, role)

        if role == Qt.DisplayRole:
            column_name = self.table_header[index.column()].name
            if self.extra_mode and column_name == "message":
                result = self.get_extra(record.message, record)
            else:
                result = getattr(record, column_name, None)
        elif role == Qt.SizeHintRole:
            if self.extra_mode and self.table_header[
                    index.column()].name == 'message':
                return QSize(
                    1,
                    CONFIG.logger_row_height *
                    (1 + len(self.get_fields_for_extra(record))))
            else:
                return QSize(1, CONFIG.logger_row_height)
        elif role == Qt.DecorationRole:
            if self.table_header[index.column()].name == 'message':
                if record.exc_text:
                    mode = CONFIG['exception_indication']
                    should = mode in (Exc_Indication.MSG_ICON,
                                      Exc_Indication.ICON_AND_RED_BG)
                    if should:
                        result = self.parent_widget.style().standardIcon(
                            QStyle.SP_BrowserStop)
        elif role == Qt.FontRole:
            level = self.levels.get(record.levelname, NO_LEVEL)
            styles = level.styles if not self.dark_theme else level.stylesDark
            result = QFont(CONFIG.logger_table_font,
                           CONFIG.logger_table_font_size)
            if styles:
                if 'bold' in styles:
                    result.setBold(True)
                if 'italic' in styles:
                    result.setItalic(True)
                if 'underline' in styles:
                    result.setUnderline(True)
        elif role == Qt.ForegroundRole:
            level = self.levels.get(record.levelname, NO_LEVEL)
            if not self.dark_theme:
                result = level.fg
            else:
                result = level.fgDark
        elif role == Qt.BackgroundRole:
            if record.exc_text:
                mode = CONFIG['exception_indication']
                should = mode in (Exc_Indication.RED_BG,
                                  Exc_Indication.ICON_AND_RED_BG)
                if should:
                    if not self.dark_theme:
                        color = QColor(255, 180, 180)
                    else:
                        color = Qt.darkRed
                    result = QBrush(color, Qt.DiagCrossPattern)
                    return result
            level = self.levels.get(record.levelname, NO_LEVEL)
            if not self.dark_theme:
                result = level.bg
            else:
                result = level.bgDark
        elif role == SearchRole:
            result = record.message
        return result
Ejemplo n.º 10
0
 def set_visible(self, is_visible: bool) -> None:
     font = QFont()
     font.setBold(is_visible)
     font.setItalic(not is_visible)
     self.setFont(0, font)