class DefaultGraphicalVertex(Vertex, QtGui.QGraphicsEllipseItem): circleSize = 10.0 * 2 def __init__(self, vertex, graph): QtGui.QGraphicsEllipseItem .__init__(self, 0, 0, self.circleSize, self.circleSize, None) Vertex.__init__(self, vertex, graph, defaultCenterConnector=True) mousePressEvent = qtutils.mixin_method(Vertex, QtGui.QGraphicsEllipseItem, "mousePressEvent") itemChange = qtutils.mixin_method(Vertex, QtGui.QGraphicsEllipseItem, "itemChange") paint = qtutils.mixin_method(QtGui.QGraphicsEllipseItem, None, "paint")
class InvisibleConnector(QtGui.QGraphicsEllipseItem, Connector): size = 10 def __init__(self, parent, *args, **kwargs): QtGui.QGraphicsEllipseItem.__init__(self, 0, 0 , self.size, self.size, None) Connector.__init__(self, *args, **kwargs) self.setBrush(QtGui.QBrush(QtCore.Qt.darkGreen)) # Needs to be visible or else won't receive events # we override paint in order to hide the item self.setVisible(True) self.fakeParent = parent def position_changed(self, *args): """reimplemented to do nothing. otherwise caught position changes from the model and ignored the position it was forced to""" pass def paint(self, painter, options, widget): pass itemChange = qtutils.mixin_method(Connector, QtGui.QGraphicsEllipseItem, "itemChange")
class GraphicalPort(qt.QtGui.QGraphicsEllipseItem, qtgraphview.Connector): """ A vertex port """ MAX_TIPLEN = 400 WIDTH = 7.0 HEIGHT = 7.0 def __init__(self, parent, port): """ """ qt.QtGui.QGraphicsEllipseItem.__init__(self, 0, 0, self.WIDTH, self.HEIGHT, parent) qtgraphview.Connector.__init__(self, observed=port) self.__interfaceColor = None self.set_connection_modifiers(qt.QtCore.Qt.NoModifier) self.initialise_from_model() port = baselisteners.GraphElementListenerBase.get_observed def initialise_from_model(self): port = self.port() mdict = port.get_ad_hoc_dict() # graphical data init. mdict.simulate_full_data_change(self, port) interface = port.get_interface() if interface and interface.__color__ is not None: self.__interfaceColor = qt.QtGui.QColor(*interface.__color__) # update tooltip self.notify(port, ("tooltip_modified", port.get_tip())) def change_observed(self, old, new): if isinstance(new, AbstractPort): qtgraphview.Element.clear_observed(self) self.set_observed(new) def close_and_delete(self, obj): self.clear_observed() del self def notify(self, sender, event): try: self.port() except: self.clear_observed() del self return if (event[0] in ["tooltip_modified", "stop_eval"]): self.__update_tooltip() elif (event[0] == "metadata_changed"): if (sender == self.port()): if (event[1] == "hide"): if event[2]: # if hide self.hide() else: self.show() self.parentItem().refresh_geometry() qtgraphview.Connector.notify(self, sender, event) def clear_observed(self, *args): qtgraphview.Element.clear_observed(self) return def __update_tooltip(self): node = self.port().vertex() if isinstance(self.port(), OutputPort): data = node.get_output(self.port().get_id()) elif isinstance(self.port(), InputPort): data = node.get_input(self.port().get_id()) try: s = str(data) except: s = '' if (len(s) > self.MAX_TIPLEN): s = "String too long..." self.setToolTip(s) else: #self.setToolTip("Value: " + s) self.setToolTip(self.port().get_tip(data)) def get_id(self): return self.port().get_id() ################## # QtWorld-Events # ################# def contextMenuEvent(self, event): if isinstance(self.port(), OutputPort): operator = GraphOperator(graph=self.graph, graphScene=self.scene()) operator.set_port_item(self) menu = qtutils.AleaQMenu(operator.get_sensible_parent()) menu.addAction(operator("Send to pool", menu, "port_send_to_pool")) menu.addAction( operator("Send to console", menu, "port_send_to_console")) menu.addAction(operator("Print", menu, "port_print_value")) menu.show() menu.move(event.screenPos()) event.accept() def paint(self, painter, option, widget): if (not self.isVisible()): return pos = self.pos() painter.setBackgroundMode(qt.QtCore.Qt.TransparentMode) gradient = qt.QtGui.QLinearGradient(0, 0, 10, 0) if self.highlighted: gradient.setColorAt(1, qt.QtGui.QColor(qt.QtCore.Qt.red).lighter(120)) gradient.setColorAt( 0, qt.QtGui.QColor(qt.QtCore.Qt.darkRed).lighter(120)) else: if self.__interfaceColor is None: gradient.setColorAt( 0.8, qt.QtGui.QColor(qt.QtCore.Qt.yellow).lighter(120)) gradient.setColorAt( 0.2, qt.QtGui.QColor(qt.QtCore.Qt.darkYellow).lighter(120)) else: gradient.setColorAt(0.8, self.__interfaceColor.lighter(120)) gradient.setColorAt(0.2, self.__interfaceColor.lighter(120)) painter.setBrush(qt.QtGui.QBrush(gradient)) painter.setPen(qt.QtGui.QPen(qt.QtCore.Qt.black, 0)) painter.drawEllipse(qt.QtCore.QRectF(0, 0, self.WIDTH, self.HEIGHT)) itemChange = mixin_method(qtgraphview.Connector, qt.QtGui.QGraphicsItem, "itemChange")
class ObserverOnlyGraphicalVertex( qtgraphview.Vertex, qtutils.AleaQGraphicsRoundedRectItem, ): # --- PAINTING STUFF --- # Color Definition default_pen_color = qt.QtGui.QColor(qt.QtCore.Qt.darkGray) default_pen_selected_color = qt.QtGui.QColor(qt.QtCore.Qt.lightGray) default_pen_error_color = qt.QtGui.QColor(qt.QtCore.Qt.red) default_top_color = qt.QtGui.QColor(200, 200, 200, 255) default_bottom_color = qt.QtGui.QColor(140, 140, 255, 255) default_error_color = qt.QtGui.QColor(255, 0, 0, 255) default_user_application_color = qt.QtGui.QColor(255, 144, 0, 200) default_unlazy_color = qt.QtGui.QColor(200, 255, 160, 255) # gradient stops startPos = 0.0 endPos = 1.0 # Shape definition portSpacing = 5.0 outMargins = 5.0 delayMargins = 7.0 evalColor = qt.QtGui.QColor(255, 0, 0, 200) default_corner_radius = 1.2 default_margin = 3.0 pen_width = 1.0 maxTipLength = 400 def __init__(self, vertex, graph, parent=None): qtutils.AleaQGraphicsRoundedRectItem.__init__( self, self.default_corner_radius, True, 0, 0, 1, 1, parent) qtgraphview.Vertex.__init__(self, vertex, graph) # ----- The colors ----- self.__topColor = self.default_top_color self.__bottomColor = self.default_bottom_color self.__penColor = self.default_pen_color # ----- Layout of the item ----- ph = GraphicalPort.HEIGHT self.vLayout = qtutils.VerticalLayout(margins=(self.outMargins, self.outMargins, 0., 0.), center=True) # in ports self.inPortLayout = qtutils.HorizontalLayout( parent=self.vLayout, innerMargins=(self.portSpacing, 0.), center=True, mins=(ph, ph)) # Caption self._caption = qt.QtGui.QGraphicsSimpleTextItem(self) self.vLayout.addItem(self._caption) # out ports self.outPortLayout = qtutils.HorizontalLayout( parent=self.vLayout, innerMargins=(self.portSpacing, 0.), center=True, mins=(ph, ph)) # Editor self.__editor = None # Small dots when the vertex has hidden ports hiddenPortItem = HiddenPort(self) hiddenPortItem.setVisible(False) self.hiddenPortItem = hiddenPortItem self.inPortLayout.addFinalItem(hiddenPortItem) # Small box when the vertex is busy, beping evaluated self._busyItem = qt.QtGui.QGraphicsRectItem(0, 0, 7, 7, self) self._busyItem.setBrush(self.evalColor) self._busyItem.setAcceptedMouseButtons(qt.QtCore.Qt.NoButton) self._busyItem.setVisible(False) # Clock image when the vertex has a delay self._delayItem = qt.QtSvg.QGraphicsSvgItem(":icons/clock.svg", self) self._delayItem.setAcceptedMouseButtons(qt.QtCore.Qt.NoButton) self._delayItem.setVisible(False) self._delayText = qt.QtGui.QGraphicsSimpleTextItem( "0", self._delayItem) self._delayText.setFont(qt.QtGui.QFont("ariana", 6)) self._delayText.setBrush( qt.QtGui.QBrush(qt.QtGui.QColor(255, 0, 0, 200))) self._delayText.setZValue(self._delayItem.zValue() + 1) self._delayText.setVisible(False) # ----- drawing nicities ----- self.setPen(qt.QtGui.QPen(qt.QtCore.Qt.black, self.pen_width)) if safeEffects: fx = qt.QtGui.QGraphicsDropShadowEffect() fx.setOffset(2, 2) fx.setBlurRadius(5) self.setGraphicsEffect(fx) def initialise_from_model(self): vertex = self.vertex() mdict = vertex.get_ad_hoc_dict() # position/color... mdict.simulate_full_data_change(self, vertex) userColor = self.get_view_data("userColor") if (userColor is None): self.store_view_data(useUserColor=False) # add connectors and configure their visibility for i in vertex.input_desc + vertex.output_desc: self.add_port(i) # once connectors are added we also initialise them for c in self.iter_connectors(): c.initialise_from_model() # configure other items to be visible or not self.update_delay_item() self.update_hidden_port_item() self.set_graphical_tooltip(vertex.get_tip()) self.set_graphical_caption(vertex.caption) # self.refresh_geometry() already done by set_graphical_caption self.update_colors() # last because gradient depends on geometry def terminate_from_model(self): vertex = self.vertex() self.remove_ports(lambda x: isinstance(x, InputPort)) self.remove_ports(lambda x: isinstance(x, OutputPort)) def set_editor_instance(self, editor): self.__editor = editor def get_editor_instance(self): return self.__editor ########### # Queries # ########### def all_inputs_visible(self): for i in self.inPortLayout: if not i.isVisible(): return False return True ############# # Modifiers # ############# def update_hidden_port_item(self): visible = not self.all_inputs_visible() and self.isVisible() self.hiddenPortItem.setVisible(visible) def update_delay_item(self): visible = self.vertex().delay > 0 self._delayItem.setVisible(visible and self.isVisible()) self._delayText.setVisible(visible and self.isVisible()) if visible: self._delayText.setText(str(self.vertex().delay)) def update_colors(self): self.__topColor = self.default_top_color self.__bottomColor = self.default_bottom_color self.__penColor = self.default_pen_color if not self.get_view_data("useUserColor"): if self.vertex().raise_exception: self.__topColor = self.default_error_color self.__bottomColor = self.__topColor.darker() self.__penColor = self.default_pen_error_color elif self.vertex().user_application: self.__topColor = self.default_user_application_color self.__bottomColor = self.__topColor.darker() elif not self.vertex().lazy: self.__topColor = self.default_unlazy_color self.__bottomColor = self.__topColor.darker() else: userColor = self.get_view_data("userColor") if userColor: self.__topColor = qt.QtGui.QColor(*userColor) self.__bottomColor = qt.QtGui.QColor(*userColor) pen = self.pen() pen.setColor(self.__penColor) gradient = qt.QtGui.QLinearGradient(self.rect().topLeft(), self.rect().bottomLeft()) gradient.setColorAt(self.startPos, self.__topColor) gradient.setColorAt(self.endPos, self.__bottomColor) brush = qt.QtGui.QBrush(gradient) self.setPen(pen) self.setBrush(brush) def set_graphical_caption(self, caption): """Sets the name displayed in the vertex widget, doesn't change the vertex data""" if caption == "" or caption == None: caption = " " if len(caption) > 20: caption = caption[:20] + "..." self._caption.setText(caption) self.refresh_geometry() def set_graphical_tooltip(self, rawtooltip): if rawtooltip is None: rawtooltip = "" if len(rawtooltip) > self.maxTipLength: rawtooltip = rawtooltip[:self.maxTipLength] rawtooltip += "...\nSee Help tab for complete documentation" self.setToolTip(rawtooltip) #################### # Observer methods # #################### def store_view_data(self, **kwargs): for k, v in kwargs.iteritems(): self.vertex().get_ad_hoc_dict().set_metadata(k, v) def get_view_data(self, key): return self.vertex().get_ad_hoc_dict().get_metadata(key) def notify(self, sender, event): """ Notification sent by the vertex associated to the item """ if event is None: return try: refresh = eval(Settings().get("UI", "EvalCue")) except: refresh = True eventTopKey = event[0] if eventTopKey == "close": if self.__editor: self.__editor.close() elif eventTopKey == "data_modified": key = event[1] if key == "caption": self.set_graphical_caption(event[2]) elif eventTopKey == "internal_state_changed": key = event[1] if key == "delay": self.update_delay_item() elif key == "lazy" or key == "blocked" or key == "user_application": self.update_colors() elif eventTopKey == "metadata_changed": if event[1] == "userColor": if event[2] is None: self.store_view_data(useUserColor=False) else: self.update_colors() elif event[1] == "useUserColor": self.update_colors() elif eventTopKey == "exception_state_changed": self.update_colors() elif eventTopKey == "hiddenPortChange": self.update_hidden_port_item() elif (eventTopKey == "tooltip_modified"): self.set_graphical_tooltip(event[1]) if refresh: if (eventTopKey == "start_eval"): self._busyItem.setVisible(self.isVisible()) qt.QtGui.QApplication.processEvents() elif (eventTopKey == "stop_eval"): self._busyItem.setVisible(False) qt.QtGui.QApplication.processEvents() elif (eventTopKey == "input_port_added"): self.add_port(event[1]) elif (eventTopKey == "output_port_added"): self.add_port(event[1]) elif (eventTopKey == "cleared_input_ports"): self.remove_ports(lambda x: isinstance(x.port(), InputPort)) elif (eventTopKey == "cleared_output_ports"): self.remove_ports(lambda x: isinstance(x.port(), OutputPort)) self.update() qtgraphview.Vertex.notify(self, sender, event) ####################################### # Methods to add/remove model ports # # They automatically do the according # # operation in the GUI. # ####################################### def add_port(self, modelPort): if isinstance(modelPort, InputPort): l = self.inPortLayout elif isinstance(modelPort, OutputPort): l = self.outPortLayout if modelPort not in l: gp = GraphicalPort(self, modelPort) if gp: l.addItem(gp) self.add_connector(gp) self.refresh_geometry() def remove_port(self, modelPort): if isinstance(modelPort, InputPort): l = self.inPortLayout elif isinstance(modelPort, OutputPort): l = self.outPortLayout for gp in l._items[:]: if gp.port() == modelPort: l.removeItem(gp) gp.remove_from_view(self.scene()) self.remove_connector(gp) del gp self.refresh_geometry() def remove_ports(self, filter=lambda x: True): for con in list(self.iter_connectors(filter)): l = self.inPortLayout if isinstance( con.port(), InputPort) else self.outPortLayout l.removeItem(con) con.remove_from_view(self.scene()) self.remove_connector(con) ##################################################################### # Code related to the layout of subitems and geometry of the vertex # ##################################################################### def layout_items(self): geom = self.vLayout.boundingRect(force=True) self.vLayout.setPos(qt.QtCore.QPointF(0., 0.)) self._busyItem.setPos(0, 0) diBr = self._delayItem.boundingRect() dtBr = self._delayText.boundingRect() self._delayItem.setPos(-diBr.width() / 2 - self.delayMargins, (geom.height() - diBr.height()) / 2) self._delayText.setPos((diBr.width() - dtBr.width()) / 2, (diBr.height() - dtBr.height()) / 2) return geom def refresh_geometry(self): halfPortH = GraphicalPort.HEIGHT / 2 geom = self.layout_items().adjusted(-self.pen_width, halfPortH - self.pen_width, self.pen_width, -(halfPortH - self.pen_width)) self.setRect(geom) self.refresh_cached_shape() ################ # Drawing Code # ################ def paint(self, painter, options, widget): path = self.shape() pen = self.pen() brush = self.brush() painter.setPen(pen) painter.setBrush(brush) painter.drawPath(path) if (self.vertex().block): brush.setStyle(qt.QtCore.Qt.BDiagPattern) painter.setBrush(brush) painter.drawPath(path) ################ # Qt Overloads # ################ def itemChange(self, change, value): if change == qt.QtGui.QGraphicsItem.ItemSelectedChange: selected = bool(value) pen = self.pen() brush = self.brush() gradient = brush.gradient() if selected: pen.setColor(self.default_pen_selected_color) if gradient: # invert the gradient gradient.setColorAt(self.endPos, self.__topColor) gradient.setColorAt(self.startPos, self.__bottomColor) scene = self.scene() scene.focusedItemChanged.emit(scene, self) else: pen.setColor(self.default_pen_color) if gradient: # invert the gradient gradient.setColorAt(self.startPos, self.__topColor) gradient.setColorAt(self.endPos, self.__bottomColor) self.setPen(pen) self.setBrush(brush) qtgraphview.Vertex.itemChange(self, change, value) return qt.QtGui.QGraphicsRectItem.itemChange(self, change, value) mousePressEvent = mixin_method(qtgraphview.Vertex, qt.QtGui.QGraphicsRectItem, "mousePressEvent")
class SimpleVertex(QtGui.QGraphicsEllipseItem, Vertex): __vertex_size__ = QtCore.QSizeF(30.0, 30.0) __border_size__ = 5 def __init__(self, vertex, graph, parent=None): QtGui.QGraphicsEllipseItem.__init__(self, 0.0, 0.0, self.__vertex_size__.width(), self.__vertex_size__.height(), parent) Vertex.__init__(self, vertex, graph) self.setZValue(1.0) #we choose the avocado colors self.setBrush(QtGui.QBrush(QtCore.Qt.yellow)) pen = QtGui.QPen(QtCore.Qt.darkGreen) pen.setWidth(self.__border_size__ - 2) self.setPen(pen) self.initialise_from_model() ################## # QtWorld-Layout # ################## def size(self): return self.__vertex_size__ def sizeHint(self, blop, blip): return self.__vertex_size__ ################## # QtWorld-Events # ################## mousePressEvent = mixin_method(Vertex, QtGui.QGraphicsEllipseItem, "mousePressEvent") itemChange = mixin_method(Vertex, QtGui.QGraphicsEllipseItem, "itemChange") def contextMenuEvent(self, event): #called on right click on the vertex. menu = QtGui.QMenu(event.widget()) action = menu.addAction("Delete vertex") action.connect(action, QtCore.SIGNAL("triggered()"), self.remove) menu.popup(event.screenPos()) event.accept() def remove(self): self.graph().remove_vertex(self.vertex()) def paint(self, painter, painterOptions, widget): QtGui.QGraphicsEllipseItem.paint(self, painter, painterOptions, widget) def store_view_data(self, key, value, notify=True): self.vertex().get_ad_hoc_dict().set_metadata(key, value, notify) def get_view_data(self, key): return self.vertex().get_ad_hoc_dict().get_metadata(key) def announce_view_data(self, exclusive=False): if not exclusive: self.vertex().get_ad_hoc_dict().simulate_full_data_change() else: self.vertex().exclusive_command( exclusive, self.vertex().get_ad_hoc_dict().simulate_full_data_change)