class DataNode: def __init__(self, data, radius=15): self.data = data # Add circular node self.node = QGraphicsEllipseItem(0, 0, 1, 1) # Set radius self.radius = radius # Add text label self.label = QGraphicsTextItem(data.label) font = self.label.font() font.setPointSize(10) self.label.setFont(font) # Add line between label and node self.line1 = QGraphicsLineItem(0, 0, 1, 1) self.line2 = QGraphicsLineItem(0, 0, 1, 1) self.node.setZValue(20) self.label.setZValue(10) self.line1.setZValue(10) self.line2.setZValue(10) self.line1.setPen(get_pen('0.5')) self.line2.setPen(get_pen('0.5')) self.color = '0.8' @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value self.node.setRect(-value, -value, 2 * value, 2 * value) def contains(self, point): # Check label if self.label.contains(self.label.mapFromScene(point)): return True # Check node if self.node.contains(self.node.mapFromScene(point)): return True return False def update(self): self.node.update() def add_to_scene(self, scene): scene.addItem(self.node) scene.addItem(self.label) scene.addItem(self.line1) scene.addItem(self.line2) def remove_from_scene(self, scene): scene.removeItem(self.node) scene.removeItem(self.label) scene.removeItem(self.line1) scene.removeItem(self.line2) @property def node_position(self): pos = self.node.pos() return pos.x(), pos.y() @node_position.setter def node_position(self, value): self.node.setPos(value[0], value[1]) self.update_lines() @property def label_position(self): pos = self.label.pos() return pos.x(), pos.y() @label_position.setter def label_position(self, value): self.label.setPos(value[0], value[1]) self.update_lines() def update_lines(self): x0, y0 = self.label_position x2, y2 = self.node_position x1 = 0.5 * (x0 + x2) y1 = y0 self.line1.setLine(x0, y0, x1, y1) self.line2.setLine(x1, y1, x2, y2) @property def color(self): return qt_to_mpl_color(self.node.brush().color()) @color.setter def color(self, value): self.node.setBrush(mpl_to_qt_color(value))
def addLine(self, text, color, width=1.): shape = QGraphicsLineItem(10., 10., 20., 20.) pen = makePen(color, width=width) shape.setPen(pen) self.addEntry(MapLegendEntryItem(shape, text))
class Diagram_Item(QGraphicsItem): def __init__(self, entity): super().__init__() self.entity = entity self.modified_callback = None self.connection_in_progress_from = None self.connection_in_progress_line = None self.connect_on_double_click = True self.avoid_router = avoid.Router( #avoid.PolyLineRouting) avoid.OrthogonalRouting) self.avoid_router.setRoutingParameter(avoid.shapeBufferDistance, 10.0) self.avoid_router.setRoutingParameter(avoid.idealNudgingDistance, 10.0) self.avoid_router.setRoutingParameter(avoid.crossingPenalty, 50000000) self.route_radius = 6.0 self.pin_length = 10.0 self._connection_items = [] self._block_items = [] for i, s in enumerate(self.entity.symbols): w = s.width h = s.height s_ui = self.add_block(s, debug=False) s_ui.moveBy(s.x, s.y) r = s_ui.rect() r.setWidth(w) r.setHeight(h) s_ui.setRect(r) s_ui._ensure_minimum_size() for c in self.entity.connections: self.add_connection(c) self.process_avoid_updates() def double_clicked_in_background(self, scene_pos): # Note that this block should really be associated with # some component, but that is not required at this time. b = diagram.Block(name="Untitled", width=100, height=100) b_item = self.add_block(b, debug=False) b_item.setPos(b_item.mapFromScene(scene_pos)) b_item.set_editing_mode(True, edit_title=True) return True # Do not call mouseDoubleClickEvent, will stop editing def mouse_moved_in_scene(self, scene_pos): diagram = self if diagram.connection_in_progress_from: diagram_pos = diagram.mapFromScene(scene_pos) x1 = diagram.connection_in_progress_line.line().x1() y1 = diagram.connection_in_progress_line.line().y1() diagram.connection_in_progress_line.setLine( x1, y1, diagram_pos.x(), diagram_pos.y()) self.connection_in_progress_from.setPen(QPen(highlight_color, 4)) def start_connecting(self, c, event): self.connection_in_progress_from = c x, y = c.get_connection_point() p1 = self.mapToParent( self.mapFromScene(c.parentItem().mapToScene(x, y))) x1 = p1.x() y1 = p1.y() p2 = self.mapFromScene(event.scenePos()) self.connection_in_progress_line = QGraphicsLineItem( x1, y1, p2.x(), p2.y()) self.connection_in_progress_line.setParentItem(self) self.connection_in_progress_line.setPen(QPen(highlight_color, 4)) c.setPen(QPen(highlight_color, 4)) self.set_hover_highlight_connectors(True) def finish_connecting(self, c): start_c = self.connection_in_progress_from if c != start_c: source_ep = diagram.Endpoint(connector=start_c._connector) sink_ep = diagram.Endpoint(connector=c._connector) conn = diagram.Connection( source=source_ep, sink=sink_ep, ) self._stop_connecting() self.add_connection(conn) self.process_avoid_updates() else: self.abort_connecting(c) def abort_connecting(self, source_item=None): if source_item: if self.connection_in_progress_from: source_item.clearFocus() self._stop_connecting() def _stop_connecting(self): start_c = self.connection_in_progress_from if start_c: self.set_hover_highlight_connectors(False) start_c._set_default_appearance() self.connection_in_progress_from = None self.connection_in_progress_line.setParentItem(None) self.connection_in_progress_line = None def add_connection(self, connection): c_ui = Connection_Item(connection, self) c_ui.setParentItem(self) c_ui.arrow.setParentItem(self) self._connection_items.append(c_ui) self._hide_duplicate_connections() if not connection in self.entity.connections: self.entity.connections.append(connection) return c_ui def remove_connection(self, c_ui): self.avoid_router.deleteConnector(c_ui.avoid_conn) self._connection_items.remove(c_ui) c_ui.setParentItem(None) c_ui.arrow.setParentItem(None) self._hide_duplicate_connections() self.entity.connections.remove(c_ui._connection) def add_block(self, block, debug=False): s_ui = Block_Item(block, debug=debug) s_ui.modified_callback = self.set_modified s_ui.setParentItem(self) self._block_items.append(s_ui) if not block in self.entity.symbols: self.entity.symbols.append(block) return s_ui def remove_block(self, block): self._block_items.remove(block) block.remove_all_connectors() block.setParentItem(None) self.entity.symbols.remove(block._block) def mouse_pressed_in(self, source_item): # Allow diagram elements to know that the mouse was pressed # outside of the element. for item in self._block_items: if source_item != item: item.mouse_pressed_in(source_item) self.abort_connecting(source_item) def paint(self, *args, **kw): # Implement pure virtual method pass def boundingRect(self, *args, **kw): # Implement pure virtual method return QRectF(0, 0, 0, 0) def set_hover_highlight_connectors(self, highlight): for b in self._block_items: b.set_hover_highlight_connectors(highlight) def set_show_connection_ports_on_hover(self, show): for c_ui in list(self._connection_items): c_ui.set_show_connection_ports_on_hover(show) def set_modified(self, subitem=None): if self.modified_callback: self.modified_callback(self) def process_avoid_updates(self): for c_ui in list(self._connection_items): success = c_ui.update_endpoints() if not success: self.remove_connection(c_ui) self.avoid_router.processTransaction() for c_ui in self._connection_items: c_ui.update_from_avoid_router() self.set_modified() def _hide_duplicate_connections(self): processed_connections = [] for c in self._connection_items: c._duplicate_is = None c._duplicate_of = None c.show() for p in processed_connections: if (c._sink_ui == p._source_ui and c._source_ui == p._sink_ui): c.hide() p._duplicate_is = c c._duplicate_of = p processed_connections.append(c) self.process_avoid_updates()
class DualColoredLineSegment(QGraphicsItemGroup): """ One individual line segment. This is a group because it needs to create two lines in order to create a multi colored dashed pattern. Attributes: spacing (int): how much space in pixels are between each line segment """ def __init__(self, parent=None, width=1, color1=QColor(0, 0, 0), color2=QColor(255, 255, 255)): super(DualColoredLineSegment, self).__init__(parent) # create lines self.line_1 = QGraphicsLineItem() self.line_2 = QGraphicsLineItem() self.width = width self._length = 2 self._spacing = 5 # set pen self.setColor1(color1) self.setColor2(color2) # add lines to group self.addToGroup(self.line_1) self.addToGroup(self.line_2) """ DISPLAY""" def setColor1(self, color): """ Sets the first line to the specified color Args: color (QColor): color for the line to be set to """ pen = self.createPen(color) self.line_1.setPen(pen) def setColor2(self, color): """ Sets the first line to the specified color Args: color (QColor): color for the line to be set to """ pen = self.createPen(color, offset=True) self.line_2.setPen(pen) def updatePen(self): for line in [self.line_1, self.line_2]: pen = line.pen() total_line_space = self.length() + (2 * self.spacing()) pen.setDashPattern([self.length(), total_line_space]) pen.setWidth(self.width) line.setPen(pen) def createPen(self, color, offset=None): """ Creates a pen of the color specified Args: color (QColor): color for the line to be set to offset (bool): if color should be offset or not. Since this is only two colors, this can be a boolean, rather than an index. """ pen = QPen() pen.setColor(color) total_line_space = self.length() + (2 * self.spacing()) if offset: pen.setDashOffset(self.length() + self.spacing()) pen.setDashPattern([self.length(), total_line_space]) pen.setWidth(self.width) return pen def setLine(self, x, y, width, height): self.line_1.setLine(x, y, width, height) self.line_2.setLine(x, y, width, height) """ PROPERTIES """ def width(self): return self._width def setWidth(self, width): self._width = width def length(self): return self._length def setLength(self, length): self._length = length def spacing(self): return self._spacing def setSpacing(self, spacing): self._spacing = spacing