def __init__(self, label, indices, pos, length, height, label_width, frame_width, color, parent=None): super(TimeLine, self).__init__(parent) self._pos = pos self._length = length self._height = height self.label = label self._label_width = label_width self._indices = indices self.frame_width = frame_width size = QSizeF() size.setHeight(self._height) size.setWidth(self._length) self._bounding_rect = QRectF() self._bounding_rect.setX(self._pos.x()) self._bounding_rect.setY(self._pos.y()) self._bounding_rect.setSize(size) self._tpos = QPointF(pos) self._tpos.setY(pos.y() + self._height) self.color = QColor() self.color.setRed(color[0] * 255) self.color.setGreen(color[1] * 255) self.color.setBlue(color[2] * 255) self.color.setAlpha(255)
def create_frame_indicator(self): size = QSizeF() height = 100 size.setHeight(height) size.setWidth(self.label_height) self.frame_indicator = FrameIndicator(size, self.frame_width, self.label_width, 0) self._label_scene.addItem(self.frame_indicator) self.edit_start_frame_indicator = FrameIndicator( size, self.frame_width, self.label_width, 0, blue) self._label_scene.addItem(self.edit_start_frame_indicator)
def getSizeFromCmnd(self, sizeinfo): ''' Returns a QSizeF based on the information in the dictionary sizeinfo. Recognized keys are "width" and "height", and correspond to those float values in the QSizeF. Values not given in sizeinfo are assigned as zero in the returned QSizeF. ''' myrect = QSizeF(0.0, 0.0) try: myrect.setWidth(float(sizeinfo["width"])) except KeyError: pass try: myrect.setHeight(float(sizeinfo["height"])) except KeyError: pass return myrect
class Node(SchemeItem): """The main component of the graph scene. A node represents a single vertex of the graph, with its associated information and inputs/outputs. """ def __init__(self, parent=None): super().__init__(parent) self.setFlag(self.ItemIsMovable) self.setFlag(self.ItemIsSelectable) # Default size and text self._size = QSizeF(0, 115) self._body_item = QGraphicsPathItem(self) self._title_item = TextLineItem("Node", self) self._divider = QGraphicsLineItem(self) self._description_item = TextRectItem("No description", self) # Inputs and outputs self.inputs = [] self.outputs = [] self.messages = SortedList(key=lambda item: item.severity()) self._config_widget = None # Set proper style and color for the item self.styleChange() self.paletteChange() # Operations ======================================================================================================= def showConnectionText(self): for input in self.inputs: input.showText() for output in self.outputs: output.showText() def hideConnectionText(self): for input in self.inputs: input.hideText() for output in self.outputs: output.hideText() # Input/output management ========================================================================================== def addInput(self, obj: Input): self.insertInput(len(self.inputs), obj) def insertInput(self, index, obj: Input): index = clamp(index, 0, len(self.inputs)) self.inputs.insert(index, obj) obj.setParentItem(self) self._updateInputPositions() def removeInput(self, index): removed = self.inputs.pop(index) removed.setParentItem(None) self._updateInputPositions() return removed def addOutput(self, obj: Output): self.insertOutput(len(self.outputs), obj) def insertOutput(self, index, obj: Output): index = clamp(index, 0, len(self.outputs)) self.outputs.insert(index, obj) obj.setParentItem(self) self._updateOutputPositions() def removeOutput(self, index): removed = self.outputs.pop(index) removed.setParentItem(None) self._updateOutputPositions() return removed # Message management =============================================================================================== def addMessage(self, obj: Message): self.messages.add(obj) obj.setParentItem(self) self._updateMessagePositions() def removeMessage(self, index): removed = self.messages.pop(index) removed.setParentItem(None) self._updateMessagePositions() return removed # Member variables ================================================================================================= def setTitle(self, title): self._title_item.setText(title) def setDescription(self, description): self._description_item.setText(description) def setConfigWidget(self, w): self._config_widget = w def size(self): return self._size def title(self): return self._title_item.text() def description(self): return self._description_item.text() def configWidget(self): """Return a widget for configuring this node, or None if it does not exist.""" return self._config_widget # Updating functions =============================================================================================== def _updateInputPositions(self): padding = self.style().pixelMetric(Style.NodeConnectionPadding) i = 0 for item in self.inputs: item.setPos(0, padding * (2 * i + 1)) i += 1 def _updateOutputPositions(self): padding = self.style().pixelMetric(Style.NodeConnectionPadding) i = 0 for item in self.outputs: item.setPos(self.size().width(), padding * (2 * i + 1)) i += 1 def _updateMessagePositions(self): padding = self.style().pixelMetric(Style.NodeMessagePadding) icon_size = self.style().pixelMetric(Style.MessageIconSize) i = 0 for msg in self.messages: msg.setPos( 0, self.size().height() + padding * (i + 1) + icon_size * i) i += 1 # Geometry and drawing ============================================================================================= def boundingRect(self) -> QRectF: frame_width = self.style().pixelMetric(Style.NodeFrameWidth) return QRectF(QPointF(-frame_width / 2, -frame_width / 2), self.size() + QSizeF(frame_width, frame_width)) def shape(self): frame_corner_radius = self.style().pixelMetric( Style.NodeFrameCornerRadius) path = QPainterPath() path.addRoundedRect(QRectF(QPointF(0, 0), self.size()), frame_corner_radius, frame_corner_radius) return path def paint(self, painter: QPainter, option, widget=...) -> None: pass # Event handlers =================================================================================================== def styleChange(self): self.prepareGeometryChange() style = self.style() # Size and Frame frame_width = style.pixelMetric(Style.NodeWidth) self._size.setWidth(frame_width) self._body_item.setPen(style.framePen(self.palette())) path = QPainterPath() path.addRoundedRect(QRectF(QPointF(), self.size()), style.pixelMetric(Style.NodeFrameCornerRadius), style.pixelMetric(Style.NodeFrameCornerRadius)) self._body_item.setPath(path) # Title item title_font = style.font(Style.NodeTitleFont) title_metrics = QFontMetricsF(title_font) padding = style.pixelMetric(Style.NodeFrameTextPadding) self._title_item.setFont(title_font) self._title_item.setPos(padding, padding) self._title_item.moveBy(0, title_metrics.capHeight()) self._title_item.setMaximumWidth(frame_width - padding * 2) # Divider item div_margin = style.pixelMetric(Style.NodeDividerTextMargin) div_voffset = padding + title_metrics.capHeight( ) + title_metrics.descent() + div_margin self._divider.setLine(0, div_voffset, frame_width, div_voffset) self._divider.setPen(style.framePen(self.palette())) # Description item self._description_item.setFont(style.font(Style.NodeDescriptionFont)) self._description_item.setPos(padding, div_voffset + div_margin) self._description_item.setFrame( QRectF( QPointF(0, 0), QSizeF( px(frame_width) - padding * 2, (px(self.size().height()) - padding) - (div_voffset + padding)))) def paletteChange(self): self._body_item.setPen(self.style().framePen(self.palette())) self._body_item.setBrush(self.palette().base()) self._title_item.setBrush(self.palette().text()) self._description_item.setBrush(self.palette().text()) self._divider.setPen(self.style().framePen(self.palette())) self.update(self.boundingRect()) def itemChange(self, change, value): if change == self.ItemSelectedHasChanged: selection = self.scene().selection() if len(selection.nodes) == 1: for edge in selection.edges: edge.autoSelect() # Update selection status for all connections for input in self.inputs: input.autoSelect() for output in self.outputs: output.autoSelect() return super().itemChange(change, value) def mouseDoubleClickEvent(self, event): if self.configWidget() is not None: view = event.widget().parent() view.configRequested.emit(self) # Serialization ==================================================================================================== def serialize(self) -> dict: return { "title": self.title(), "description": self.description(), "inputs": self.inputs, "outputs": self.outputs, "messages": list(self.messages), "position": inch(self.pos()) } @classmethod def deserialize(cls, data: dict): obj = cls() obj.setTitle(data["title"]) obj.setDescription(data["description"]) obj.setPos(px(data["position"])) for i in range(len(obj.inputs)): obj.removeInput(0) for i in range(len(obj.outputs)): obj.removeOutput(0) for i in range(len(obj.messages)): obj.removeMessage(0) for input in data["inputs"]: obj.addInput(input) for output in data["outputs"]: obj.addOutput(output) for message in data["messages"]: obj.addMessage(message) return obj