Ejemplo n.º 1
0
 def __add_squares(self):
     """Adds initial squares to the board view."""
     for file in 'abcdefgh':
         for rank in '12345678':
             (x, y) = self.__pos_to_x_y(file + rank)
             square = QGraphicsSvgItem(
                 self.IMAGES['l'] if self.
                 __is_light_square(file + rank) else self.IMAGES['d'])
             square.setPos(x, y)
             self.addItem(square)
Ejemplo n.º 2
0
    def __init__(self, toolbox, x, y, w, h, project_item, icon_file,
                 icon_color, background_color):
        """Base class for project item icons drawn in Design View.

        Args:
            toolbox (ToolBoxUI): QMainWindow instance
            x (float): Icon x coordinate
            y (float): Icon y coordinate
            w (float): Icon width
            h (float): Icon height
            project_item (ProjectItem): Item
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self._project_item = project_item
        self._moved_on_scene = False
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem()
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(QRectF(x, y, w, h))  # Set ellipse coordinates and size
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        name = project_item.name if project_item else ""
        self.name_item = QGraphicsSimpleTextItem(name)
        shadow_effect = QGraphicsDropShadowEffect()
        shadow_effect.setOffset(1)
        shadow_effect.setEnabled(False)
        self.setGraphicsEffect(shadow_effect)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.rank_icon = RankIcon(self)
        # Group the drawn items together by setting the background rectangle as the parent of other QGraphicsItems
        # NOTE: setting the parent item moves the items as one!
        self.name_item.setParentItem(self)
        for conn in self.connectors.values():
            conn.setParentItem(self)
        self.svg_item.setParentItem(self)
        self.exclamation_icon.setParentItem(self)
        self.rank_icon.setParentItem(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        # Add items to scene
        scene = self._toolbox.ui.graphicsView.scene()
        scene.addItem(self)
Ejemplo n.º 3
0
 def __init__(self, x, y, w, h, parent):
     super().__init__(x, y, w, h, parent)
     self._parent = parent
     color = QColor("slateblue")
     self.setBrush(qApp.palette().window())  # pylint: disable=undefined-variable
     self._text_item = QGraphicsTextItem(self)
     font = QFont('Font Awesome 5 Free Solid')
     self._text_item.setFont(font)
     self._text_item.setDefaultTextColor(color)
     self._svg_item = QGraphicsSvgItem(self)
     self._datapkg_renderer = QSvgRenderer()
     self._datapkg_renderer.load(":/icons/datapkg.svg")
     self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False)
     self._block_updates = False
Ejemplo n.º 4
0
    def __init__(self, text=None, icon_filename=None, parent=None):
        super(Message, self).__init__(parent)
        self.setFlag(QGraphicsItem.ItemHasNoContents)

        # Message text -------------------------------------------------------------------------------------------------
        self._text_item = TextLineItem(text or "Message", self)

        # Message icon -------------------------------------------------------------------------------------------------
        self._icon_item = QGraphicsSvgItem(self)
        if icon_filename is not None:
            self.setIcon(icon_filename)

        self.styleChange()
        self.paletteChange()
Ejemplo n.º 5
0
    def highlight_squares(self, squares):
        """
        Highlighted a list of squares on the board.

        :param squares: List of squares to highlight.
        """
        for item in self.highlighted_square_items:
            self.removeItem(item)
        self.highlighted_square_items = []

        for pos in squares:
            (x, y) = self.__pos_to_x_y(pos)
            highlighted_square_item = QGraphicsSvgItem(self.IMAGES['h'])
            highlighted_square_item.setPos(x, y)
            self.addItem(highlighted_square_item)
            self.highlighted_square_items += [highlighted_square_item]
Ejemplo n.º 6
0
    def set_position(self, populated_squares):
        """See BoardView.set_position()."""
        for item in self.highlighted_square_items:
            self.removeItem(item)
        self.highlighted_square_items = []

        for item in self.piece_items:
            self.removeItem(item)
        self.piece_items = []

        for pos in populated_squares:
            (x, y) = self.__pos_to_x_y(pos)
            piece = QGraphicsSvgItem(self.IMAGES[populated_squares[pos]])
            piece.setPos(x, y)
            self.addItem(piece)
            self.piece_items += [piece]
Ejemplo n.º 7
0
def showLogo(win, logoName):

    logoPath = str(sessionVars.assetPath / logoName)

    ico = QGraphicsSvgItem(logoPath)
    scene = QGraphicsScene()
    scene.addItem(ico)

    win.logo.setScene(scene)
Ejemplo n.º 8
0
    def __init__(self, toolbox, x, y, project_item, icon_file, icon_color,
                 background_color):
        """Base class for project item icons drawn in Design View.

        Args:
            toolbox (ToolBoxUI): QMainWindow instance
            x (float): Icon x coordinate
            y (float): Icon y coordinate
            project_item (ProjectItem): Item
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self._project_item = project_item
        self._moved_on_scene = False
        self._previous_pos = QPointF()
        self._current_pos = QPointF()
        self.icon_group = {self}
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem(self)
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(
            QRectF(x - self.ITEM_EXTENT / 2, y - self.ITEM_EXTENT / 2,
                   self.ITEM_EXTENT, self.ITEM_EXTENT))
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        name = project_item.name if project_item else ""
        self.name_item = QGraphicsSimpleTextItem(name, self)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.rank_icon = RankIcon(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        self.activate()
Ejemplo n.º 9
0
 def __init__(self, toolbox, icon_file, icon_color, background_color):
     """
     Args:
         toolbox (ToolboxUI): QMainWindow instance
         icon_file (str): Path to icon resource
         icon_color (QColor): Icon's color
         background_color (QColor): Background color
     """
     super().__init__()
     self._toolbox = toolbox
     self.icon_file = icon_file
     self._moved_on_scene = False
     self.previous_pos = QPointF()
     self.current_pos = QPointF()
     self.icon_group = {self}
     self.renderer = QSvgRenderer()
     self.svg_item = QGraphicsSvgItem(self)
     self.colorizer = QGraphicsColorizeEffect()
     self.setRect(
         QRectF(-self.ITEM_EXTENT / 2, -self.ITEM_EXTENT / 2,
                self.ITEM_EXTENT, self.ITEM_EXTENT))
     self.text_font_size = 10  # point size
     # Make item name graphics item.
     self._name = ""
     self.name_item = QGraphicsSimpleTextItem(self._name, self)
     self.set_name_attributes()  # Set font, size, position, etc.
     # Make connector buttons
     self.connectors = dict(
         bottom=ConnectorButton(self, toolbox, position="bottom"),
         left=ConnectorButton(self, toolbox, position="left"),
         right=ConnectorButton(self, toolbox, position="right"),
     )
     # Make exclamation and rank icons
     self.exclamation_icon = ExclamationIcon(self)
     self.execution_icon = ExecutionIcon(self)
     self.rank_icon = RankIcon(self)
     brush = QBrush(background_color)
     self._setup(brush, icon_file, icon_color)
     shadow_effect = QGraphicsDropShadowEffect()
     shadow_effect.setOffset(1)
     shadow_effect.setEnabled(False)
     self.setGraphicsEffect(shadow_effect)
Ejemplo n.º 10
0
    def __init__(self, spine_db_editor, x, y, extent, db_map_entity_id=None):
        """Initializes item

        Args:
            spine_db_editor (SpineDBEditor): 'owner'
            x (float): x-coordinate of central point
            y (float): y-coordinate of central point
            extent (int): Preferred extent
            db_map_entity_id (tuple): db_map, entity id
        """
        super().__init__()
        self._spine_db_editor = spine_db_editor
        self.db_mngr = spine_db_editor.db_mngr
        self.db_map_entity_id = db_map_entity_id
        self.arc_items = list()
        self._extent = extent
        self.setRect(-0.5 * self._extent, -0.5 * self._extent, self._extent,
                     self._extent)
        self.setPen(Qt.NoPen)
        self._svg_item = QGraphicsSvgItem(self)
        self._svg_item.setCacheMode(
            QGraphicsItem.CacheMode.NoCache
        )  # Needed for the exported pdf to be vector
        self.refresh_icon()
        self.setPos(x, y)
        self._moved_on_scene = False
        self._bg = None
        self._bg_brush = Qt.NoBrush
        self._init_bg()
        self._bg.setFlag(QGraphicsItem.ItemStacksBehindParent, enabled=True)
        self.setZValue(0)
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsMovable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIgnoresTransformations, enabled=True)
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, enabled=True)
        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.ArrowCursor)
        self.setToolTip(self._make_tool_tip())
