class PreXoverItem(QGraphicsRectItem): """A PreXoverItem exists between a single 'from' VirtualHelixItem index and zero or more 'to' VirtualHelixItem Indices Attributes: adapter (TYPE): Description idx (int): the base index within the virtual helix is_fwd (TYPE): Description prexoveritemgroup (TYPE): Description to_vh_id_num (TYPE): Description """ def __init__(self, from_virtual_helix_item, is_fwd, from_index, to_vh_id_num, prexoveritemgroup, color): """Summary Args: from_virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_fwd (TYPE): Description from_index (TYPE): Description to_vh_id_num (TYPE): Description prexoveritemgroup (TYPE): Description color (TYPE): Description """ super(QGraphicsRectItem, self).__init__(BASE_RECT, from_virtual_helix_item) self.adapter = PropertyWrapperObject(self) self._bond_item = QGraphicsPathItem(self) self._bond_item.hide() self._label = PreXoverLabel(is_fwd, color, self) self._phos_item = Triangle(FWDPHOS_PP, self) self.setPen(getNoPen()) self.resetItem(from_virtual_helix_item, is_fwd, from_index, to_vh_id_num, prexoveritemgroup, color) # end def def shutdown(self): """Summary Returns: TYPE: Description """ self.setBrush(getBrushObj(self._color, alpha=0)) self.to_vh_id_num = None self.adapter.resetAnimations() phos = self._phos_item phos.adapter.resetAnimations() phos.resetTransform() phos.setPos(0, 0) self.setAcceptHoverEvents(False) self.setFlag(KEYINPUT_ACTIVE_FLAG, False) self.hide() # end def def resetItem(self, from_virtual_helix_item, is_fwd, from_index, to_vh_id_num, prexoveritemgroup, color): """Summary Args: from_virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_fwd (TYPE): Description from_index (TYPE): Description to_vh_id_num (TYPE): Description prexoveritemgroup (TYPE): Description color (TYPE): Description Returns: TYPE: Description """ self.setParentItem(from_virtual_helix_item) self.resetTransform() self._id_num = from_virtual_helix_item.idNum() self.idx = from_index self.is_fwd = is_fwd self.to_vh_id_num = to_vh_id_num self._color = color self.prexoveritemgroup = prexoveritemgroup self._bond_item.hide() self._label_txt = lbt = None if to_vh_id_num is None else str( to_vh_id_num) self.setLabel(text=lbt) self._label.resetItem(is_fwd, color) phos = self._phos_item bonditem = self._bond_item if is_fwd: phos.setPath(FWDPHOS_PP) phos.setTransformOriginPoint(0, phos.boundingRect().center().y()) phos.setPos(0.5 * BASE_WIDTH, BASE_WIDTH) phos.setPen(getNoPen()) phos.setBrush(getBrushObj(color)) bonditem.setPen(getPenObj(color, styles.PREXOVER_STROKE_WIDTH)) self.setPos(from_index * BASE_WIDTH, -BASE_WIDTH) else: phos.setPath(REVPHOS_PP) phos.setTransformOriginPoint(0, phos.boundingRect().center().y()) phos.setPos(0.5 * BASE_WIDTH, 0) phos.setPen(getPenObj(color, 0.25)) phos.setBrush(getNoBrush()) bonditem.setPen( getPenObj(color, styles.PREXOVER_STROKE_WIDTH, penstyle=Qt.DotLine, capstyle=Qt.RoundCap)) self.setPos(from_index * BASE_WIDTH, 2 * BASE_WIDTH) if to_vh_id_num is not None: inactive_alpha = PROX_ALPHA self.setBrush(getBrushObj(color, alpha=inactive_alpha)) else: self.setBrush(getBrushObj(color, alpha=0)) self.show() # end def def getInfo(self): """ Returns: Tuple: (from_id_num, is_fwd, from_index, to_vh_id_num) """ return (self._id_num, self.is_fwd, self.idx, self.to_vh_id_num) ### ACCESSORS ### def color(self): """Summary Returns: TYPE: Description """ return self._color def remove(self): """Summary Returns: TYPE: Description """ scene = self.scene() self.adapter.destroy() if scene: scene.removeItem(self._label) self._label = None self._phos_item.adapter.resetAnimations() self._phos_item.adapter = None scene.removeItem(self._phos_item) self._phos_item = None scene.removeItem(self._bond_item) self._bond_item = None self.adapter.resetAnimations() self.adapter = None scene.removeItem(self) # end defS ### EVENT HANDLERS ### def hoverEnterEvent(self, event): """Only if enableActive(True) is called hover and key events disabled by default Args: event (TYPE): Description """ self.setFocus(Qt.MouseFocusReason) self.prexoveritemgroup.updateModelActiveBaseInfo(self.getInfo()) self.setActiveHovered(True) status_string = "%d[%d]" % (self._id_num, self.idx) self.parentItem().window().statusBar().showMessage(status_string) # end def def hoverLeaveEvent(self, event): """Summary Args: event (TYPE): Description Returns: TYPE: Description """ self.prexoveritemgroup.updateModelActiveBaseInfo(None) self.setActiveHovered(False) self.clearFocus() self.parentItem().window().statusBar().showMessage("") # end def def keyPressEvent(self, event): """Summary Args: event (TYPE): Description Returns: TYPE: Description """ self.prexoveritemgroup.handlePreXoverKeyPress(event.key()) # end def ### PUBLIC SUPPORT METHODS ### def setLabel(self, text=None, outline=False): """Summary Args: text (None, optional): Description outline (bool, optional): Description Returns: TYPE: Description """ if text: self._label.setTextAndStyle(text=text, outline=outline) self._label.show() else: self._label.hide() # end def def animate(self, item, property_name, duration, start_value, end_value): """Summary Args: item (TYPE): Description property_name (TYPE): Description duration (TYPE): Description start_value (TYPE): Description end_value (TYPE): Description Returns: TYPE: Description """ b_name = property_name.encode('ascii') anim = item.adapter.getRef(property_name) if anim is None: anim = QPropertyAnimation(item.adapter, b_name) item.adapter.saveRef(property_name, anim) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start() # end def def setActiveHovered(self, is_active): """Rotate phosphate Triangle if `self.to_vh_id_num` is not `None` Args: is_active (bool): whether or not the PreXoverItem is parented to the active VirtualHelixItem """ if is_active: self.setBrush(getBrushObj(self._color, alpha=128)) self.animate(self, 'brush_alpha', 1, 0, 128) # overwrite running anim # if self.to_vh_id_num is not None: self.animate(self._phos_item, 'rotation', 500, 0, -90) else: inactive_alpha = 0 if self.to_vh_id_num is None else PROX_ALPHA self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 1000, 128, inactive_alpha) self.animate(self._phos_item, 'rotation', 500, -90, 0) # end def def enableActive(self, is_active, to_vh_id_num=None): """Call on PreXoverItems created on the active VirtualHelixItem Args: is_active (TYPE): Description to_vh_id_num (None, optional): Description """ if is_active: self.to_vh_id_num = to_vh_id_num self.setAcceptHoverEvents(True) if to_vh_id_num is None: self.setLabel(text=None) self.setBrush(getBrushObj(self._color, alpha=0)) else: self.setLabel(text=str(to_vh_id_num)) inactive_alpha = PROX_ALPHA self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 1000, 128, inactive_alpha) self.setFlag(KEYINPUT_ACTIVE_FLAG, True) else: self.setBrush(getNoBrush()) # self.setLabel(text=None) self.setAcceptHoverEvents(False) self.setFlag(KEYINPUT_ACTIVE_FLAG, False) def activateNeighbor(self, active_prexoveritem, shortcut=None): """To be called with whatever the active_prexoveritem is for the parts `active_base` Args: active_prexoveritem (TYPE): Description shortcut (None, optional): Description """ p1 = self._phos_item.scenePos() p2 = active_prexoveritem._phos_item.scenePos() scale = 3 delta1 = -BASE_WIDTH * scale if self.is_fwd else BASE_WIDTH * scale delta2 = BASE_WIDTH * scale if active_prexoveritem.is_fwd else -BASE_WIDTH * scale c1 = self.mapFromScene(QPointF(p1.x(), p1.y() + delta1)) c2 = self.mapFromScene(QPointF(p2.x(), p2.y() - delta2)) pp = QPainterPath() pp.moveTo(self._phos_item.pos()) pp.cubicTo(c1, c2, self._bond_item.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show() alpha = 32 idx, active_idx = self.idx, active_prexoveritem.idx if self.is_fwd != active_prexoveritem.is_fwd: if idx == active_idx: alpha = 255 elif idx == active_idx + 1: alpha = 255 elif idx == active_idx - 1: alpha = 255 inactive_alpha = PROX_ALPHA if self.to_vh_id_num is not None else 0 self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 500, inactive_alpha, alpha) self.animate(self._phos_item, 'rotation', 500, 0, -90) self.setLabel(text=shortcut, outline=True) # end def def deactivateNeighbor(self): """Summary Returns: TYPE: Description """ if self.isVisible(): inactive_alpha = PROX_ALPHA if self.to_vh_id_num is not None else 0 self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 1000, 128, inactive_alpha) self.animate(self._phos_item, 'rotation', 500, -90, 0) self._bond_item.hide() self.setLabel(text=self._label_txt)
class PreXoverItem(QGraphicsRectItem): """A PreXoverItem exists between a single 'from' VirtualHelixItem index and zero or more 'to' VirtualHelixItem Indices Attributes: adapter (:obj:`PropertyWrapperObject`): Description idx (int): the base index within the virtual helix is_fwd (bool): is this a forward strand? prexoveritem_manager (:obj:`PreXoverManager`): Manager of the PreXoverItems to_vh_id_num (int): Virtual Helix number this Xover point might connect to """ FILTER_NAME = "xover" def __init__(self, from_virtual_helix_item: PathVirtualHelixItemT, is_fwd: bool, from_index: int, nearby_idxs: List[int], to_vh_id_num: int, prexoveritem_manager: PreXoverManagerT): """Summary Args: from_virtual_helix_item: Description is_fwd: is this a forward strand? from_index: index of the Virtual Helix this xover is coming from nearby_idxs: to_vh_id_num: Virtual Helix number this Xover point might connect to prexoveritem_manager: Manager of the PreXoverItems """ super(QGraphicsRectItem, self).__init__(BASE_RECT, from_virtual_helix_item) self.adapter = PropertyWrapperObject(self) self._tick_marks = QGraphicsPathItem(self) self._tick_marks.setAcceptHoverEvents(True) self._bond_item = QGraphicsPathItem(self) self._bond_item.hide() self._label = PreXoverLabel(is_fwd, self) self._path = QGraphicsPathItem() self.setZValue(styles.ZPREXOVERITEM) self.setPen(getNoPen()) self.resetItem(from_virtual_helix_item, is_fwd, from_index, nearby_idxs, to_vh_id_num, prexoveritem_manager) self._getActiveTool = from_virtual_helix_item.viewroot().manager.activeToolGetter # end def def shutdown(self): """Summary """ self.setBrush(getNoBrush()) self.to_vh_id_num = None self.adapter.resetAnimations() self.setAcceptHoverEvents(False) self.hide() # end def def resetItem(self, from_virtual_helix_item: PathVirtualHelixItemT, is_fwd: bool, from_index: int, nearby_idxs: List[int], to_vh_id_num: int, prexoveritem_manager: PreXoverManagerT): """Update this pooled PreXoverItem with current info. Called by PreXoverManager. Args: from_virtual_helix_item: the associated vh_item is_fwd: True if associated with fwd strand, False if rev strand from_index: idx of associated vh nearby_idxs: to_vh_id_num: id_num of the other vh prexoveritem_manager: the manager """ # to_vh_item = from_virtual_helix_item.partItem().idToVirtualHelixItem(to_vh_id_num) self.setParentItem(from_virtual_helix_item) # self.setParentItem(to_vh_item) self.resetTransform() self._id_num = from_virtual_helix_item.idNum() self._model_part = from_virtual_helix_item.part() self.idx = from_index self.is_low = False self.is_high = False self.nearby_idxs = nearby_idxs self.is_fwd = is_fwd self.color = None self.is3p = None self.enter_pos = None self.exit_pos = None self.to_vh_id_num = to_vh_id_num self._label_txt = None self.prexoveritem_manager = prexoveritem_manager # todo: check here if xover present and disable result = self.setPathAppearance(from_virtual_helix_item) if result: self.setBrush(getNoBrush()) if is_fwd: self.setPos(from_index*BASE_WIDTH, -BASE_WIDTH - 0.1*BASE_WIDTH) else: self.setPos(from_index*BASE_WIDTH, 2*BASE_WIDTH) self.show() # label self._label_txt = lbt = None if to_vh_id_num is None else str(to_vh_id_num) self._label.resetItem(is_fwd, self.color) self.setLabel(text=lbt) # bond line bonditem = self._bond_item bonditem.setPen(getPenObj( self.color, styles.PREXOVER_STROKE_WIDTH, penstyle=Qt.DotLine)) bonditem.hide() # end def def setPathAppearance(self, from_virtual_helix_item: PathVirtualHelixItemT) -> bool: """Sets the PainterPath according to the index (low = Left, high = Right) and strand position (top = Up, bottom = Down). Args: from_virtual_helix_item: """ part = self._model_part idx = self.idx is_fwd = self.is_fwd id_num = self._id_num strand_type = StrandEnum.FWD if is_fwd else StrandEnum.REV # relative position info bpr = from_virtual_helix_item.getProperty('bases_per_repeat') self.is_low = is_low = idx+1 in self.nearby_idxs or (idx+1) % bpr in self.nearby_idxs self.is_high = is_high = idx-1 in self.nearby_idxs or (idx-1) % bpr in self.nearby_idxs # check strand for xover and color if part.hasStrandAtIdx(id_num, idx)[strand_type]: strand = part.getStrand(self.is_fwd, id_num, idx) if strand.hasXoverAt(idx): return False self.color = strand.getColor() if strand is not None else EMPTY_COL else: self.color = EMPTY_COL if is_low and is_high: path = (_FWD_DUAL_PATH, _REV_DUAL_PATH)[strand_type] raise NotImplementedError("Dual xovers not yet supported") elif is_low: path = (_FWD_LO_PATH, _REV_LO_PATH)[strand_type] self.is3p = True if is_fwd else False self.enter_pos = _FWD_LO_PTS[0][-1] if is_fwd else _REV_LO_PTS[0][-1] self.exit_pos = _FWD_LO_PTS[0][-1] if is_fwd else _REV_LO_PTS[0][-1] elif is_high: path = (_FWD_HI_PATH, _REV_HI_PATH)[strand_type] self.is3p = False if is_fwd else True self.enter_pos = _FWD_HI_PTS[0][-1] if is_fwd else _REV_HI_PTS[0][-1] self.exit_pos = _FWD_HI_PTS[0][-1] if is_fwd else _REV_HI_PTS[0][-1] else: # print("unpaired PreXoverItem at {}[{}]".format(self._id_num, self.idx), self.nearby_idxs) return False self._tick_marks.setPen(getPenObj( self.color, styles.PREXOVER_STROKE_WIDTH, capstyle=Qt.FlatCap, joinstyle=Qt.RoundJoin)) self._tick_marks.setPath(path) self._tick_marks.show() return True # end def ### ACCESSORS ### def color(self) -> str: """The PreXoverItem's color, derived from the associated strand's oligo. Returns: str: color in hex code """ return self.color def getInfo(self) -> ABInfoT: """ Returns: Tuple: (from_id_num, is_fwd, from_index, to_vh_id_num) """ return (self._id_num, self.is_fwd, self.idx, self.to_vh_id_num) def destroyItem(self): """Removes animation adapter, label, bond_item, and this item from scene. """ scene = self.scene() self.adapter.destroyItem() if scene: scene.removeItem(self._label) self._label = None scene.removeItem(self._bond_item) self._bond_item = None self.adapter.resetAnimations() self.adapter = None scene.removeItem(self) # end defS ### EVENT HANDLERS ### def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): """Only ``if enableActive(True)`` is called hover and key events disabled by default Args: event: the hover event """ if self._getActiveTool().methodPrefix() != "selectTool": return self.setFocus(Qt.MouseFocusReason) self.prexoveritem_manager.updateModelActiveBaseInfo(self.getInfo()) self.setActiveHovered(True) status_string = "%d[%d]" % (self._id_num, self.idx) self.parentItem().window().statusBar().showMessage(status_string) return QGraphicsItem.hoverEnterEvent(self, event) # end def def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): """Summary Args: event (QGraphicsSceneHoverEvent): the hover event """ self.prexoveritem_manager.updateModelActiveBaseInfo(None) self.setActiveHovered(False) self.clearFocus() self.parentItem().window().statusBar().showMessage("") return QGraphicsItem.hoverLeaveEvent(self, event) # end def def mousePressEvent(self, event: QGraphicsSceneMouseEvent): """TODO: NEED TO ADD FILTER FOR A CLICK ON THE 3' MOST END OF THE XOVER TO DISALLOW OR HANDLE DIFFERENTLY """ viewroot = self.parentItem().viewroot() current_filter_set = viewroot.selectionFilterSet() if (self._getActiveTool().methodPrefix() != "selectTool" or (self.FILTER_NAME not in current_filter_set) ): return part = self._model_part is_fwd = self.is_fwd if self.is3p: strand5p = part.getStrand(is_fwd, self._id_num, self.idx) strand3p = part.getStrand(not is_fwd, self.to_vh_id_num, self.idx) else: strand5p = part.getStrand(not is_fwd, self.to_vh_id_num, self.idx) strand3p = part.getStrand(is_fwd, self._id_num, self.idx) if strand5p is None or strand3p is None: return # print(strand3p, strand5p) part.createXover(strand5p, self.idx, strand3p, self.idx) nkey = (self.to_vh_id_num, not is_fwd, self.idx) npxi = self.prexoveritem_manager.neighbor_prexover_items.get(nkey, None) if npxi: npxi.shutdown() self.shutdown() part.setActiveVirtualHelix(self._id_num, is_fwd, self.idx) # self.prexoveritem_manager.handlePreXoverClick(self) def keyPressEvent(self, event: QKeyEvent): """ Args: event: Description """ self.prexoveritem_manager.handlePreXoverKeyPress(event.key()) # end def ### PUBLIC SUPPORT METHODS ### def setLabel(self, text: str = None, outline: bool = False): """Summary Args: text: Default is ``None`` outline: Default is ``False`` """ if text: self._label.setTextAndStyle(text=text, outline=outline) self._label.show() else: self._label.hide() # end def def animate(self, item: QGraphicsItem, property_name: str, duration: int, start_value, end_value): """ Args: item: Description property_name: Description duration: Description start_value (QVariant): Description end_value (QVariant): Description """ b_name = property_name.encode('ascii') anim = item.adapter.getRef(property_name) if anim is None: anim = QPropertyAnimation(item.adapter, b_name) item.adapter.saveRef(property_name, anim) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start() # end def def setActiveHovered(self, is_active: bool): """Rotate phosphate Triangle if `self.to_vh_id_num` is not `None` Args: is_active: whether or not the PreXoverItem is parented to the active VirtualHelixItem """ pass # end def def enableActive(self, is_active: bool, to_vh_id_num: int = None): """Call on PreXoverItems created on the active VirtualHelixItem Args: is_active: Description to_vh_id_num: Default is ``None`` """ if is_active: self.to_vh_id_num = to_vh_id_num self.setAcceptHoverEvents(True) if to_vh_id_num is None: self.setLabel(text=None) else: self.setLabel(text=str(to_vh_id_num)) else: self.setBrush(getNoBrush()) self.setAcceptHoverEvents(False) def activateNeighbor(self, active_prexoveritem: 'PreXoverItem', shortcut: str = None): """Draws a quad line starting from the item5p to the item3p. To be called with whatever the active_prexoveritem is for the parts `active_base`. Args: active_prexoveritem: Description shortcut: Default is None """ if self._getActiveTool().methodPrefix() != "selectTool": return if self.is3p and not active_prexoveritem.is3p: item5p = active_prexoveritem item3p = self elif not self.is3p and active_prexoveritem.is3p: item5p = self item3p = active_prexoveritem else: return same_parity = self.is_fwd == active_prexoveritem.is_fwd p1 = item5p._tick_marks.scenePos() + item5p.exit_pos p2 = item3p._tick_marks.scenePos() + item3p.exit_pos c1 = QPointF() # case 1: same parity if same_parity: dy = abs(p2.y() - p1.y()) c1.setX(p1.x() + _X_SCALE * dy) c1.setY(0.5 * (p1.y() + p2.y())) # case 2: different parity else: if item3p.is_fwd: c1.setX(p1.x() - _X_SCALE * abs(p2.y() - p1.y())) else: c1.setX(p1.x() + _X_SCALE * abs(p2.y() - p1.y())) c1.setY(0.5 * (p1.y() + p2.y())) pp = QPainterPath() pp.moveTo(self._tick_marks.mapFromScene(p1)) pp.quadTo(self._tick_marks.mapFromScene(c1), self._tick_marks.mapFromScene(p2)) # pp.cubicTo(c1, c2, self._tick_marks.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show() # end def def deactivateNeighbor(self): """Summary """ if self.isVisible(): self._bond_item.hide() self.setLabel(text=self._label_txt)
class PreXoverItem(QGraphicsRectItem): """A PreXoverItem exists between a single 'from' VirtualHelixItem index and zero or more 'to' VirtualHelixItem Indices Attributes: adapter (TYPE): Description idx (int): the base index within the virtual helix is_fwd (TYPE): Description prexoveritem_manager (TYPE): Description to_vh_id_num (TYPE): Description """ def __init__(self, from_virtual_helix_item, is_fwd, from_index, nearby_idxs, to_vh_id_num, prexoveritem_manager): """Summary Args: from_virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_fwd (TYPE): Description from_index (TYPE): Description to_vh_id_num (TYPE): Description prexoveritem_manager (TYPE): Description """ super(QGraphicsRectItem, self).__init__(BASE_RECT, from_virtual_helix_item) self.adapter = PropertyWrapperObject(self) self._tick_marks = QGraphicsPathItem(self) self._tick_marks.setAcceptHoverEvents(True) self._bond_item = QGraphicsPathItem(self) self._bond_item.hide() self._label = PreXoverLabel(is_fwd, self) self._path = QGraphicsPathItem() self.setZValue(styles.ZPREXOVERITEM) self.setPen(getNoPen()) self.resetItem(from_virtual_helix_item, is_fwd, from_index, nearby_idxs, to_vh_id_num, prexoveritem_manager) # end def def shutdown(self): """Summary Returns: TYPE: Description """ self.setBrush(getNoBrush()) self.to_vh_id_num = None self.adapter.resetAnimations() self.setAcceptHoverEvents(False) self.hide() # end def def resetItem(self, from_virtual_helix_item, is_fwd, from_index, nearby_idxs, to_vh_id_num, prexoveritem_manager): """Update this pooled PreXoverItem with current info. Called by PreXoverManager. Args: from_virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): the associated vh_item is_fwd (bool): True if associated with fwd strand, False if rev strand from_index (int): idx of associated vh to_vh_id_num (int): id_num of the other vh prexoveritem_manager (cadnano.views.pathview.prexoermanager.PreXoverManager): the manager """ self.setParentItem(from_virtual_helix_item) self.resetTransform() self._id_num = from_virtual_helix_item.idNum() self._model_vh = from_virtual_helix_item.cnModel() self.idx = from_index self.is_low = False self.is_high = False self.nearby_idxs = nearby_idxs self.is_fwd = is_fwd self.color = None self.is3p = None self.enter_pos = None self.exit_pos = None self.to_vh_id_num = to_vh_id_num self.prexoveritem_manager = prexoveritem_manager # todo: check here if xover present and disable result = self.setPathAppearance(from_virtual_helix_item) if result: self.setBrush(getNoBrush()) if is_fwd: self.setPos(from_index*BASE_WIDTH, -BASE_WIDTH) else: self.setPos(from_index*BASE_WIDTH, 2*BASE_WIDTH) self.show() # label self._label_txt = lbt = None if to_vh_id_num is None else str(to_vh_id_num) self.setLabel(text=lbt) self._label.resetItem(is_fwd, self.color) # bond line bonditem = self._bond_item bonditem.setPen(getPenObj(self.color, styles.PREXOVER_STROKE_WIDTH)) bonditem.hide() # end def def setPathAppearance(self, from_virtual_helix_item): """ Sets the PainterPath according to the index (low = Left, high = Right) and strand position (top = Up, bottom = Down). """ part = self._model_vh.part() idx = self.idx is_fwd = self.is_fwd id_num = self._id_num strand_type = StrandType.FWD if is_fwd else StrandType.REV # relative position info bpr = from_virtual_helix_item.getProperty('bases_per_repeat') self.is_low = is_low = idx+1 in self.nearby_idxs or (idx+1) % bpr in self.nearby_idxs self.is_high = is_high = idx-1 in self.nearby_idxs or (idx-1) % bpr in self.nearby_idxs # check strand for xover and color if part.hasStrandAtIdx(id_num, idx)[strand_type]: strand = part.getStrand(self.is_fwd, id_num, idx) if strand.hasXoverAt(idx): return False self.color = strand.getColor() if strand is not None else EMPTY_COL else: self.color = EMPTY_COL if is_low and is_high: print("dual xover") path = (_FWD_DUAL_PATH, _REV_DUAL_PATH)[strand_type] elif is_low: path = (_FWD_LO_PATH, _REV_LO_PATH)[strand_type] self.is3p = True if is_fwd else False self.enter_pos = _FWD_LO_PTS[0][-1] if is_fwd else _REV_LO_PTS[0][-1] self.exit_pos = _FWD_LO_PTS[0][-1] if is_fwd else _REV_LO_PTS[0][-1] elif is_high: path = (_FWD_HI_PATH, _REV_HI_PATH)[strand_type] self.is3p = False if is_fwd else True self.enter_pos = _FWD_HI_PTS[0][-1] if is_fwd else _REV_HI_PTS[0][-1] self.exit_pos = _FWD_HI_PTS[0][-1] if is_fwd else _REV_HI_PTS[0][-1] else: # print("unpaired PreXoverItem at {}[{}]".format(self._id_num, self.idx), self.nearby_idxs) return False self._tick_marks.setPen(getPenObj(self.color, styles.PREXOVER_STROKE_WIDTH, capstyle=Qt.FlatCap, joinstyle=Qt.RoundJoin)) self._tick_marks.setPath(path) self._tick_marks.show() return True # end def ### ACCESSORS ### def color(self): """The PreXoverItem's color, derived from the associated strand's oligo. Returns: str: color in hex code """ return self.color def getInfo(self): """ Returns: Tuple: (from_id_num, is_fwd, from_index, to_vh_id_num) """ return (self._id_num, self.is_fwd, self.idx, self.to_vh_id_num) def remove(self): """Removes animation adapter, label, bond_item, and this item from scene. """ scene = self.scene() self.adapter.destroy() if scene: scene.removeItem(self._label) self._label = None scene.removeItem(self._bond_item) self._bond_item = None self.adapter.resetAnimations() self.adapter = None scene.removeItem(self) # end defS ### EVENT HANDLERS ### def hoverEnterEvent(self, event): """Only if enableActive(True) is called hover and key events disabled by default Args: event (QGraphicsSceneHoverEvent): the hover event """ self.setFocus(Qt.MouseFocusReason) self.prexoveritem_manager.updateModelActiveBaseInfo(self.getInfo()) self.setActiveHovered(True) status_string = "%d[%d]" % (self._id_num, self.idx) self.parentItem().window().statusBar().showMessage(status_string) # end def def hoverLeaveEvent(self, event): """Summary Args: event (QGraphicsSceneHoverEvent): the hover event Returns: TYPE: Description """ self.prexoveritem_manager.updateModelActiveBaseInfo(None) self.setActiveHovered(False) self.clearFocus() self.parentItem().window().statusBar().showMessage("") # end def def mousePressEvent(self, event): part = self._model_vh.part() is_fwd = self.is_fwd if self.is3p: strand5p = part.getStrand(is_fwd, self._id_num, self.idx) strand3p = part.getStrand(not is_fwd, self.to_vh_id_num, self.idx) else: strand5p = part.getStrand(not is_fwd, self.to_vh_id_num, self.idx) strand3p = part.getStrand(is_fwd, self._id_num, self.idx) if strand5p is None or strand3p is None: return part.createXover(strand5p, self.idx, strand3p, self.idx) nkey = (self.to_vh_id_num, not is_fwd, self.idx) npxi = self.prexoveritem_manager.neighbor_prexover_items.get(nkey, None) if npxi: npxi.shutdown() self.shutdown() part.setActiveVirtualHelix(self._id_num, is_fwd, self.idx) # self.prexoveritem_manager.handlePreXoverClick(self) def keyPressEvent(self, event): """Summary Args: event (TYPE): Description Returns: TYPE: Description """ self.prexoveritem_manager.handlePreXoverKeyPress(event.key()) # end def ### PUBLIC SUPPORT METHODS ### def setLabel(self, text=None, outline=False): """Summary Args: text (None, optional): Description outline (bool, optional): Description Returns: TYPE: Description """ if text: self._label.setTextAndStyle(text=text, outline=outline) self._label.show() else: self._label.hide() # end def def animate(self, item, property_name, duration, start_value, end_value): """Summary Args: item (TYPE): Description property_name (TYPE): Description duration (TYPE): Description start_value (TYPE): Description end_value (TYPE): Description Returns: TYPE: Description """ b_name = property_name.encode('ascii') anim = item.adapter.getRef(property_name) if anim is None: anim = QPropertyAnimation(item.adapter, b_name) item.adapter.saveRef(property_name, anim) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start() # end def def setActiveHovered(self, is_active): """Rotate phosphate Triangle if `self.to_vh_id_num` is not `None` Args: is_active (bool): whether or not the PreXoverItem is parented to the active VirtualHelixItem """ pass # end def def enableActive(self, is_active, to_vh_id_num=None): """Call on PreXoverItems created on the active VirtualHelixItem Args: is_active (TYPE): Description to_vh_id_num (None, optional): Description """ if is_active: self.to_vh_id_num = to_vh_id_num self.setAcceptHoverEvents(True) if to_vh_id_num is None: self.setLabel(text=None) else: self.setLabel(text=str(to_vh_id_num)) else: self.setBrush(getNoBrush()) self.setAcceptHoverEvents(False) def activateNeighbor(self, active_prexoveritem, shortcut=None): """ Draws a quad line starting from the item5p to the item3p. To be called with whatever the active_prexoveritem is for the parts `active_base`. Args: active_prexoveritem (TYPE): Description shortcut (None, optional): Description """ if self.is3p and not active_prexoveritem.is3p: item5p = active_prexoveritem item3p = self elif not self.is3p and active_prexoveritem.is3p: item5p = self item3p = active_prexoveritem else: return same_parity = self.is_fwd == active_prexoveritem.is_fwd p1 = item5p._tick_marks.scenePos() + item5p.exit_pos p2 = item3p._tick_marks.scenePos() + item3p.exit_pos c1 = QPointF() # case 1: same parity if same_parity: dy = abs(p2.y() - p1.y()) c1.setX(p1.x() + _X_SCALE * dy) c1.setY(0.5 * (p1.y() + p2.y())) # case 2: different parity else: if item3p.is_fwd: c1.setX(p1.x() - _X_SCALE * abs(p2.y() - p1.y())) else: c1.setX(p1.x() + _X_SCALE * abs(p2.y() - p1.y())) c1.setY(0.5 * (p1.y() + p2.y())) pp = QPainterPath() pp.moveTo(self._tick_marks.mapFromScene(p1)) pp.quadTo(self._tick_marks.mapFromScene(c1), self._tick_marks.mapFromScene(p2)) # pp.cubicTo(c1, c2, self._tick_marks.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show() # end def def deactivateNeighbor(self): """Summary Returns: TYPE: Description """ if self.isVisible(): self._bond_item.hide() self.setLabel(text=self._label_txt)
class PreXoverItem(QGraphicsRectItem): """A PreXoverItem exists between a single 'from' VirtualHelixItem index and zero or more 'to' VirtualHelixItem Indices Attributes: adapter (:obj:`PropertyWrapperObject`): Description idx (int): the base index within the virtual helix is_fwd (bool): is this a forward strand? prexoveritem_manager (:obj:`PreXoverManager`): Manager of the PreXoverItems to_vh_id_num (int): Virtual Helix number this Xover point might connect to """ FILTER_NAME = "xover" def __init__(self, from_virtual_helix_item: PathVirtualHelixItemT, is_fwd: bool, from_index: int, nearby_idxs: List[int], to_vh_id_num: int, prexoveritem_manager: PreXoverManagerT): """Summary Args: from_virtual_helix_item: Description is_fwd: is this a forward strand? from_index: index of the Virtual Helix this xover is coming from nearby_idxs: to_vh_id_num: Virtual Helix number this Xover point might connect to prexoveritem_manager: Manager of the PreXoverItems """ super(QGraphicsRectItem, self).__init__(BASE_RECT, from_virtual_helix_item) self.adapter = PropertyWrapperObject(self) self._tick_marks = QGraphicsPathItem(self) self._tick_marks.setAcceptHoverEvents(True) self._bond_item = QGraphicsPathItem(self) self._bond_item.hide() self._label = PreXoverLabel(is_fwd, self) self._path = QGraphicsPathItem() self.setZValue(styles.ZPREXOVERITEM) self.setPen(getNoPen()) self.resetItem(from_virtual_helix_item, is_fwd, from_index, nearby_idxs, to_vh_id_num, prexoveritem_manager) self._getActiveTool = from_virtual_helix_item.viewroot( ).manager.activeToolGetter # end def def shutdown(self): """Summary """ self.setBrush(getNoBrush()) self.to_vh_id_num = None self.adapter.resetAnimations() self.setAcceptHoverEvents(False) self.hide() # end def def resetItem(self, from_virtual_helix_item: PathVirtualHelixItemT, is_fwd: bool, from_index: int, nearby_idxs: List[int], to_vh_id_num: int, prexoveritem_manager: PreXoverManagerT): """Update this pooled PreXoverItem with current info. Called by PreXoverManager. Args: from_virtual_helix_item: the associated vh_item is_fwd: True if associated with fwd strand, False if rev strand from_index: idx of associated vh nearby_idxs: to_vh_id_num: id_num of the other vh prexoveritem_manager: the manager """ # to_vh_item = from_virtual_helix_item.partItem().idToVirtualHelixItem(to_vh_id_num) self.setParentItem(from_virtual_helix_item) # self.setParentItem(to_vh_item) self.resetTransform() self._id_num = from_virtual_helix_item.idNum() self._model_part = from_virtual_helix_item.part() self.idx = from_index self.is_low = False self.is_high = False self.nearby_idxs = nearby_idxs self.is_fwd = is_fwd self.color = None self.is3p = None self.enter_pos = None self.exit_pos = None self.to_vh_id_num = to_vh_id_num self._label_txt = None self.prexoveritem_manager = prexoveritem_manager # todo: check here if xover present and disable result = self.setPathAppearance(from_virtual_helix_item) if result: self.setBrush(getNoBrush()) if is_fwd: self.setPos(from_index * BASE_WIDTH, -BASE_WIDTH - 0.1 * BASE_WIDTH) else: self.setPos(from_index * BASE_WIDTH, 2 * BASE_WIDTH) self.show() # label self._label_txt = lbt = None if to_vh_id_num is None else str( to_vh_id_num) self._label.resetItem(is_fwd, self.color) self.setLabel(text=lbt) # bond line bonditem = self._bond_item bonditem.setPen( getPenObj(self.color, styles.PREXOVER_STROKE_WIDTH, penstyle=Qt.DotLine)) bonditem.hide() # end def def setPathAppearance( self, from_virtual_helix_item: PathVirtualHelixItemT) -> bool: """Sets the PainterPath according to the index (low = Left, high = Right) and strand position (top = Up, bottom = Down). Args: from_virtual_helix_item: """ part = self._model_part idx = self.idx is_fwd = self.is_fwd id_num = self._id_num strand_type = StrandEnum.FWD if is_fwd else StrandEnum.REV # relative position info bpr = from_virtual_helix_item.getProperty('bases_per_repeat') self.is_low = is_low = idx + 1 in self.nearby_idxs or ( idx + 1) % bpr in self.nearby_idxs self.is_high = is_high = idx - 1 in self.nearby_idxs or ( idx - 1) % bpr in self.nearby_idxs # check strand for xover and color if part.hasStrandAtIdx(id_num, idx)[strand_type]: strand = part.getStrand(self.is_fwd, id_num, idx) if strand.hasXoverAt(idx): return False self.color = strand.getColor() if strand is not None else EMPTY_COL else: self.color = EMPTY_COL if is_low and is_high: path = (_FWD_DUAL_PATH, _REV_DUAL_PATH)[strand_type] raise NotImplementedError("Dual xovers not yet supported") elif is_low: path = (_FWD_LO_PATH, _REV_LO_PATH)[strand_type] self.is3p = True if is_fwd else False self.enter_pos = _FWD_LO_PTS[0][-1] if is_fwd else _REV_LO_PTS[0][ -1] self.exit_pos = _FWD_LO_PTS[0][-1] if is_fwd else _REV_LO_PTS[0][-1] elif is_high: path = (_FWD_HI_PATH, _REV_HI_PATH)[strand_type] self.is3p = False if is_fwd else True self.enter_pos = _FWD_HI_PTS[0][-1] if is_fwd else _REV_HI_PTS[0][ -1] self.exit_pos = _FWD_HI_PTS[0][-1] if is_fwd else _REV_HI_PTS[0][-1] else: # print("unpaired PreXoverItem at {}[{}]".format(self._id_num, self.idx), self.nearby_idxs) return False self._tick_marks.setPen( getPenObj(self.color, styles.PREXOVER_STROKE_WIDTH, capstyle=Qt.FlatCap, joinstyle=Qt.RoundJoin)) self._tick_marks.setPath(path) self._tick_marks.show() return True # end def ### ACCESSORS ### def color(self) -> str: """The PreXoverItem's color, derived from the associated strand's oligo. Returns: str: color in hex code """ return self.color def getInfo(self) -> ABInfoT: """ Returns: Tuple: (from_id_num, is_fwd, from_index, to_vh_id_num) """ return (self._id_num, self.is_fwd, self.idx, self.to_vh_id_num) def destroyItem(self): """Removes animation adapter, label, bond_item, and this item from scene. """ scene = self.scene() self.adapter.destroyItem() if scene: scene.removeItem(self._label) self._label = None scene.removeItem(self._bond_item) self._bond_item = None self.adapter.resetAnimations() self.adapter = None scene.removeItem(self) # end defS ### EVENT HANDLERS ### def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): """Only ``if enableActive(True)`` is called hover and key events disabled by default Args: event: the hover event """ if self._getActiveTool().methodPrefix() != "selectTool": return self.setFocus(Qt.MouseFocusReason) self.prexoveritem_manager.updateModelActiveBaseInfo(self.getInfo()) self.setActiveHovered(True) status_string = "%d[%d]" % (self._id_num, self.idx) self.parentItem().window().statusBar().showMessage(status_string) return QGraphicsItem.hoverEnterEvent(self, event) # end def def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): """Summary Args: event (QGraphicsSceneHoverEvent): the hover event """ self.prexoveritem_manager.updateModelActiveBaseInfo(None) self.setActiveHovered(False) self.clearFocus() self.parentItem().window().statusBar().showMessage("") return QGraphicsItem.hoverLeaveEvent(self, event) # end def def mousePressEvent(self, event: QGraphicsSceneMouseEvent): """TODO: NEED TO ADD FILTER FOR A CLICK ON THE 3' MOST END OF THE XOVER TO DISALLOW OR HANDLE DIFFERENTLY """ viewroot = self.parentItem().viewroot() current_filter_set = viewroot.selectionFilterSet() if (self._getActiveTool().methodPrefix() != "selectTool" or (self.FILTER_NAME not in current_filter_set)): return part = self._model_part is_fwd = self.is_fwd if self.is3p: strand5p = part.getStrand(is_fwd, self._id_num, self.idx) strand3p = part.getStrand(not is_fwd, self.to_vh_id_num, self.idx) else: strand5p = part.getStrand(not is_fwd, self.to_vh_id_num, self.idx) strand3p = part.getStrand(is_fwd, self._id_num, self.idx) if strand5p is None or strand3p is None: return # print(strand3p, strand5p) part.createXover(strand5p, self.idx, strand3p, self.idx) nkey = (self.to_vh_id_num, not is_fwd, self.idx) npxi = self.prexoveritem_manager.neighbor_prexover_items.get( nkey, None) if npxi: npxi.shutdown() self.shutdown() part.setActiveVirtualHelix(self._id_num, is_fwd, self.idx) # self.prexoveritem_manager.handlePreXoverClick(self) def keyPressEvent(self, event: QKeyEvent): """ Args: event: Description """ self.prexoveritem_manager.handlePreXoverKeyPress(event.key()) # end def ### PUBLIC SUPPORT METHODS ### def setLabel(self, text: str = None, outline: bool = False): """Summary Args: text: Default is ``None`` outline: Default is ``False`` """ if text: self._label.setTextAndStyle(text=text, outline=outline) self._label.show() else: self._label.hide() # end def def animate(self, item: QGraphicsItem, property_name: str, duration: int, start_value, end_value): """ Args: item: Description property_name: Description duration: Description start_value (QVariant): Description end_value (QVariant): Description """ b_name = property_name.encode('ascii') anim = item.adapter.getRef(property_name) if anim is None: anim = QPropertyAnimation(item.adapter, b_name) item.adapter.saveRef(property_name, anim) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start() # end def def setActiveHovered(self, is_active: bool): """Rotate phosphate Triangle if `self.to_vh_id_num` is not `None` Args: is_active: whether or not the PreXoverItem is parented to the active VirtualHelixItem """ pass # end def def enableActive(self, is_active: bool, to_vh_id_num: int = None): """Call on PreXoverItems created on the active VirtualHelixItem Args: is_active: Description to_vh_id_num: Default is ``None`` """ if is_active: self.to_vh_id_num = to_vh_id_num self.setAcceptHoverEvents(True) if to_vh_id_num is None: self.setLabel(text=None) else: self.setLabel(text=str(to_vh_id_num)) else: self.setBrush(getNoBrush()) self.setAcceptHoverEvents(False) def activateNeighbor(self, active_prexoveritem: 'PreXoverItem', shortcut: str = None): """Draws a quad line starting from the item5p to the item3p. To be called with whatever the active_prexoveritem is for the parts `active_base`. Args: active_prexoveritem: Description shortcut: Default is None """ if self._getActiveTool().methodPrefix() != "selectTool": return if self.is3p and not active_prexoveritem.is3p: item5p = active_prexoveritem item3p = self elif not self.is3p and active_prexoveritem.is3p: item5p = self item3p = active_prexoveritem else: return same_parity = self.is_fwd == active_prexoveritem.is_fwd p1 = item5p._tick_marks.scenePos() + item5p.exit_pos p2 = item3p._tick_marks.scenePos() + item3p.exit_pos c1 = QPointF() # case 1: same parity if same_parity: dy = abs(p2.y() - p1.y()) c1.setX(p1.x() + _X_SCALE * dy) c1.setY(0.5 * (p1.y() + p2.y())) # case 2: different parity else: if item3p.is_fwd: c1.setX(p1.x() - _X_SCALE * abs(p2.y() - p1.y())) else: c1.setX(p1.x() + _X_SCALE * abs(p2.y() - p1.y())) c1.setY(0.5 * (p1.y() + p2.y())) pp = QPainterPath() pp.moveTo(self._tick_marks.mapFromScene(p1)) pp.quadTo(self._tick_marks.mapFromScene(c1), self._tick_marks.mapFromScene(p2)) # pp.cubicTo(c1, c2, self._tick_marks.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show() # end def def deactivateNeighbor(self): """Summary """ if self.isVisible(): self._bond_item.hide() self.setLabel(text=self._label_txt)
class PreXoverItem(QGraphicsRectItem): """A PreXoverItem exists between a single 'from' VirtualHelixItem index and zero or more 'to' VirtualHelixItem Indices Attributes: adapter (TYPE): Description idx (int): the base index within the virtual helix is_fwd (TYPE): Description prexoveritemgroup (TYPE): Description to_vh_id_num (TYPE): Description """ def __init__(self, from_virtual_helix_item, is_fwd, from_index, to_vh_id_num, prexoveritemgroup, color): """Summary Args: from_virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_fwd (TYPE): Description from_index (TYPE): Description to_vh_id_num (TYPE): Description prexoveritemgroup (TYPE): Description color (TYPE): Description """ super(QGraphicsRectItem, self).__init__(BASE_RECT, from_virtual_helix_item) self.adapter = PropertyWrapperObject(self) self._bond_item = QGraphicsPathItem(self) self._bond_item.hide() self._label = PreXoverLabel(is_fwd, color, self) self._phos_item = Triangle(FWDPHOS_PP, self) self.setPen(getNoPen()) self.resetItem(from_virtual_helix_item, is_fwd, from_index, to_vh_id_num, prexoveritemgroup, color) # end def def shutdown(self): """Summary Returns: TYPE: Description """ self.setBrush(getBrushObj(self._color, alpha=0)) self.to_vh_id_num = None self.adapter.resetAnimations() phos = self._phos_item phos.adapter.resetAnimations() phos.resetTransform() phos.setPos(0, 0) self.setAcceptHoverEvents(False) self.setFlag(KEYINPUT_ACTIVE_FLAG, False) self.hide() # end def def resetItem(self, from_virtual_helix_item, is_fwd, from_index, to_vh_id_num, prexoveritemgroup, color): """Summary Args: from_virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_fwd (TYPE): Description from_index (TYPE): Description to_vh_id_num (TYPE): Description prexoveritemgroup (TYPE): Description color (TYPE): Description Returns: TYPE: Description """ self.setParentItem(from_virtual_helix_item) self.resetTransform() self._id_num = from_virtual_helix_item.idNum() self.idx = from_index self.is_fwd = is_fwd self.to_vh_id_num = to_vh_id_num self._color = color self.prexoveritemgroup = prexoveritemgroup self._bond_item.hide() self._label_txt = lbt = None if to_vh_id_num is None else str(to_vh_id_num) self.setLabel(text=lbt) self._label.resetItem(is_fwd, color) phos = self._phos_item bonditem = self._bond_item if is_fwd: phos.setPath(FWDPHOS_PP) phos.setTransformOriginPoint(0, phos.boundingRect().center().y()) phos.setPos(0.5*BASE_WIDTH, BASE_WIDTH) phos.setPen(getNoPen()) phos.setBrush(getBrushObj(color)) bonditem.setPen(getPenObj(color, styles.PREXOVER_STROKE_WIDTH)) self.setPos(from_index*BASE_WIDTH, -BASE_WIDTH) else: phos.setPath(REVPHOS_PP) phos.setTransformOriginPoint(0, phos.boundingRect().center().y()) phos.setPos(0.5*BASE_WIDTH, 0) phos.setPen(getPenObj(color, 0.25)) phos.setBrush(getNoBrush()) bonditem.setPen(getPenObj(color, styles.PREXOVER_STROKE_WIDTH, penstyle=Qt.DotLine, capstyle=Qt.RoundCap)) self.setPos(from_index*BASE_WIDTH, 2*BASE_WIDTH) if to_vh_id_num is not None: inactive_alpha = PROX_ALPHA self.setBrush(getBrushObj(color, alpha=inactive_alpha)) else: self.setBrush(getBrushObj(color, alpha=0)) self.show() # end def def getInfo(self): """ Returns: Tuple: (from_id_num, is_fwd, from_index, to_vh_id_num) """ return (self._id_num, self.is_fwd, self.idx, self.to_vh_id_num) ### ACCESSORS ### def color(self): """Summary Returns: TYPE: Description """ return self._color def remove(self): """Summary Returns: TYPE: Description """ scene = self.scene() self.adapter.destroy() if scene: scene.removeItem(self._label) self._label = None self._phos_item.adapter.resetAnimations() self._phos_item.adapter = None scene.removeItem(self._phos_item) self._phos_item = None scene.removeItem(self._bond_item) self._bond_item = None self.adapter.resetAnimations() self.adapter = None scene.removeItem(self) # end defS ### EVENT HANDLERS ### def hoverEnterEvent(self, event): """Only if enableActive(True) is called hover and key events disabled by default Args: event (TYPE): Description """ self.setFocus(Qt.MouseFocusReason) self.prexoveritemgroup.updateModelActiveBaseInfo(self.getInfo()) self.setActiveHovered(True) status_string = "%d[%d]" % (self._id_num, self.idx) self.parentItem().window().statusBar().showMessage(status_string) # end def def hoverLeaveEvent(self, event): """Summary Args: event (TYPE): Description Returns: TYPE: Description """ self.prexoveritemgroup.updateModelActiveBaseInfo(None) self.setActiveHovered(False) self.clearFocus() self.parentItem().window().statusBar().showMessage("") # end def def keyPressEvent(self, event): """Summary Args: event (TYPE): Description Returns: TYPE: Description """ self.prexoveritemgroup.handlePreXoverKeyPress(event.key()) # end def ### PUBLIC SUPPORT METHODS ### def setLabel(self, text=None, outline=False): """Summary Args: text (None, optional): Description outline (bool, optional): Description Returns: TYPE: Description """ if text: self._label.setTextAndStyle(text=text, outline=outline) self._label.show() else: self._label.hide() # end def def animate(self, item, property_name, duration, start_value, end_value): """Summary Args: item (TYPE): Description property_name (TYPE): Description duration (TYPE): Description start_value (TYPE): Description end_value (TYPE): Description Returns: TYPE: Description """ b_name = property_name.encode('ascii') anim = item.adapter.getRef(property_name) if anim is None: anim = QPropertyAnimation(item.adapter, b_name) item.adapter.saveRef(property_name, anim) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start() # end def def setActiveHovered(self, is_active): """Rotate phosphate Triangle if `self.to_vh_id_num` is not `None` Args: is_active (bool): whether or not the PreXoverItem is parented to the active VirtualHelixItem """ if is_active: self.setBrush(getBrushObj(self._color, alpha=128)) self.animate(self, 'brush_alpha', 1, 0, 128) # overwrite running anim # if self.to_vh_id_num is not None: self.animate(self._phos_item, 'rotation', 500, 0, -90) else: inactive_alpha = 0 if self.to_vh_id_num is None else PROX_ALPHA self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 1000, 128, inactive_alpha) self.animate(self._phos_item, 'rotation', 500, -90, 0) # end def def enableActive(self, is_active, to_vh_id_num=None): """Call on PreXoverItems created on the active VirtualHelixItem Args: is_active (TYPE): Description to_vh_id_num (None, optional): Description """ if is_active: self.to_vh_id_num = to_vh_id_num self.setAcceptHoverEvents(True) if to_vh_id_num is None: self.setLabel(text=None) self.setBrush(getBrushObj(self._color, alpha=0)) else: self.setLabel(text=str(to_vh_id_num)) inactive_alpha = PROX_ALPHA self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 1000, 128, inactive_alpha) self.setFlag(KEYINPUT_ACTIVE_FLAG, True) else: self.setBrush(getNoBrush()) # self.setLabel(text=None) self.setAcceptHoverEvents(False) self.setFlag(KEYINPUT_ACTIVE_FLAG, False) def activateNeighbor(self, active_prexoveritem, shortcut=None): """To be called with whatever the active_prexoveritem is for the parts `active_base` Args: active_prexoveritem (TYPE): Description shortcut (None, optional): Description """ p1 = self._phos_item.scenePos() p2 = active_prexoveritem._phos_item.scenePos() scale = 3 delta1 = -BASE_WIDTH*scale if self.is_fwd else BASE_WIDTH*scale delta2 = BASE_WIDTH*scale if active_prexoveritem.is_fwd else -BASE_WIDTH*scale c1 = self.mapFromScene(QPointF(p1.x(), p1.y() + delta1)) c2 = self.mapFromScene(QPointF(p2.x(), p2.y() - delta2)) pp = QPainterPath() pp.moveTo(self._phos_item.pos()) pp.cubicTo(c1, c2, self._bond_item.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show() alpha = 32 idx, active_idx = self.idx, active_prexoveritem.idx if self.is_fwd != active_prexoveritem.is_fwd: if idx == active_idx: alpha = 255 elif idx == active_idx + 1: alpha = 255 elif idx == active_idx - 1: alpha = 255 inactive_alpha = PROX_ALPHA if self.to_vh_id_num is not None else 0 self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 500, inactive_alpha, alpha) self.animate(self._phos_item, 'rotation', 500, 0, -90) self.setLabel(text=shortcut, outline=True) # end def def deactivateNeighbor(self): """Summary Returns: TYPE: Description """ if self.isVisible(): inactive_alpha = PROX_ALPHA if self.to_vh_id_num is not None else 0 self.setBrush(getBrushObj(self._color, alpha=inactive_alpha)) self.animate(self, 'brush_alpha', 1000, 128, inactive_alpha) self.animate(self._phos_item, 'rotation', 500, -90, 0) self._bond_item.hide() self.setLabel(text=self._label_txt)