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)
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)
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 __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()
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]
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]
def showLogo(win, logoName): logoPath = str(sessionVars.assetPath / logoName) ico = QGraphicsSvgItem(logoPath) scene = QGraphicsScene() scene.addItem(ico) win.logo.setScene(scene)
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 __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 __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 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
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)
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())
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)
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)
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