Ejemplo n.º 11
0
    def get_q_svg_scene_item(self, parent=None):

        # TODO this is overkill, recreating the SVG Widget way too often.
        self.create(
            config_controller.box_size[0] * self.properties.w,
            config_controller.box_size[1] * self.properties.h,
        )

        q_graphics_svg_item = QGraphicsSvgItem()
        q_graphics_svg_item.parent = self
        q_graphics_svg_item.setFlags(
            QGraphicsItem.ItemIsMovable
            | QGraphicsItem.ItemIsSelectable
            | QGraphicsItem.ItemIsFocusable
            | QGraphicsItem.ItemSendsScenePositionChanges
            | QGraphicsItem.ItemSendsGeometryChanges)
        q_graphics_svg_item.uid = self.uid
        q_graphics_svg_item.setSharedRenderer(self.svg_renderer)
        q_graphics_svg_item.setPos(
            float(config_controller.box_size[0] * self.properties.x),
            float(config_controller.box_size[1] * self.properties.y),
        )
        return q_graphics_svg_item
Ejemplo n.º 12
0
class ProjectItemIcon(QGraphicsRectItem):
    """Base class for project item icons drawn in Design View."""

    ITEM_EXTENT = 64

    def __init__(self, toolbox, icon_file, icon_color, background_color):
        """
        Args:
            toolbox (ToolboxUI): QMainWindow instance
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self.icon_file = icon_file
        self._moved_on_scene = False
        self.previous_pos = QPointF()
        self.current_pos = QPointF()
        self.icon_group = {self}
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem(self)
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(
            QRectF(-self.ITEM_EXTENT / 2, -self.ITEM_EXTENT / 2,
                   self.ITEM_EXTENT, self.ITEM_EXTENT))
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        self._name = ""
        self.name_item = QGraphicsSimpleTextItem(self._name, self)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.execution_icon = ExecutionIcon(self)
        self.rank_icon = RankIcon(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        shadow_effect = QGraphicsDropShadowEffect()
        shadow_effect.setOffset(1)
        shadow_effect.setEnabled(False)
        self.setGraphicsEffect(shadow_effect)

    def finalize(self, name, x, y):
        """
        Names the icon and moves it by given amount.

        Args:
            name (str): icon's name
            x (int): horizontal offset
            y (int): vertical offset
        """
        self.update_name_item(name)
        self.moveBy(x, y)

    def _setup(self, brush, svg, svg_color):
        """Setup item's attributes.

        Args:
            brush (QBrush): Used in filling the background rectangle
            svg (str): Path to SVG icon file
            svg_color (QColor): Color of SVG icon
        """
        self.setPen(QPen(Qt.black, 1, Qt.SolidLine))
        self.setBrush(brush)
        self.colorizer.setColor(svg_color)
        # Load SVG
        loading_ok = self.renderer.load(svg)
        if not loading_ok:
            self._toolbox.msg_error.emit(
                "Loading SVG icon from resource:{0} failed".format(svg))
            return
        size = self.renderer.defaultSize()
        self.svg_item.setSharedRenderer(self.renderer)
        self.svg_item.setElementId(
            "")  # guess empty string loads the whole file
        dim_max = max(size.width(), size.height())
        rect_w = self.rect().width()  # Parent rect width
        margin = 32
        self.svg_item.setScale((rect_w - margin) / dim_max)
        self.svg_item.setPos(self.rect().center() -
                             self.svg_item.sceneBoundingRect().center())
        self.svg_item.setGraphicsEffect(self.colorizer)
        self.setFlag(QGraphicsItem.ItemIsMovable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, enabled=True)
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, enabled=True)
        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.PointingHandCursor)
        # Set exclamation, execution_log, and rank icons position
        self.exclamation_icon.setPos(
            self.rect().topRight() -
            self.exclamation_icon.sceneBoundingRect().topRight())
        self.execution_icon.setPos(
            self.rect().bottomRight() -
            0.5 * self.execution_icon.sceneBoundingRect().bottomRight())
        self.rank_icon.setPos(self.rect().topLeft())

    def name(self):
        """Returns name of the item that is represented by this icon.

        Returns:
            str: icon's name
        """
        return self._name

    def update_name_item(self, new_name):
        """Set a new text to name item.

        Args:
            new_name (str): icon's name
        """
        self._name = new_name
        self.name_item.setText(new_name)
        self.set_name_attributes()

    def set_name_attributes(self):
        """Set name QGraphicsSimpleTextItem attributes (font, size, position, etc.)"""
        # Set font size and style
        font = self.name_item.font()
        font.setPointSize(self.text_font_size)
        font.setBold(True)
        self.name_item.setFont(font)
        # Set name item position (centered on top of the master icon)
        name_width = self.name_item.boundingRect().width()
        name_height = self.name_item.boundingRect().height()
        self.name_item.setPos(
            self.rect().x() + self.rect().width() / 2 - name_width / 2,
            self.rect().y() - name_height - 4)

    def conn_button(self, position="left"):
        """Returns item's connector button.

        Args:
            position (str): "left", "right" or "bottom"

        Returns:
            QWidget: connector button
        """
        return self.connectors.get(position, self.connectors["left"])

    def outgoing_links(self):
        """Collects outgoing links.

        Returns:
            list of LinkBase: outgoing links
        """
        return [
            l for conn in self.connectors.values()
            for l in conn.outgoing_links()
        ]

    def incoming_links(self):
        """Collects incoming links.

        Returns:
            list of LinkBase: outgoing links
        """
        return [
            l for conn in self.connectors.values()
            for l in conn.incoming_links()
        ]

    def run_execution_leave_animation(self, skipped):
        """
        Starts the animation associated with execution leaving the icon.

        Args:
            skipped (bool): True if project item was not actually executed.
        """
        animation_group = QParallelAnimationGroup(self._toolbox)
        for link in self.outgoing_links():
            animation_group.addAnimation(
                link.make_execution_animation(skipped))
        animation_group.start()

    def hoverEnterEvent(self, event):
        """Sets a drop shadow effect to icon when mouse enters its boundaries.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        self.prepareGeometryChange()
        self.graphicsEffect().setEnabled(True)
        event.accept()

    def hoverLeaveEvent(self, event):
        """Disables the drop shadow when mouse leaves icon boundaries.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        self.prepareGeometryChange()
        self.graphicsEffect().setEnabled(False)
        event.accept()

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        self.icon_group = set(x for x in self.scene().selectedItems()
                              if isinstance(x, ProjectItemIcon)) | {self}
        for icon in self.icon_group:
            icon.previous_pos = icon.scenePos()

    def mouseMoveEvent(self, event):
        """Moves icon(s) while the mouse button is pressed.
        Update links that are connected to selected icons.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        super().mouseMoveEvent(event)
        self.update_links_geometry()

    def moveBy(self, dx, dy):
        super().moveBy(dx, dy)
        self.update_links_geometry()

    def update_links_geometry(self):
        """Updates geometry of connected links to reflect this item's most recent position."""
        links = set(link for icon in self.icon_group
                    for conn in icon.connectors.values()
                    for link in conn.links)
        for link in links:
            link.update_geometry()

    def mouseReleaseEvent(self, event):
        for icon in self.icon_group:
            icon.current_pos = icon.scenePos()
        # pylint: disable=undefined-variable
        if (self.current_pos - self.previous_pos
            ).manhattanLength() > qApp.startDragDistance():
            self._toolbox.undo_stack.push(
                MoveIconCommand(self, self._toolbox.project()))
        super().mouseReleaseEvent(event)

    def notify_item_move(self):
        if self._moved_on_scene:
            self._moved_on_scene = False
            scene = self.scene()
            scene.item_move_finished.emit(self)

    def contextMenuEvent(self, event):
        """Show item context menu.

        Args:
            event (QGraphicsSceneMouseEvent): Mouse event
        """
        event.accept()
        self.scene().clearSelection()
        self.setSelected(True)
        ind = self._toolbox.project_item_model.find_item(self.name())
        self._toolbox.show_project_item_context_menu(event.screenPos(), ind)

    def itemChange(self, change, value):
        """
        Reacts to item removal and position changes.

        In particular, destroys the drop shadow effect when the items is removed from a scene
        and keeps track of item's movements on the scene.

        Args:
            change (GraphicsItemChange): a flag signalling the type of the change
            value: a value related to the change

        Returns:
             Whatever super() does with the value parameter
        """
        if change == QGraphicsItem.ItemScenePositionHasChanged:
            self._moved_on_scene = True
        elif change == QGraphicsItem.GraphicsItemChange.ItemSceneChange and value is None:
            self.prepareGeometryChange()
            self.setGraphicsEffect(None)
        return super().itemChange(change, value)

    def select_item(self):
        """Update GUI to show the details of the selected item."""
        ind = self._toolbox.project_item_model.find_item(self.name())
        self._toolbox.ui.treeView_project.setCurrentIndex(ind)
