def __init__(self, model_part_instance, viewroot, parent): """parent should always be pathrootitem Args: model_part_instance (TYPE): Description viewroot (TYPE): Description parent (TYPE): Description """ super(PathNucleicAcidPartItem, self).__init__(model_part_instance, viewroot, parent) self._getActiveTool = viewroot.manager.activeToolGetter self.active_virtual_helix_item = None m_p = self._model_part self._controller = NucleicAcidPartItemController(self, m_p) self.prexover_manager = PreXoverManager(self) self._virtual_helix_item_list = [] self._vh_rect = QRectF() self.setAcceptHoverEvents(True) self._initModifierRect() self._proxy_parent = ProxyParentItem(self) self._proxy_parent.setFlag(QGraphicsItem.ItemHasNoContents) self._scale_2_model = m_p.baseWidth() / _BASE_WIDTH self._scale_2_Qt = _BASE_WIDTH / m_p.baseWidth() GC_SIZE = 20 self.grab_corner = GrabCornerItem(GC_SIZE, m_p.getColor(), False, self)
def __init__(self, model_part_instance, viewroot, parent=None): """Summary Args: model_part_instance (TYPE): Description viewroot (TYPE): Description parent (None, optional): Description """ super(SliceNucleicAcidPartItem, self).__init__(model_part_instance, viewroot, parent) self._getActiveTool = viewroot.manager.activeToolGetter m_p = self._model_part self._controller = NucleicAcidPartItemController(self, m_p) self.scale_factor = self._RADIUS / m_p.radius() self.active_virtual_helix_item = None self.prexover_manager = PreXoverManager(self) self.hide() # hide while until after attemptResize() to avoid flicker self._rect = QRectF(0., 0., 1000., 1000.) # set this to a token value self.boundRectToModel() self.setPen(getNoPen()) self.setRect(self._rect) self.setAcceptHoverEvents(True) # Cache of VHs that were active as of last call to activeSliceChanged # If None, all slices will be redrawn and the cache will be filled. # Connect destructor. This is for removing a part from scenes. # initialize the NucleicAcidPartItem with an empty set of old coords self.setZValue(styles.ZPARTITEM) self.outline = outline = QGraphicsRectItem(self) o_rect = self.configureOutline(outline) outline.setFlag(QGraphicsItem.ItemStacksBehindParent) outline.setZValue(styles.ZDESELECTOR) model_color = m_p.getColor() self.outline.setPen(getPenObj(model_color, _DEFAULT_WIDTH)) GC_SIZE = 10 self.grab_cornerTL = GrabCornerItem(GC_SIZE, model_color, True, self) self.grab_cornerTL.setTopLeft(o_rect.topLeft()) self.grab_cornerBR = GrabCornerItem(GC_SIZE, model_color, True, self) self.grab_cornerBR.setBottomRight(o_rect.bottomRight()) self.griditem = GridItem(self, self._model_props['grid_type']) self.griditem.setZValue(1) self.grab_cornerTL.setZValue(2) self.grab_cornerBR.setZValue(2) # select upon creation for part in m_p.document().children(): if part is m_p: part.setSelected(True) else: part.setSelected(False) self.show()
def __init__(self, model_part, parent): super(OutlineNucleicAcidPartItem, self).__init__(model_part, parent) self._controller = NucleicAcidPartItemController(self, model_part) self._model_part = model_part self.setExpanded(True) # properties temp_color = model_part.getColor() # outlinerview takes responsibility of overriding default part color if temp_color == "#000000": index = len(model_part.document().children()) - 1 new_color = styles.PARTCOLORS[index % len(styles.PARTCOLORS)] model_part.setProperty('color', new_color) # item groups self._root_items = {} self._root_items['VHelixList'] = self.createRootPartItem('Virtual Helices', self) self._root_items['OligoList'] = self.createRootPartItem('Oligos', self) # self._root_items['Modifications'] = self._createRootItem('Modifications', self) if model_part.is_active: print("should be active") self.activate()
def __init__(self, **kwargs): """Summary Args: model_part (Part): The model part parent (PropertyEditorWidget): The property editor key (None, optional): Description """ super().__init__(**kwargs) if self._key == "name": for model_part in self.cnModelList(): self._controller_list.append( NucleicAcidPartItemController(self, model_part))
class PathNucleicAcidPartItem(QAbstractPartItem): """Summary Attributes: active_virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description findChild (TYPE): Description grab_corner (TYPE): Description prexover_manager (TYPE): Description """ findChild = util.findChild # for debug _BOUNDING_RECT_PADDING = 0 def __init__(self, model_part_instance, viewroot, parent): """parent should always be pathrootitem Args: model_part_instance (TYPE): Description viewroot (TYPE): Description parent (TYPE): Description """ super(PathNucleicAcidPartItem, self).__init__(model_part_instance, viewroot, parent) self._getActiveTool = viewroot.manager.activeToolGetter self.active_virtual_helix_item = None m_p = self._model_part self._controller = NucleicAcidPartItemController(self, m_p) self.prexover_manager = PreXoverManager(self) self._virtual_helix_item_list = [] self._vh_rect = QRectF() self.setAcceptHoverEvents(True) self._initModifierRect() self._proxy_parent = ProxyParentItem(self) self._proxy_parent.setFlag(QGraphicsItem.ItemHasNoContents) self._scale_2_model = m_p.baseWidth()/_BASE_WIDTH self._scale_2_Qt = _BASE_WIDTH / m_p.baseWidth() GC_SIZE = 10 self.grab_corner = GrabCornerItem(GC_SIZE, m_p.getColor(), False, self) # end def def proxy(self): """Summary Returns: TYPE: Description """ return self._proxy_parent # end def def modelColor(self): """Summary Returns: TYPE: Description """ return self._model_part.getProperty('color') # end def def convertToModelZ(self, z): """scale Z-axis coordinate to the model Args: z (TYPE): Description """ return z * self._scale_2_model # end def def convertToQtZ(self, z): """Summary Args: z (TYPE): Description Returns: TYPE: Description """ return z * self._scale_2_Qt # end def def _initModifierRect(self): """docstring for _initModifierRect """ self._can_show_mod_rect = False self._mod_rect = m_r = QGraphicsRectItem(_DEFAULT_RECT, self) m_r.setPen(_MOD_PEN) m_r.hide() # end def def vhItemForIdNum(self, id_num): """Returns the pathview VirtualHelixItem corresponding to id_num Args: id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. """ return self._virtual_helix_item_hash.get(id_num) ### SIGNALS ### ### SLOTS ### def partActiveVirtualHelixChangedSlot(self, part, id_num): """Summary Args: part (TYPE): Description id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. Returns: TYPE: Description """ vhi = self._virtual_helix_item_hash.get(id_num, None) self.setActiveVirtualHelixItem(vhi) self.setPreXoverItemsVisible(vhi) # end def def partActiveBaseInfoSlot(self, part, info): """Summary Args: part (TYPE): Description info (TYPE): Description Returns: TYPE: Description """ pxoig = self.prexover_manager pxoig.deactivateNeighbors() if info and info is not None: id_num, is_fwd, idx, to_vh_id_num = info pxoig.activateNeighbors(id_num, is_fwd, idx) # end def def partZDimensionsChangedSlot(self, model_part, min_id_num, max_id_num, ztf=False): """Summary Args: model_part (Part): The model part min_id_num (TYPE): Description max_id_num (TYPE): Description ztf (bool, optional): Description Returns: TYPE: Description """ if len(self._virtual_helix_item_list) > 0: vhi_hash = self._virtual_helix_item_hash vhi_max = vhi_hash[max_id_num] vhi_rect_max = vhi_max.boundingRect() self._vh_rect.setRight(vhi_rect_max.right() + vhi_max.x()) vhi_min = vhi_hash[min_id_num] vhi_h_rect = vhi_min.handle().boundingRect() self._vh_rect.setLeft((vhi_h_rect.left() - styles.VH_XOFFSET + vhi_min.x())) if ztf: self.scene().views()[0].zoomToFit() self._updateBoundingRect() # end def def partSelectedChangedSlot(self, model_part, is_selected): """Summary Args: model_part (Part): The model part is_selected (TYPE): Description Returns: TYPE: Description """ # print("partSelectedChangedSlot", is_selected) if is_selected: self.resetPen(styles.SELECTED_COLOR, styles.SELECTED_PEN_WIDTH) self.resetBrush(styles.SELECTED_BRUSH_COLOR, styles.SELECTED_ALPHA) else: self.resetPen(self.modelColor()) self.resetBrush(styles.DEFAULT_BRUSH_COLOR, styles.DEFAULT_ALPHA) def partPropertyChangedSlot(self, model_part, property_key, new_value): """Summary Args: model_part (Part): The model part property_key (TYPE): Description new_value (TYPE): Description Returns: TYPE: Description """ if self._model_part == model_part: self._model_props[property_key] = new_value if property_key == 'color': self._updateBoundingRect() for vhi in self._virtual_helix_item_list: vhi.handle().refreshColor() self.grab_corner.setBrush(getBrushObj(new_value)) elif property_key == 'is_visible': if new_value: self.show() else: self.hide() elif property_key == 'virtual_helix_order': vhi_dict = self._virtual_helix_item_hash new_list = [vhi_dict[id_num] for id_num in new_value] ztf = False self._setVirtualHelixItemList(new_list, zoom_to_fit=ztf) # end def def partVirtualHelicesTranslatedSlot(self, sender, vh_set, left_overs, do_deselect): """Summary Args: sender (obj): Model object that emitted the signal. vh_set (TYPE): Description left_overs (TYPE): Description do_deselect (TYPE): Description Returns: TYPE: Description """ # self.prexover_manager.clearPreXoverItems() # if self.active_virtual_helix_item is not None: # self.active_virtual_helix_item.deactivate() # self.active_virtual_helix_item = None # if self.active_virtual_helix_item is not None: # self.setPreXoverItemsVisible(self.active_virtual_helix_item) pass # end def def partRemovedSlot(self, sender): """docstring for partRemovedSlot Args: sender (obj): Model object that emitted the signal. """ self.parentItem().removePartItem(self) scene = self.scene() scene.removeItem(self) self._model_part = None self._virtual_helix_item_hash = None self._virtual_helix_item_list = None self._controller.disconnectSignals() self._controller = None self.grab_corner = None # end def def partVirtualHelixAddedSlot(self, model_part, id_num, virtual_helix, neighbors): """ When a virtual helix is added to the model, this slot handles the instantiation of a virtualhelix item. Args: model_part (Part): The model part id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. """ # print("NucleicAcidPartItem.partVirtualHelixAddedSlot") vhi = PathVirtualHelixItem(virtual_helix, self, self._viewroot) self._virtual_helix_item_hash[id_num] = vhi vhi_list = self._virtual_helix_item_list # reposition when first VH is added if len(vhi_list) == 0: view = self.window().path_graphics_view p = view.scene_root_item.childrenBoundingRect().bottomLeft() _p = _BOUNDING_RECT_PADDING self.setPos(p.x() + _p*6 + styles.VIRTUALHELIXHANDLEITEM_RADIUS, p.y() + _p*3) # self.setPos(p.x() + _VH_XOFFSET, p.y() + _p*3) vhi_list.append(vhi) ztf = not getBatch() self._setVirtualHelixItemList(vhi_list, zoom_to_fit=ztf) # end def def partVirtualHelixResizedSlot(self, sender, id_num, virtual_helix): """Notifies the virtualhelix at coord to resize. Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. """ vhi = self._virtual_helix_item_hash[id_num] # print("resize:", id_num, virtual_helix.getSize()) vhi.resize() # end def def partVirtualHelixRemovingSlot(self, sender, id_num, virtual_helix, neighbors): """Summary Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. Returns: TYPE: Description """ self.removeVirtualHelixItem(id_num) # end def def partVirtualHelixRemovedSlot(self, sender, id_num): """ Step 2 of removing a VHI """ ztf = not getBatch() self._setVirtualHelixItemList(self._virtual_helix_item_list, zoom_to_fit=ztf) # end def def partVirtualHelixPropertyChangedSlot(self, sender, id_num, virtual_helix, keys, values): """Summary Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. keys (TYPE): Description values (TYPE): Description Returns: TYPE: Description """ if self._model_part == sender: vh_i = self._virtual_helix_item_hash[id_num] vh_i.virtualHelixPropertyChangedSlot(keys, values) # end def def partVirtualHelicesSelectedSlot(self, sender, vh_set, is_adding): """is_adding (bool): adding (True) virtual helices to a selection or removing (False) Args: sender (obj): Model object that emitted the signal. vh_set (TYPE): Description is_adding (TYPE): Description """ vhhi_group = self._viewroot.vhiHandleSelectionGroup() vh_hash = self._virtual_helix_item_hash doc = self._viewroot.document() if is_adding: # print("got the adding slot in path") for id_num in vh_set: vhi = vh_hash[id_num] vhhi = vhi.handle() vhhi.modelSelect(doc) # end for vhhi_group.processPendingToAddList() else: # print("got the removing slot in path") for id_num in vh_set: vhi = vh_hash[id_num] vhhi = vhi.handle() vhhi.modelDeselect(doc) # end for vhhi_group.processPendingToAddList() # end def ### ACCESSORS ### def removeVirtualHelixItem(self, id_num): """Summary Args: id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. Returns: TYPE: Description """ self.setActiveVirtualHelixItem(None) vhi = self._virtual_helix_item_hash[id_num] vhi.virtualHelixRemovedSlot() self._virtual_helix_item_list.remove(vhi) del self._virtual_helix_item_hash[id_num] # end def def window(self): """Summary Returns: TYPE: Description """ return self.parentItem().window() # end def ### PRIVATE METHODS ### def _setVirtualHelixItemList(self, new_list, zoom_to_fit=True): """ Give me a list of VirtualHelixItems and I'll parent them to myself if necessary, position them in a column, adopt their handles, and position them as well. Args: new_list (TYPE): Description zoom_to_fit (bool, optional): Description """ y = 0 # How far down from the top the next PH should be vhi_rect = None vhi_h_rect = None vhi_h_selection_group = self._viewroot.vhiHandleSelectionGroup() for vhi in new_list: _, _, _z = vhi.cnModel().getAxisPoint(0) _z *= self._scale_2_Qt vhi.setPos(_z, y) if vhi_rect is None: vhi_rect = vhi.boundingRect() step = vhi_rect.height() + styles.PATH_HELIX_PADDING # end if # get the VirtualHelixHandleItem vhi_h = vhi.handle() do_reselect = False if vhi_h.parentItem() == vhi_h_selection_group: do_reselect = True vhi_h.tempReparent() # so positioning works if vhi_h_rect is None: vhi_h_rect = vhi_h.boundingRect() vhi_h_x = _z - _VH_XOFFSET vhi_h_y = y + (vhi_rect.height() - vhi_h_rect.height()) / 2 vhi_h.setPos(vhi_h_x, vhi_h_y) y += step self.updateXoverItems(vhi) if do_reselect: vhi_h_selection_group.addToGroup(vhi_h) # end for # this need only adjust top and bottom edges of the bounding rectangle self._vh_rect.setTop(-10) self._vh_rect.setBottom(y) self._virtual_helix_item_list = new_list # now update Z dimension (X in Qt space in the Path view) part = self.part() self.partZDimensionsChangedSlot(part, *part.zBoundsIds(), ztf=zoom_to_fit) # end def def resetPen(self, color, width=0): """Summary Args: color (TYPE): Description width (int, optional): Description Returns: TYPE: Description """ pen = getPenObj(color, width) self.setPen(pen) # end def def resetBrush(self, color, alpha): """Summary Args: color (TYPE): Description alpha (TYPE): Description Returns: TYPE: Description """ brush = getBrushObj(color, alpha=alpha) self.setBrush(brush) # end def def _updateBoundingRect(self): """ Updates the bounding rect to the size of the childrenBoundingRect, and refreshes the addBases and removeBases buttons accordingly. Called by partZDimensionsChangedSlot and partPropertyChangedSlot """ self.setPen(getPenObj(self.modelColor(), 0)) self.resetBrush(styles.DEFAULT_BRUSH_COLOR, styles.DEFAULT_ALPHA) # self.setRect(self.childrenBoundingRect()) _p = _BOUNDING_RECT_PADDING temp_rect = self._vh_rect.adjusted(-_p/2, -_p, _p, -_p/2) self.grab_corner.setTopLeft(temp_rect.topLeft()) self.setRect(temp_rect) # end def ### PUBLIC METHODS ### def setModifyState(self, bool): """Hides the modRect when modify state disabled. Args: bool (TYPE): Description """ self._can_show_mod_rect = bool if bool is False: self._mod_rect.hide() def getOrderedVirtualHelixList(self): """Used for encoding. """ ret = [] for vhi in self._virtual_helix_item_list: ret.append(vhi.coord()) return ret # end def def reorderHelices(self, id_nums, index_delta): """ Reorder helices by moving helices _pathHelixList[first:last] by a distance delta in the list. Notify each PathHelix and PathHelixHandle of its new location. Args: first (TYPE): Description last (TYPE): Description index_delta (TYPE): Description """ vhi_list = self._virtual_helix_item_list helix_numbers = [vhi.idNum() for vhi in vhi_list] first_index = helix_numbers.index(id_nums[0]) last_index = helix_numbers.index(id_nums[-1]) + 1 insert_idxs = [helix_numbers.index(id_num) for id_num in id_nums] for id_num in id_nums: helix_numbers.remove(id_num) if index_delta < 0: # move group earlier in the list new_index = max(0, index_delta + first_index) - len(id_nums) else: # move group later in list new_index = min(len(vhi_list), index_delta + last_index ) - len(id_nums) new_list = helix_numbers[:new_index] + id_nums + helix_numbers[new_index:] # call the method to move the items and store the list self._model_part.setImportedVHelixOrder(new_list, check_batch=False) # end def def setActiveVirtualHelixItem(self, new_active_vhi): """Summary Args: new_active_vhi (TYPE): Description Returns: TYPE: Description """ current_vhi = self.active_virtual_helix_item if new_active_vhi != current_vhi: if current_vhi is not None: current_vhi.deactivate() if new_active_vhi is not None: new_active_vhi.activate() self.active_virtual_helix_item = new_active_vhi # end def def setPreXoverItemsVisible(self, virtual_helix_item): """ self._pre_xover_items list references prexovers parented to other PathHelices such that only the activeHelix maintains the list of visible prexovers Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description """ vhi = virtual_helix_item if vhi is None: return # print("path.setPreXoverItemsVisible", virtual_helix_item.idNum()) part = self.part() info = part.active_base_info if info and virtual_helix_item is not None: id_num, is_fwd, idx, to_vh_id_num = info per_neighbor_hits, pairs = part.potentialCrossoverMap(id_num, idx) self.prexover_manager.activateVirtualHelix(virtual_helix_item, idx, per_neighbor_hits) else: self.prexover_manager.reset() # end def def updateXoverItems(self, virtual_helix_item): """Summary Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description Returns: TYPE: Description """ for item in virtual_helix_item.childItems(): if isinstance(item, XoverNode3): item.refreshXover() # end def def updateStatusBar(self, status_string): """Shows status_string in the MainWindow's status bar. Args: status_string (str): The text to be displayed. """ self.window().statusBar().showMessage(status_string) ### COORDINATE METHODS ### def keyPanDeltaX(self): """How far a single press of the left or right arrow key should move the scene (in scene space) """ vhs = self._virtual_helix_item_list return vhs[0].keyPanDeltaX() if vhs else 5 # end def def keyPanDeltaY(self): """How far an an arrow key should move the scene (in scene space) for a single press """ vhs = self._virtual_helix_item_list if not len(vhs) > 1: return 5 dy = vhs[0].pos().y() - vhs[1].pos().y() dummyRect = QRectF(0, 0, 1, dy) return self.mapToScene(dummyRect).boundingRect().height() # end def ### TOOL METHODS ### def mousePressEvent(self, event): """Handler for user mouse press. Args: event (QGraphicsSceneMouseEvent): Contains item, scene, and screen coordinates of the the event, and previous event. """ self._viewroot.clearSelectionsIfActiveTool() return QGraphicsItem.mousePressEvent(self, event) def hoverMoveEvent(self, event): """ Parses a mouseMoveEvent to extract strandSet and base index, forwarding them to approproate tool method as necessary. Args: event (TYPE): Description """ active_tool = self._getActiveTool() tool_method_name = active_tool.methodPrefix() + "HoverMove" if hasattr(self, tool_method_name): getattr(self, tool_method_name)(event.pos()) # end def def pencilToolHoverMove(self, pt): """Pencil the strand is possible. Args: pt (QPointF): mouse cursor location of pencil tool hover. """ active_tool = self._getActiveTool() if not active_tool.isFloatingXoverBegin(): temp_xover = active_tool.floatingXover() temp_xover.updateFloatingFromPartItem(self, pt)
class OutlineNucleicAcidPartItem(CNOutlinerItem, AbstractPartItem): FILTER_NAME = "part" def __init__(self, model_part, parent): super(OutlineNucleicAcidPartItem, self).__init__(model_part, parent) self._controller = NucleicAcidPartItemController(self, model_part) self._model_part = model_part self.setExpanded(True) # properties temp_color = model_part.getColor() # outlinerview takes responsibility of overriding default part color if temp_color == "#000000": index = len(model_part.document().children()) - 1 new_color = styles.PARTCOLORS[index % len(styles.PARTCOLORS)] model_part.setProperty('color', new_color) # item groups self._root_items = {} self._root_items['VHelixList'] = self.createRootPartItem('Virtual Helices', self) self._root_items['OligoList'] = self.createRootPartItem('Oligos', self) # self._root_items['Modifications'] = self._createRootItem('Modifications', self) if model_part.is_active: print("should be active") self.activate() # end def ### PRIVATE SUPPORT METHODS ### def __repr__(self): return "OutlineNucleicAcidPartItem %s" % self._cn_model.getProperty('name') ### PUBLIC SUPPORT METHODS ### def rootItems(self): return self._root_items # end def def part(self): return self._cn_model # end def def itemType(self): return ItemType.NUCLEICACID # end def def isModelSelected(self, document): """Make sure the item is selected in the model TODO implement Part selection Args: document (Document): reference the the model :class:`Document` """ return False # end def ### SLOTS ### def partRemovedSlot(self, sender): self._controller.disconnectSignals() self._cn_model = None self._controller = None # end def def partOligoAddedSlot(self, model_part, model_oligo): m_o = model_oligo m_o.oligoRemovedSignal.connect(self.partOligoRemovedSlot) o_i = OutlineOligoItem(m_o, self._root_items['OligoList']) self._oligo_item_hash[m_o] = o_i # end def def partOligoRemovedSlot(self, model_part, model_oligo): m_o = model_oligo m_o.oligoRemovedSignal.disconnect(self.partOligoRemovedSlot) o_i = self._oligo_item_hash[m_o] o_i.parent().removeChild(o_i) del self._oligo_item_hash[m_o] # end def def partVirtualHelixAddedSlot(self, model_part, id_num, virtual_helix, neighbors): tw = self.treeWidget() tw.is_child_adding += 1 vh_i = OutlineVirtualHelixItem(virtual_helix, self._root_items['VHelixList']) self._virtual_helix_item_hash[id_num] = vh_i tw.is_child_adding -= 1 def partVirtualHelixRemovingSlot(self, model_part, id_num, virtual_helix, neigbors): vh_i = self._virtual_helix_item_hash.get(id_num) # in case a OutlineVirtualHelixItem Object is cleaned up before this happends if vh_i is not None: del self._virtual_helix_item_hash[id_num] vh_i.parent().removeChild(vh_i) # end def def partPropertyChangedSlot(self, model_part, property_key, new_value): if self._cn_model == model_part: self.setValue(property_key, new_value) if property_key == 'virtual_helix_order': vhi_dict = self._virtual_helix_item_hash document = self.treeWidget().document() new_list = [vhi_dict[id_num] for id_num in new_value] # 0. record what was selected selected_list = [(x, x.isSelected()) for x in new_list] root_vhi = self._root_items['VHelixList'] # 1. move the items root_vhi.takeChildren() for vhi in new_list: root_vhi.addChild(vhi) # 2. now reselect the previously selected. # could also query the model for vhi, was_selected in selected_list: if was_selected: vhi.setSelected(True) # end def def partSelectedChangedSlot(self, model_part, is_selected): # print("part", is_selected) self.setSelected(is_selected) # end def def partVirtualHelixPropertyChangedSlot(self, sender, id_num, virtual_helix, keys, values): if self._cn_model == sender: vh_i = self._virtual_helix_item_hash[id_num] for key, val in zip(keys, values): if key in CNOutlinerItem.PROPERTIES: vh_i.setValue(key, val) # end def def partVirtualHelicesSelectedSlot(self, sender, vh_set, is_adding): """ is_adding (bool): adding (True) virtual helices to a selection or removing (False) """ vhi_hash = self._virtual_helix_item_hash tw = self.treeWidget() model = tw.model() selection_model = tw.selectionModel() top_idx = tw.indexOfTopLevelItem(self) top_midx = model.index(top_idx, 0) vh_list = self._root_items['VHelixList'] root_midx = model.index(self.indexOfChild(vh_list), 0, top_midx) tw.selection_filter_disabled = True if is_adding: flag = QItemSelectionModel.Select for id_num in vh_set: vhi = vhi_hash.get(id_num) # selecting a selected item will deselect it, so check idx = vh_list.indexOfChild(vhi) qmodel_idx = model.index(idx, 0, root_midx) if not vhi.isSelected() and not selection_model.isSelected(qmodel_idx): # print("++++++slot Sselect outlinerview", vh_set) selection_model.select(qmodel_idx, flag) else: flag = QItemSelectionModel.Deselect for id_num in vh_set: vhi = vhi_hash.get(id_num) # deselecting a deselected item will select it, so check idx = vh_list.indexOfChild(vhi) qmodel_idx = model.index(idx, 0, root_midx) if vhi.isSelected() and selection_model.isSelected(qmodel_idx): # print("-----slot deselect outlinerview", vh_set) selection_model.select(qmodel_idx, flag) tw.selection_filter_disabled = False # end def def partActiveVirtualHelixChangedSlot(self, part, id_num): vhi = self._virtual_helix_item_hash.get(id_num, None) # if vhi is not None: self.setActiveVirtualHelixItem(vhi) # end def def partActiveChangedSlot(self, part, is_active): if part == self._cn_model: self.activate() if is_active else self.deactivate() # end def def setActiveVirtualHelixItem(self, new_active_vhi): current_vhi = self.active_virtual_helix_item if new_active_vhi != current_vhi: if current_vhi is not None: current_vhi.deactivate() if new_active_vhi is not None: new_active_vhi.activate() self.active_virtual_helix_item = new_active_vhi
class SliceNucleicAcidPartItem(QAbstractPartItem): """Parent should be either a SliceRootItem, or an AssemblyItem. Invariant: keys in _empty_helix_hash = range(_nrows) x range(_ncols) where x is the cartesian product. Attributes: active_virtual_helix_item (cadnano.gui.views.sliceview.virtualhelixitem.SliceVirtualHelixItem): Description grab_cornerBR (TYPE): bottom right bounding box handle grab_cornerTL (TYPE): top left bounding box handle griditem (TYPE): Description outline (TYPE): Description prexover_manager (TYPE): Description scale_factor (TYPE): Description """ _RADIUS = styles.SLICE_HELIX_RADIUS _BOUNDING_RECT_PADDING = 80 def __init__(self, model_part_instance, viewroot, parent=None): """Summary Args: model_part_instance (TYPE): Description viewroot (TYPE): Description parent (None, optional): Description """ super(SliceNucleicAcidPartItem, self).__init__(model_part_instance, viewroot, parent) self._getActiveTool = viewroot.manager.activeToolGetter m_p = self._model_part self._controller = NucleicAcidPartItemController(self, m_p) self.scale_factor = self._RADIUS / m_p.radius() self.active_virtual_helix_item = None self.prexover_manager = PreXoverManager(self) self.hide() # hide while until after attemptResize() to avoid flicker self._rect = QRectF(0., 0., 1000., 1000.) # set this to a token value self.boundRectToModel() self.setPen(getNoPen()) self.setRect(self._rect) self.setAcceptHoverEvents(True) # Cache of VHs that were active as of last call to activeSliceChanged # If None, all slices will be redrawn and the cache will be filled. # Connect destructor. This is for removing a part from scenes. # initialize the NucleicAcidPartItem with an empty set of old coords self.setZValue(styles.ZPARTITEM) self.outline = outline = QGraphicsRectItem(self) o_rect = self.configureOutline(outline) outline.setFlag(QGraphicsItem.ItemStacksBehindParent) outline.setZValue(styles.ZDESELECTOR) model_color = m_p.getColor() self.outline.setPen(getPenObj(model_color, _DEFAULT_WIDTH)) GC_SIZE = 10 self.grab_cornerTL = GrabCornerItem(GC_SIZE, model_color, True, self) self.grab_cornerTL.setTopLeft(o_rect.topLeft()) self.grab_cornerBR = GrabCornerItem(GC_SIZE, model_color, True, self) self.grab_cornerBR.setBottomRight(o_rect.bottomRight()) self.griditem = GridItem(self, self._model_props['grid_type']) self.griditem.setZValue(1) self.grab_cornerTL.setZValue(2) self.grab_cornerBR.setZValue(2) # select upon creation for part in m_p.document().children(): if part is m_p: part.setSelected(True) else: part.setSelected(False) self.show() # end def ### SIGNALS ### ### SLOTS ### def partActiveVirtualHelixChangedSlot(self, part, id_num): """Summary Args: part (TYPE): Description id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. Args: TYPE: Description """ vhi = self._virtual_helix_item_hash.get(id_num, None) self.setActiveVirtualHelixItem(vhi) self.setPreXoverItemsVisible(vhi) # end def def partActiveBaseInfoSlot(self, part, info): """Summary Args: part (TYPE): Description info (TYPE): Description Args: TYPE: Description """ pxom = self.prexover_manager pxom.deactivateNeighbors() if info and info is not None: id_num, is_fwd, idx, _ = info pxom.activateNeighbors(id_num, is_fwd, idx) # end def def partPropertyChangedSlot(self, model_part, property_key, new_value): """Summary Args: model_part (Part): The model part property_key (TYPE): Description new_value (TYPE): Description Args: TYPE: Description """ if self._model_part == model_part: self._model_props[property_key] = new_value if property_key == 'color': self.outline.setPen(getPenObj(new_value, _DEFAULT_WIDTH)) for vhi in self._virtual_helix_item_hash.values(): vhi.updateAppearance() self.grab_cornerTL.setPen(getPenObj(new_value, 0)) self.grab_cornerBR.setPen(getPenObj(new_value, 0)) elif property_key == 'is_visible': if new_value: self.show() else: self.hide() elif property_key == 'grid_type': self.griditem.setGridType(new_value) # end def def partRemovedSlot(self, sender): """docstring for partRemovedSlot Args: sender (obj): Model object that emitted the signal. """ self.parentItem().removePartItem(self) scene = self.scene() scene.removeItem(self) self._model_part = None self._mod_circ = None self._controller.disconnectSignals() self._controller = None self.grab_cornerTL = None self.grab_cornerBR = None self.griditem = None # end def def partVirtualHelicesTranslatedSlot(self, sender, vh_set, left_overs, do_deselect): """ left_overs are neighbors that need updating due to changes Args: sender (obj): Model object that emitted the signal. vh_set (TYPE): Description left_overs (TYPE): Description do_deselect (TYPE): Description """ if do_deselect: tool = self._getActiveTool() if tool.methodPrefix() == "selectTool": if tool.isSelectionActive(): # tool.deselectItems() tool.modelClear() # 1. move everything that moved for id_num in vh_set: vhi = self._virtual_helix_item_hash[id_num] vhi.updatePosition() # 2. now redraw what makes sense to be redrawn for id_num in vh_set: vhi = self._virtual_helix_item_hash[id_num] self._refreshVirtualHelixItemGizmos(id_num, vhi) for id_num in left_overs: vhi = self._virtual_helix_item_hash[id_num] self._refreshVirtualHelixItemGizmos(id_num, vhi) # 0. clear PreXovers: # self.prexover_manager.hideGroups() # if self.active_virtual_helix_item is not None: # self.active_virtual_helix_item.deactivate() # self.active_virtual_helix_item = None avhi = self.active_virtual_helix_item self.setPreXoverItemsVisible(avhi) self.enlargeRectToFit() # end def def _refreshVirtualHelixItemGizmos(self, id_num, vhi): """Update props and appearance of self & recent neighbors. Ultimately triggered by a partVirtualHelicesTranslatedSignal. Args: id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. vhi (cadnano.gui.views.sliceview.virtualhelixitem.SliceVirtualHelixItem): the item associated with id_num """ neighbors = vhi.cnModel().getProperty('neighbors') neighbors = literal_eval(neighbors) vhi.beginAddWedgeGizmos() for nvh in neighbors: nvhi = self._virtual_helix_item_hash.get(nvh, False) if nvhi: vhi.setWedgeGizmo(nvh, nvhi) # end for vhi.endAddWedgeGizmos() # end def def partVirtualHelixPropertyChangedSlot(self, sender, id_num, virtual_helix, keys, values): """Summary Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. keys (tuple): keys that changed values (tuple): new values for each key that changed Args: TYPE: Description """ if self._model_part == sender: vh_i = self._virtual_helix_item_hash[id_num] vh_i.virtualHelixPropertyChangedSlot(keys, values) # end def def partVirtualHelixAddedSlot(self, sender, id_num, virtual_helix, neighbors): """Summary Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. neighbors (TYPE): Description Args: TYPE: Description """ vhi = SliceVirtualHelixItem(virtual_helix, self) self._virtual_helix_item_hash[id_num] = vhi self._refreshVirtualHelixItemGizmos(id_num, vhi) for neighbor_id in neighbors: nvhi = self._virtual_helix_item_hash.get(neighbor_id, False) if nvhi: self._refreshVirtualHelixItemGizmos(neighbor_id, nvhi) self.enlargeRectToFit() # end def def partVirtualHelixRemovingSlot(self, sender, id_num, virtual_helix, neighbors): """Summary Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. neighbors (TYPE): Description Args: TYPE: Description """ tm = self._viewroot.manager tm.resetTools() self.removeVirtualHelixItem(id_num) for neighbor_id in neighbors: nvhi = self._virtual_helix_item_hash[neighbor_id] self._refreshVirtualHelixItemGizmos(neighbor_id, nvhi) # end def def partSelectedChangedSlot(self, model_part, is_selected): """Set this Z to front, and return other Zs to default. Args: model_part (Part): The model part is_selected (TYPE): Description """ if is_selected: # self._drag_handle.resetAppearance(_SELECTED_COLOR, _SELECTED_WIDTH, _SELECTED_ALPHA) self.setZValue(styles.ZPARTITEM + 1) else: # self._drag_handle.resetAppearance(self.modelColor(), _DEFAULT_WIDTH, _DEFAULT_ALPHA) self.setZValue(styles.ZPARTITEM) # end def def partVirtualHelicesSelectedSlot(self, sender, vh_set, is_adding): """is_adding (bool): adding (True) virtual helices to a selection or removing (False) Args: sender (obj): Model object that emitted the signal. vh_set (TYPE): Description is_adding (TYPE): Description """ select_tool = self._viewroot.select_tool if is_adding: # print("got the adding slot in path") select_tool.selection_set.update(vh_set) select_tool.setPartItem(self) select_tool.getSelectionBoundingRect() else: select_tool.deselectSet(vh_set) # end def def partDocumentSettingChangedSlot(self, part, key, value): """Summary Args: part (TYPE): Description key (TYPE): Description value (TYPE): Description Args: TYPE: Description Raises: ValueError: Description """ if key == 'grid': print("grid change", value) if value == 'lines and points': self.griditem.setDrawlines(True) elif value == 'points': self.griditem.setDrawlines(False) elif value == 'circles': pass # self.griditem.setDrawlines(False) else: raise ValueError("unknown grid styling") # end def ### ACCESSORS ### def boundingRect(self): """Summary Args: TYPE: Description """ return self._rect # end def def modelColor(self): """Summary Args: TYPE: Description """ return self._model_props['color'] # end def def window(self): """Summary Args: TYPE: Description """ return self.parentItem().window() # end def def setActiveVirtualHelixItem(self, new_active_vhi): """Summary Args: new_active_vhi (TYPE): Description """ current_vhi = self.active_virtual_helix_item # print(current_vhi, new_active_vhi) if new_active_vhi != current_vhi: if current_vhi is not None: current_vhi.deactivate() if new_active_vhi is not None: new_active_vhi.activate() self.active_virtual_helix_item = new_active_vhi # end def def setPreXoverItemsVisible(self, virtual_helix_item): """ self._pre_xover_items list references prexovers parented to other PathHelices such that only the activeHelix maintains the list of visible prexovers Args: virtual_helix_item (cadnano.gui.views.sliceview.virtualhelixitem.SliceVirtualHelixItem): Description """ vhi = virtual_helix_item pxom = self.prexover_manager if vhi is None: pxom.hideGroups() return # print("slice.setPreXoverItemsVisible", virtual_helix_item.idNum()) part = self.part() info = part.active_base_info if info: id_num, is_fwd, idx, to_vh_id_num = info per_neighbor_hits, pairs = part.potentialCrossoverMap(id_num, idx) pxom.activateVirtualHelix(virtual_helix_item, idx, per_neighbor_hits, pairs) # end def def removeVirtualHelixItem(self, id_num): """Summary Args: id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. Args: TYPE: Description """ vhi = self._virtual_helix_item_hash[id_num] if vhi == self.active_virtual_helix_item: self.active_virtual_helix_item = None vhi.virtualHelixRemovedSlot() del self._virtual_helix_item_hash[id_num] # end def def reconfigureRect(self, top_left, bottom_right, padding=80, do_grid=False): """Summary Args: top_left (TYPE): Description bottom_right (TYPE): Description Returns: tuple: tuple of point tuples representing the top_left and bottom_right as reconfigured with padding """ rect = self._rect ptTL = QPointF( *self.padTL(padding, *top_left)) if top_left else rect.topLeft() ptBR = QPointF(*self.padBR( padding, *bottom_right)) if bottom_right else rect.bottomRight() self._rect = new_rect = QRectF(ptTL, ptBR) self.setRect(new_rect) self.configureOutline(self.outline) if do_grid: self.griditem.updateGrid() return (ptTL.x(), ptTL.y()), (ptBR.x(), ptBR.y()) # end def def padTL(self, padding, xTL, yTL): return xTL + padding, yTL + padding # end def def padBR(self, padding, xBR, yBR): return xBR - padding, yBR - padding # end def def enlargeRectToFit(self): """Enlarges Part Rectangle to fit the model bounds. Call this when adding a SliceVirtualHelixItem. """ p = self._BOUNDING_RECT_PADDING xTL, yTL, xBR, yBR = self.getModelBounds() xTL = xTL - p yTL = yTL - p xBR = xBR + p yBR = yBR + p tl, br = self.reconfigureRect((xTL, yTL), (xBR, yBR), do_grid=True) self.grab_cornerTL.alignPos(*tl) self.grab_cornerBR.alignPos(*br) # end def ### PRIVATE SUPPORT METHODS ### def configureOutline(self, outline): """Adjusts `outline` size with default padding. Args: outline (TYPE): Description Returns: o_rect (QRect): `outline` rect adjusted by _BOUNDING_RECT_PADDING """ _p = self._BOUNDING_RECT_PADDING o_rect = self.rect().adjusted(-_p, -_p, _p, _p) outline.setRect(o_rect) return o_rect # end def def boundRectToModel(self): """update the boundaries to what's in the model with a minimum size """ xTL, yTL, xBR, yBR = self.getModelBounds() self._rect = QRectF(QPointF(xTL, yTL), QPointF(xBR, yBR)) # end def def getModelBounds(self): """Bounds in form of Qt scaled from model Args: Tuple (top_left, bottom_right) """ xLL, yLL, xUR, yUR = self.part().boundDimensions(self.scale_factor) return xLL, -yUR, xUR, -yLL # end def def bounds(self): """x_low, x_high, y_low, y_high """ rect = self._rect return (rect.left(), rect.right(), rect.bottom(), rect.top()) ### PUBLIC SUPPORT METHODS ### def setModifyState(self, bool_val): """Hides the mod_rect when modify state disabled. Args: bool_val (TYPE): what the modifystate should be set to. """ self._can_show_mod_circ = bool_val if bool_val is False: self._mod_circ.hide() # end def def updateStatusBar(self, status_str): """Shows status_str in the MainWindow's status bar. Args: status_str (TYPE): Description """ pass # disabled for now. # self.window().statusBar().showMessage(status_str, timeout) # end def def zoomToFit(self): """Summary Args: TYPE: Description """ thescene = self.scene() theview = thescene.views()[0] theview.zoomToFit() # end def ### EVENT HANDLERS ### def mousePressEvent(self, event): """Handler for user mouse press. Args: event (QGraphicsSceneMouseEvent): Contains item, scene, and screen coordinates of the the event, and previous event. Args: TYPE: Description """ if event.button() == Qt.RightButton: return part = self._model_part part.setSelected(True) if self.isMovable(): return QGraphicsItem.mousePressEvent(self, event) tool = self._getActiveTool() if tool.FILTER_NAME not in part.document().filter_set: return tool_method_name = tool.methodPrefix() + "MousePress" if hasattr(self, tool_method_name): getattr(self, tool_method_name)(tool, event) else: event.setAccepted(False) QGraphicsItem.mousePressEvent(self, event) # end def def hoverMoveEvent(self, event): """Summary Args: event (TYPE): Description Args: TYPE: Description """ tool = self._getActiveTool() tool_method_name = tool.methodPrefix() + "HoverMove" if hasattr(self, tool_method_name): getattr(self, tool_method_name)(tool, event) else: event.setAccepted(False) QGraphicsItem.hoverMoveEvent(self, event) # end def def hoverLeaveEvent(self, event): tool = self._getActiveTool() tool.hideLineItem() def getModelPos(self, pos): """Y-axis is inverted in Qt +y === DOWN Args: pos (TYPE): Description """ sf = self.scale_factor x, y = pos.x() / sf, -1.0 * pos.y() / sf return x, y # end def def getVirtualHelixItem(self, id_num): """Summary Args: id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. Returns: TYPE: Description """ return self._virtual_helix_item_hash.get(id_num) # end def def createToolMousePress(self, tool, event, alt_event=None): """Summary Args: tool (TYPE): Description event (TYPE): Description alt_event (None, optional): Description Returns: TYPE: Description """ # 1. get point in model coordinates: part = self._model_part if alt_event is None: # print() pt = tool.eventToPosition(self, event) # print("reg_event", pt) else: # pt = alt_event.scenePos() # pt = self.mapFromScene(pt) pt = alt_event.pos() # print("alt_event", pt) if pt is None: tool.deactivate() return QGraphicsItem.mousePressEvent(self, event) part_pt_tuple = self.getModelPos(pt) mod = Qt.MetaModifier if not (event.modifiers() & mod): pass # don't create a new VirtualHelix if the click overlaps with existing # VirtualHelix current_id_num = tool.idNum() check = part.isVirtualHelixNearPoint(part_pt_tuple, current_id_num) # print("current_id_num", current_id_num, check) # print(part_pt_tuple) tool.setPartItem(self) if check: id_num = part.getVirtualHelixAtPoint(part_pt_tuple) # print("got a check", id_num) if id_num is not None: # print("restart", id_num) vhi = self._virtual_helix_item_hash[id_num] tool.setVirtualHelixItem(vhi) tool.startCreation() else: # print("creating", part_pt_tuple) part.createVirtualHelix(*part_pt_tuple) id_num = part.getVirtualHelixAtPoint(part_pt_tuple) vhi = self._virtual_helix_item_hash[id_num] tool.setVirtualHelixItem(vhi) tool.startCreation() # end def def createToolHoverMove(self, tool, event): """Summary Args: tool (TYPE): Description event (TYPE): Description Returns: TYPE: Description """ tool.hoverMoveEvent(self, event) return QGraphicsItem.hoverMoveEvent(self, event) # end def def selectToolMousePress(self, tool, event): """ Args: tool (TYPE): Description event (TYPE): Description """ tool.setPartItem(self) pt = tool.eventToPosition(self, event) part_pt_tuple = self.getModelPos(pt) part = self._model_part if part.isVirtualHelixNearPoint(part_pt_tuple): id_num = part.getVirtualHelixAtPoint(part_pt_tuple) if id_num is not None: print(id_num) loc = part.getCoordinate(id_num, 0) print("VirtualHelix #{} at ({:.3f}, {:.3f})".format( id_num, loc[0], loc[1])) else: # tool.deselectItems() tool.modelClear() else: # tool.deselectItems() tool.modelClear() return QGraphicsItem.mousePressEvent(self, event)