def __init__(self, parent): """Exclamation icon graphics item. Used to notify that a ProjectItem is missing some configuration. Args: parent (ProjectItemIcon): the parent item """ super().__init__(parent) self._parent = parent self._notifications = list() self.renderer = QSvgRenderer() self.colorizer = QGraphicsColorizeEffect() self.colorizer.setColor(QColor("red")) # Load SVG loading_ok = self.renderer.load( ":/icons/project_item_icons/exclamation-circle.svg") if not loading_ok: return size = self.renderer.defaultSize() self.setSharedRenderer(self.renderer) dim_max = max(size.width(), size.height()) rect_w = parent.rect().width() # Parent rect width self.setScale(0.2 * rect_w / dim_max) self.setGraphicsEffect(self.colorizer) self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False) self.hide()
def findIcons(self): if hou.applicationVersion()[0] < 15: for category in os.listdir(self.path): for ico in os.listdir(os.path.join(self.path, category)): iconPath = os.path.join( os.path.join(self.path, category, ico)) iconName = '_'.join([category, os.path.splitext(ico)[0]]) self.icons[iconName] = QPixmap(iconPath) else: zf = zipfile.ZipFile(self.path, 'r') for f in zf.namelist(): if f.startswith('old'): continue if os.path.splitext(f)[-1] == '.svg': svg = QSvgRenderer(QByteArray(zf.read(f))) if not svg.isValid(): continue pixmap = QPixmap(iconSize, iconSize) painter = QPainter() painter.begin(pixmap) pixmap.fill(QColor(Qt.black)) svg.render(painter) painter.end() category, ico = f.split('/') iconName = '_'.join([category, os.path.splitext(ico)[0]]) self.icons[iconName] = pixmap zf.close()
class ExclamationIcon(QGraphicsSvgItem): def __init__(self, parent): """Exclamation icon graphics item. Used to notify that a ProjectItem is missing some configuration. Args: parent (ProjectItemIcon): the parent item """ super().__init__(parent) self._parent = parent self._notifications = list() self.renderer = QSvgRenderer() self.colorizer = QGraphicsColorizeEffect() self.colorizer.setColor(QColor("red")) # Load SVG loading_ok = self.renderer.load( ":/icons/project_item_icons/exclamation-circle.svg") if not loading_ok: return size = self.renderer.defaultSize() self.setSharedRenderer(self.renderer) dim_max = max(size.width(), size.height()) rect_w = parent.rect().width() # Parent rect width self.setScale(0.2 * rect_w / dim_max) self.setGraphicsEffect(self.colorizer) self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False) self.hide() def clear_notifications(self): """Clear all notifications.""" self._notifications.clear() self.hide() def add_notification(self, text): """Add a notification.""" self._notifications.append(text) self.show() def hoverEnterEvent(self, event): """Shows notifications as tool tip. Args: event (QGraphicsSceneMouseEvent): Event """ if not self._notifications: return tip = "<p>" + "<p>".join(self._notifications) QToolTip.showText(event.screenPos(), tip) def hoverLeaveEvent(self, event): """Hides tool tip. Args: event (QGraphicsSceneMouseEvent): Event """ QToolTip.hideText()
def __load_svg(self, svg_file: str) -> QPixmap: """Loads a SVG file and scales it correctly for High-DPI screens""" svg_renderer = QSvgRenderer(svg_file) pixmap = QPixmap(svg_renderer.defaultSize() * self.devicePixelRatio()) pixmap.fill(Qt.transparent) painter = QPainter() painter.begin(pixmap) svg_renderer.render(painter) painter.end() pixmap.setDevicePixelRatio(self.devicePixelRatio()) return pixmap
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 paint(self, painter, option, index): row = index.row() column = index.column() cell_data = self.grid_data[row][column] is_word_cell = cell_data[0] clue_index = cell_data[1] or cell_data[2] painter.save() if is_word_cell: if clue_index: icon_path = os.path.join(icons_folder, '{}.svg'.format(clue_index)) renderer = QSvgRenderer(icon_path) #pixmap = QPixmap(QSize(grid_cell_size, grid_cell_size)) #pixmap.fill(Qt.transparent) renderer.render(painter, option.rect()) painter.restore()
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)
def __init__(self, parent, fn, *args): super(LoadingBar, self).__init__(parent) self.fn = fn self.args = args self.worker = Worker(fn, *args) self.worker.signals.result.connect( main_window.MainWindow.get_instance().calc_done) main_window.MainWindow.get_instance().threadpool.start(self.worker) # overlay # make the window frameless self.loading_icon = QSvgRenderer('resources/images/loading_icon.svg') self.qp = QPainter() self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.fillColor = QColor(30, 30, 30, 120) self.penColor = QColor("#333333") self.close_btn = QPushButton(self) self.close_btn.setText("Abbrechen") font = QFont() font.setPixelSize(18) font.setBold(True) self.close_btn.setFont(font) self.close_btn.setStyleSheet("QPushButton {" "background-color: #EAEAEA;" "border: None;" "padding-top: 12px;" "padding-bottom: 12px;" "padding-left: 20px;" "padding-right: 20px;" "}" "QPushButton:hover {" "background-color: #DFDFDF;" "}") #self.close_btn.setFixedSize(30, 30) self.close_btn.clicked.connect(self._onclose) self.signals = LoadingBarSignals()
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 data(self, index, role=Qt.DisplayRole): row = index.row() column = index.column() cell_data = self.grid_data[row][column] is_word_cell = cell_data[0] clue_index = cell_data[1] or cell_data[2] if role == Qt.DisplayRole: return self.solution_data[row][column] if role == Qt.EditRole: return self.solution_data[row][column] #elif role == Qt.DecorationRole: # if clue_index: # icon_path = os.path.join(icons_folder, '{}.svg'.format(clue_index)) # return QIcon(icon_path) # else: # return None elif role == Qt.BackgroundRole: if is_word_cell: if clue_index: icon_path = os.path.join(icons_folder, '{}.svg'.format(clue_index)) renderer = QSvgRenderer(icon_path) pixmap = QPixmap(QSize(grid_cell_size, grid_cell_size)) pixmap.fill(Qt.transparent) painter = QPainter() renderer.render(painter, pixmap.rect()) brush = QBrush() brush.setTexture(pixmap) return brush else: return QColor(Qt.white) else: return QColor(Qt.black) elif role == Qt.FontRole: font = QFont(font_name, font_size) return font elif role == Qt.TextAlignmentRole: return Qt.AlignCenter return None
class MCacheDict(object): _render = QSvgRenderer() def __init__(self, cls): super(MCacheDict, self).__init__() self.cls = cls self._cache_pix_dict = {} def _render_svg(self, svg_path, replace_color=None): from dayu_widgets import dayu_theme replace_color = replace_color or dayu_theme.icon_color if (self.cls is QIcon) and (replace_color is None): return QIcon(svg_path) with open(svg_path, 'r') as f: data_content = f.read() if replace_color is not None: data_content = data_content.replace('#555555', replace_color) self._render.load(QByteArray(data_content.encode())) pix = QPixmap(128, 128) pix.fill(Qt.transparent) painter = QPainter(pix) self._render.render(painter) painter.end() if self.cls is QPixmap: return pix else: return self.cls(pix) def __call__(self, path, color=None): from dayu_widgets import utils full_path = utils.get_static_file(path) if full_path is None: return self.cls() key = u'{}{}'.format(full_path.lower(), color or '') pix_map = self._cache_pix_dict.get(key, None) if pix_map is None: if full_path.endswith('svg'): pix_map = self._render_svg(full_path, color) else: pix_map = self.cls(full_path) self._cache_pix_dict.update({key: pix_map}) return pix_map
def _svgRenderer(filename) -> QSvgRenderer: """Get an QSvgRenderer from an `.svg` filename. This function is for internal use within this file. """ return QSvgRenderer(filename)
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)
def svg_to_outlines(root, width_mm=None, height_mm=None, pixels_per_mm=5.0): """ Given an SVG as a Python ElementTree, return a set of straight line segments which approximate the outlines in that SVG when rendered. Occlusion is not accounted for in the returned list of outlines. Even if one shape is completely occluded by another, both of their outlines will be reported. Simillarly, overlapping lines will also be passed through. .. note:: This function internally uses the QSvg library from Qt to render the provided SVG to a special painting backend which captures the output lines. As a consequence, SVG feature support is as good or as bad as that library. QSvg is generally regarded as a high quality and relatively complete SVG implementation and so most SVG features should be supported. .. note:: Due to its internal use of Qt, a PySide2.QtGui.QGuiApplication will be created if one has not already been created. Non-Qt users and most Qt users should not be affected by this. Parameters ---------- root : ElementTree The SVG whose outlines should be extracted. width_mm, height_mm : float or None The page size to render the SVG at (in milimeters). If omitted, this will be determined automatically from the SVG's width and height attributes. These arguments are mandatory for SVGs lacking these attributes. pixels_per_mm : float Curved outlines will be approximated by straight lines in the output. This parameter controls how exactly curves will be approximated. Specifically, the curve approximation will be at least fine enough for rasterised versions of the lines to 'look right' at the specified pixel density. Returns ------- [((r, g, b, a) or None, width, [(x, y), ...]), ...] A list of polylines described by (colour, line) pairs. The 'colour' values define the colour used to draw the line (if a solid colour was used) or None (if a gradient or patterned stroke was used). Colours are given as four-tuples in RGBA order with values from 0.0 to 1.0. The 'width' value gives the line width used to draw the line (given in mm). If the shape being drawn has a non-uniform scaling applied, this value may not be meaningful and its actual value should be considered undefined. The 'line' part of each tuple defines the outline as a series of (x, y) coordinates (given in mm) which describe a continuous polyline. These polylines may be considered open. Closed lines in the input SVG will result in polylines where the first and last coordinate are identical. Lines may go beyond the bounds of the designated page size (as in the input SVG). """ # This method internally uses various parts of Qt which require that a Qt # application exists. If one does not exist, one will be created. if QGuiApplication.instance() is None: QGuiApplication() # Determine the page size from the document if necessary if width_mm is None or height_mm is None: width_mm, height_mm = get_svg_page_size(root) # Load the SVG into QSvg xml_stream_reader = QXmlStreamReader() xml_stream_reader.addData(ElementTree.tostring(root, "unicode")) svg_renderer = QSvgRenderer() svg_renderer.load(xml_stream_reader) # Paint the SVG into the OutlinePaintDevice which will capture the set of # line segments which make up the SVG as rendered. outline_paint_device = OutlinePaintDevice(width_mm, height_mm, pixels_per_mm) painter = QPainter(outline_paint_device) try: svg_renderer.render(painter) finally: painter.end() return outline_paint_device.getOutlines()
class LoadingBar(QWidget): def __init__(self, parent, fn, *args): super(LoadingBar, self).__init__(parent) self.fn = fn self.args = args self.worker = Worker(fn, *args) self.worker.signals.result.connect( main_window.MainWindow.get_instance().calc_done) main_window.MainWindow.get_instance().threadpool.start(self.worker) # overlay # make the window frameless self.loading_icon = QSvgRenderer('resources/images/loading_icon.svg') self.qp = QPainter() self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.fillColor = QColor(30, 30, 30, 120) self.penColor = QColor("#333333") self.close_btn = QPushButton(self) self.close_btn.setText("Abbrechen") font = QFont() font.setPixelSize(18) font.setBold(True) self.close_btn.setFont(font) self.close_btn.setStyleSheet("QPushButton {" "background-color: #EAEAEA;" "border: None;" "padding-top: 12px;" "padding-bottom: 12px;" "padding-left: 20px;" "padding-right: 20px;" "}" "QPushButton:hover {" "background-color: #DFDFDF;" "}") #self.close_btn.setFixedSize(30, 30) self.close_btn.clicked.connect(self._onclose) self.signals = LoadingBarSignals() def resizeEvent(self, event=None): button_x = (self.width() - self.close_btn.width()) / 2 button_y = (self.height() - self.close_btn.height()) / 2 + 60 self.close_btn.move(button_x, button_y) def paintEvent(self, event): # This method is, in practice, drawing the contents of # your window. # get current window size s = self.size() self.qp.begin(self) self.qp.setRenderHint(QPainter.Antialiasing, True) self.qp.setPen(self.penColor) self.qp.setBrush(self.fillColor) self.qp.drawRect(0, 0, s.width(), s.height()) icon_size = 180 self.loading_icon.render( self.qp, QRect((self.width() - icon_size) / 2, (self.height() - icon_size) / 2 - 60, icon_size, icon_size)) self.qp.end() def _onclose(self): self.signals.close.emit()
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)