Ejemplo n.º 13
0
class EntityItem(QGraphicsRectItem):
    """Base class for ObjectItem and RelationshipItem."""
    def __init__(self, spine_db_editor, x, y, extent, db_map_entity_id=None):
        """Initializes item

        Args:
            spine_db_editor (SpineDBEditor): 'owner'
            x (float): x-coordinate of central point
            y (float): y-coordinate of central point
            extent (int): Preferred extent
            db_map_entity_id (tuple): db_map, entity id
        """
        super().__init__()
        self._spine_db_editor = spine_db_editor
        self.db_mngr = spine_db_editor.db_mngr
        self.db_map_entity_id = db_map_entity_id
        self.arc_items = list()
        self._extent = extent
        self.setRect(-0.5 * self._extent, -0.5 * self._extent, self._extent,
                     self._extent)
        self.setPen(Qt.NoPen)
        self._svg_item = QGraphicsSvgItem(self)
        self._svg_item.setCacheMode(
            QGraphicsItem.CacheMode.NoCache
        )  # Needed for the exported pdf to be vector
        self.refresh_icon()
        self.setPos(x, y)
        self._moved_on_scene = False
        self._bg = None
        self._bg_brush = Qt.NoBrush
        self._init_bg()
        self._bg.setFlag(QGraphicsItem.ItemStacksBehindParent, enabled=True)
        self.setZValue(0)
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsMovable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIgnoresTransformations, enabled=True)
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, enabled=True)
        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.ArrowCursor)
        self.setToolTip(self._make_tool_tip())

    def _make_tool_tip(self):
        raise NotImplementedError()

    @property
    def entity_type(self):
        raise NotImplementedError()

    @property
    def entity_name(self):
        return self.db_mngr.get_item(self.db_map, self.entity_type,
                                     self.entity_id)["name"]

    @property
    def entity_class_type(self):
        return {
            "relationship": "relationship_class",
            "object": "object_class"
        }[self.entity_type]

    @property
    def entity_class_id(self):
        return self.db_mngr.get_item(self.db_map, self.entity_type,
                                     self.entity_id)["class_id"]

    @property
    def entity_class_name(self):
        return self.db_mngr.get_item(self.db_map, self.entity_class_type,
                                     self.entity_class_id)["name"]

    @property
    def db_map(self):
        return self.db_map_entity_id[0]

    @property
    def entity_id(self):
        return self.db_map_entity_id[1]

    @property
    def first_db_map(self):
        return self.db_map

    @property
    def display_data(self):
        return self.entity_name

    @property
    def display_database(self):
        return self.db_map.codename

    @property
    def db_maps(self):
        return (self.db_map, )

    def db_map_data(self, _db_map):
        # NOTE: Needed by EditObjectsDialog and EditRelationshipsDialog
        return self.db_mngr.get_item(self.db_map, self.entity_type,
                                     self.entity_id)

    def db_map_id(self, _db_map):
        # NOTE: Needed by EditObjectsDialog and EditRelationshipsDialog
        return self.entity_id

    def boundingRect(self):
        return super().boundingRect() | self.childrenBoundingRect()

    def moveBy(self, dx, dy):
        super().moveBy(dx, dy)
        self.update_arcs_line()

    def _init_bg(self):
        self._bg = QGraphicsRectItem(self.boundingRect(), self)
        self._bg.setPen(Qt.NoPen)

    def refresh_icon(self):
        """Refreshes the icon."""
        renderer = self.db_mngr.entity_class_renderer(self.db_map,
                                                      self.entity_class_type,
                                                      self.entity_class_id)
        self._set_renderer(renderer)

    def _set_renderer(self, renderer):
        self._svg_item.setSharedRenderer(renderer)
        size = renderer.defaultSize()
        scale = self._extent / max(size.width(), size.height())
        self._svg_item.setScale(scale)
        self._svg_item.setPos(self.rect().center() - 0.5 * scale *
                              QPointF(size.width(), size.height()))

    def shape(self):
        """Returns a shape containing the entire bounding rect, to work better with icon transparency."""
        path = QPainterPath()
        path.setFillRule(Qt.WindingFill)
        path.addRect(self._bg.boundingRect())
        return path

    def paint(self, painter, option, widget=None):
        """Shows or hides the selection halo."""
        if option.state & (QStyle.State_Selected):
            self._paint_as_selected()
            option.state &= ~QStyle.State_Selected
        else:
            self._paint_as_deselected()
        super().paint(painter, option, widget)

    def _paint_as_selected(self):
        self._bg.setBrush(QGuiApplication.palette().highlight())

    def _paint_as_deselected(self):
        self._bg.setBrush(self._bg_brush)

    def add_arc_item(self, arc_item):
        """Adds an item to the list of arcs.

        Args:
            arc_item (ArcItem)
        """
        self.arc_items.append(arc_item)
        arc_item.update_line()

    def apply_zoom(self, factor):
        """Applies zoom.

        Args:
            factor (float): The zoom factor.
        """
        if factor > 1:
            factor = 1
        self.setScale(factor)

    def apply_rotation(self, angle, center):
        """Applies rotation.

        Args:
            angle (float): The angle in degrees.
            center (QPoint): Rotates around this point.
        """
        line = QLineF(center, self.pos())
        line.setAngle(line.angle() + angle)
        self.setPos(line.p2())
        self.update_arcs_line()

    def block_move_by(self, dx, dy):
        self.moveBy(dx, dy)

    def mouseMoveEvent(self, event):
        """Moves the item and all connected arcs.

        Args:
            event (QGraphicsSceneMouseEvent)
        """
        if event.buttons() & Qt.LeftButton == 0:
            super().mouseMoveEvent(event)
            return
        move_by = event.scenePos() - event.lastScenePos()
        # Move selected items together
        for item in self.scene().selectedItems():
            if isinstance(item, (EntityItem)):
                item.block_move_by(move_by.x(), move_by.y())

    def update_arcs_line(self):
        """Moves arc items."""
        for item in self.arc_items:
            item.update_line()

    def itemChange(self, change, value):
        """
        Keeps track of item's movements on the scene.

        Args:
            change (GraphicsItemChange): a flag signalling the type of the change
            value: a value related to the change

        Returns:
            the same value given as input
        """
        if change == QGraphicsItem.ItemScenePositionHasChanged:
            self._moved_on_scene = True
        return value

    def set_all_visible(self, on):
        """Sets visibility status for this item and all arc items.

        Args:
            on (bool)
        """
        for item in self.arc_items:
            item.setVisible(on)
        self.setVisible(on)

    def _make_menu(self):
        return self._spine_db_editor.ui.graphicsView.make_items_menu()

    def contextMenuEvent(self, e):
        """Shows context menu.

        Args:
            e (QGraphicsSceneMouseEvent): Mouse event
        """
        e.accept()
        if not self.isSelected() and not e.modifiers() & Qt.ControlModifier:
            self.scene().clearSelection()
        self.setSelected(True)
        menu = self._make_menu()
        menu.popup(e.screenPos())
Ejemplo n.º 14
0
class ProjectItemIcon(QGraphicsRectItem):

    ITEM_EXTENT = 64

    def __init__(self, toolbox, x, y, project_item, icon_file, icon_color,
                 background_color):
        """Base class for project item icons drawn in Design View.

        Args:
            toolbox (ToolBoxUI): QMainWindow instance
            x (float): Icon x coordinate
            y (float): Icon y coordinate
            project_item (ProjectItem): Item
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self._project_item = project_item
        self._moved_on_scene = False
        self._previous_pos = QPointF()
        self._current_pos = QPointF()
        self.icon_group = {self}
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem(self)
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(
            QRectF(x - self.ITEM_EXTENT / 2, y - self.ITEM_EXTENT / 2,
                   self.ITEM_EXTENT, self.ITEM_EXTENT))
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        name = project_item.name if project_item else ""
        self.name_item = QGraphicsSimpleTextItem(name, self)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.rank_icon = RankIcon(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        self.activate()

    def activate(self):
        """Adds items to scene and setup graphics effect.
        Called in the constructor and when re-adding the item to the project in the context of undo/redo.
        """
        scene = self._toolbox.ui.graphicsView.scene()
        scene.addItem(self)
        shadow_effect = QGraphicsDropShadowEffect()
        shadow_effect.setOffset(1)
        shadow_effect.setEnabled(False)
        self.setGraphicsEffect(shadow_effect)

    def _setup(self, brush, svg, svg_color):
        """Setup item's attributes.

        Args:
            brush (QBrush): Used in filling the background rectangle
            svg (str): Path to SVG icon file
            svg_color (QColor): Color of SVG icon
        """
        self.setPen(QPen(Qt.black, 1, Qt.SolidLine))
        self.setBrush(brush)
        self.colorizer.setColor(svg_color)
        # Load SVG
        loading_ok = self.renderer.load(svg)
        if not loading_ok:
            self._toolbox.msg_error.emit(
                "Loading SVG icon from resource:{0} failed".format(svg))
            return
        size = self.renderer.defaultSize()
        self.svg_item.setSharedRenderer(self.renderer)
        self.svg_item.setElementId(
            "")  # guess empty string loads the whole file
        dim_max = max(size.width(), size.height())
        rect_w = self.rect().width()  # Parent rect width
        margin = 32
        self.svg_item.setScale((rect_w - margin) / dim_max)
        x_offset = (rect_w - self.svg_item.sceneBoundingRect().width()) / 2
        y_offset = (rect_w - self.svg_item.sceneBoundingRect().height()) / 2
        self.svg_item.setPos(self.rect().x() + x_offset,
                             self.rect().y() + y_offset)
        self.svg_item.setGraphicsEffect(self.colorizer)
        self.setFlag(QGraphicsItem.ItemIsMovable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, enabled=True)
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, enabled=True)
        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.PointingHandCursor)
        # Set exclamation and rank icons position
        self.exclamation_icon.setPos(
            self.rect().topRight() -
            self.exclamation_icon.sceneBoundingRect().topRight())
        self.rank_icon.setPos(self.rect().topLeft())

    def name(self):
        """Returns name of the item that is represented by this icon."""
        return self._project_item.name

    def update_name_item(self, new_name):
        """Set a new text to name item. Used when a project item is renamed."""
        self.name_item.setText(new_name)
        self.set_name_attributes()

    def set_name_attributes(self):
        """Set name QGraphicsSimpleTextItem attributes (font, size, position, etc.)"""
        # Set font size and style
        font = self.name_item.font()
        font.setPointSize(self.text_font_size)
        font.setBold(True)
        self.name_item.setFont(font)
        # Set name item position (centered on top of the master icon)
        name_width = self.name_item.boundingRect().width()
        name_height = self.name_item.boundingRect().height()
        self.name_item.setPos(
            self.rect().x() + self.rect().width() / 2 - name_width / 2,
            self.rect().y() - name_height - 4)

    def conn_button(self, position="left"):
        """Returns items connector button (QWidget)."""
        return self.connectors.get(position, self.connectors["left"])

    def outgoing_links(self):
        return [
            l for conn in self.connectors.values()
            for l in conn.outgoing_links()
        ]

    def incoming_links(self):
        return [
            l for conn in self.connectors.values()
            for l in conn.incoming_links()
        ]

    def hoverEnterEvent(self, event):
        """Sets a drop shadow effect to icon when mouse enters its boundaries.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        self.prepareGeometryChange()
        self.graphicsEffect().setEnabled(True)
        event.accept()

    def hoverLeaveEvent(self, event):
        """Disables the drop shadow when mouse leaves icon boundaries.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        self.prepareGeometryChange()
        self.graphicsEffect().setEnabled(False)
        event.accept()

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        self.icon_group = set(x for x in self.scene().selectedItems()
                              if isinstance(x, ProjectItemIcon)) | {self}
        for icon in self.icon_group:
            icon._previous_pos = icon.scenePos()

    def mouseMoveEvent(self, event):
        """Moves icon(s) while the mouse button is pressed.
        Update links that are connected to selected icons.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        super().mouseMoveEvent(event)
        self.update_links_geometry()

    def moveBy(self, dx, dy):
        super().moveBy(dx, dy)
        self.update_links_geometry()

    def update_links_geometry(self):
        """Updates geometry of connected links to reflect this item's most recent position."""
        links = set(link for icon in self.icon_group
                    for conn in icon.connectors.values()
                    for link in conn.links)
        for link in links:
            link.update_geometry()

    def mouseReleaseEvent(self, event):
        for icon in self.icon_group:
            icon._current_pos = icon.scenePos()
        # pylint: disable=undefined-variable
        if (self._current_pos - self._previous_pos
            ).manhattanLength() > qApp.startDragDistance():
            self._toolbox.undo_stack.push(MoveIconCommand(self))
        super().mouseReleaseEvent(event)

    def notify_item_move(self):
        if self._moved_on_scene:
            self._moved_on_scene = False
            scene = self.scene()
            scene.item_move_finished.emit(self)

    def contextMenuEvent(self, event):
        """Show item context menu.

        Args:
            event (QGraphicsSceneMouseEvent): Mouse event
        """
        self.scene().clearSelection()
        self.setSelected(True)
        self._toolbox.show_item_image_context_menu(event.screenPos(),
                                                   self.name())

    def keyPressEvent(self, event):
        """Handles deleting and rotating the selected
        item when dedicated keys are pressed.

        Args:
            event (QKeyEvent): Key event
        """
        if event.key() == Qt.Key_Delete and self.isSelected():
            self._project_item._project.remove_item(self.name())
            event.accept()
        elif event.key() == Qt.Key_R and self.isSelected():
            # TODO:
            # 1. Change name item text direction when rotating
            # 2. Save rotation into project file
            rect = self.mapToScene(self.boundingRect()).boundingRect()
            center = rect.center()
            t = QTransform()
            t.translate(center.x(), center.y())
            t.rotate(90)
            t.translate(-center.x(), -center.y())
            self.setPos(t.map(self.pos()))
            self.setRotation(self.rotation() + 90)
            links = set(lnk for conn in self.connectors.values()
                        for lnk in conn.links)
            for link in links:
                link.update_geometry()
            event.accept()
        else:
            super().keyPressEvent(event)

    def itemChange(self, change, value):
        """
        Reacts to item removal and position changes.

        In particular, destroys the drop shadow effect when the items is removed from a scene
        and keeps track of item's movements on the scene.

        Args:
            change (GraphicsItemChange): a flag signalling the type of the change
            value: a value related to the change

        Returns:
             Whatever super() does with the value parameter
        """
        if change == QGraphicsItem.ItemScenePositionHasChanged:
            self._moved_on_scene = True
        elif change == QGraphicsItem.GraphicsItemChange.ItemSceneChange and value is None:
            self.prepareGeometryChange()
            self.setGraphicsEffect(None)
        return super().itemChange(change, value)

    def show_item_info(self):
        """Update GUI to show the details of the selected item."""
        ind = self._toolbox.project_item_model.find_item(self.name())
        self._toolbox.ui.treeView_project.setCurrentIndex(ind)
Ejemplo n.º 15
0
class _LinkIcon(QGraphicsEllipseItem):
    """An icon to show over a Link."""
    def __init__(self, x, y, w, h, parent):
        super().__init__(x, y, w, h, parent)
        self._parent = parent
        color = QColor("slateblue")
        self.setBrush(qApp.palette().window())  # pylint: disable=undefined-variable
        self._text_item = QGraphicsTextItem(self)
        font = QFont('Font Awesome 5 Free Solid')
        self._text_item.setFont(font)
        self._text_item.setDefaultTextColor(color)
        self._svg_item = QGraphicsSvgItem(self)
        self._datapkg_renderer = QSvgRenderer()
        self._datapkg_renderer.load(":/icons/datapkg.svg")
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False)
        self._block_updates = False

    def update_icon(self):
        """Sets the icon (filter, datapkg, or none), depending on Connection state."""
        connection = self._parent.connection
        if connection.use_datapackage:
            self.setVisible(True)
            self._svg_item.setVisible(True)
            self._svg_item.setSharedRenderer(self._datapkg_renderer)
            scale = 0.8 * self.rect().width(
            ) / self._datapkg_renderer.defaultSize().width()
            self._svg_item.setScale(scale)
            self._svg_item.setPos(0, 0)
            self._svg_item.setPos(self.sceneBoundingRect().center() -
                                  self._svg_item.sceneBoundingRect().center())
            self._text_item.setVisible(False)
            return
        if connection.has_filters():
            self.setVisible(True)
            self._text_item.setVisible(True)
            self._text_item.setPlainText("\uf0b0")
            self._svg_item.setPos(0, 0)
            self._text_item.setPos(
                self.sceneBoundingRect().center() -
                self._text_item.sceneBoundingRect().center())
            self._svg_item.setVisible(False)
            return
        self.setVisible(False)
        self._text_item.setVisible(False)
        self._svg_item.setVisible(False)
Ejemplo n.º 16
0
class Message(SchemeItem):
    """A message that is displayed to the user.
    
    Messages can be of different severity. This base class is inherited by InfoMessage, WarningMessage and
    ErrorMessage.
    """
    def __init__(self, text=None, icon_filename=None, parent=None):
        super(Message, self).__init__(parent)
        self.setFlag(QGraphicsItem.ItemHasNoContents)

        # Message text -------------------------------------------------------------------------------------------------
        self._text_item = TextLineItem(text or "Message", self)

        # Message icon -------------------------------------------------------------------------------------------------
        self._icon_item = QGraphicsSvgItem(self)
        if icon_filename is not None:
            self.setIcon(icon_filename)

        self.styleChange()
        self.paletteChange()

    @classmethod
    def severity(cls):
        """Message severity: how important is this message. Lower is more important."""
        return 0

    def setText(self, text):
        self._text_item.setText(text)

    def setIcon(self, icon_filename):
        """Set the icon from an SVG file."""
        renderer = _svgRenderer(icon_filename)
        self._icon_item.setSharedRenderer(renderer)

        icon_size = self.style().pixelMetric(Style.MessageIconSize)

        scale = icon_size / renderer.defaultSize().width()
        self._icon_item.setScale(scale)

    def text(self):
        return self._text_item.text()

    def styleChange(self):
        super().styleChange()
        style = self.style()

        # Text item ----------------------------------------------------------------------------------------------------
        self._text_item.setFont(style.font(Style.MessageTextFont))
        self._text_item.setMaximumWidth(style.pixelMetric(Style.MessageTextLength))

        # Position
        text_metrics = QFontMetricsF(self._text_item.font())
        icon_size = style.pixelMetric(Style.MessageIconSize)
        margin = style.pixelMetric(Style.MessageIconTextMargin)

        self._text_item.setPos(icon_size + margin, 0)
        self._text_item.moveBy(0, text_metrics.capHeight() / 2)
        self._text_item.moveBy(0, icon_size / 2)

        # Icon item ----------------------------------------------------------------------------------------------------
        icon_renderer = self._icon_item.renderer()
        if icon_renderer is None:  # No icon set
            scale = 1
        else:
            scale = icon_size / icon_renderer.defaultSize().width()

        self._icon_item.setScale(scale)
    
    def paletteChange(self):
        super().paletteChange()
        text_bg = self.palette().background().color()
        text_bg.setAlpha(196)

        self._text_item.setBrush(self.palette().text())
        self._text_item.setBackgroundBrush(text_bg)

    def boundingRect(self) -> QRectF:
        return QRectF()

    def paint(self, painter: QPainter, option, widget=...) -> None:
        pass
    
    # Serialization ====================================================================================================
    def serialize(self) -> dict:
        return {
            "text": self.text()
        }

    @classmethod
    def deserialize(cls, data: dict):
        obj = cls()
        obj.setText(data["text"])

        return obj