def __init__(self, command): """ Constructor for the Nanotube property manager. """ self.endPoint1 = None self.endPoint2 = None self.nanotube = NanotubeParameters() # A 5x5 CNT. _superclass.__init__(self, command) self.showTopRowButtons( PM_DONE_BUTTON | \ PM_CANCEL_BUTTON | \ PM_WHATS_THIS_BUTTON)
def setProps(self, props): """ Sets some properties. These will be used while editing the structure. (but if the structure is read from an mmp file, this won't work. As a fall back, it returns some constant values) @see: InsertNanotube_EditCommand.createStructure which calls this method. @see: self.getProps, EditNanotube_EditCommand.editStructure """ (_n, _m), _type, _endings, (_endPoint1, _endPoint2) = props from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() self.nanotube.setChirality(_n, _m) self.nanotube.setType(_type) self.nanotube.setEndings(_endings) self.nanotube.setEndPoints(_endPoint1, _endPoint2)
def _createStructure(self): """ Returns the current NanotubeSegment being edited with a new nanotube chunk. @return : Nanotube segment that include the new nanotube chunk. @rtype: L{NanotubeSegment} """ try: # Create a new nanotube chunk using new params. n, m, type, endings, endPoint1, endPoint2 = self._gatherParameters() from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() nanotube = self.nanotube nanotube.setChirality(n, m) nanotube.setType(type) nanotube.setEndings(endings) nanotube.setEndPoints(endPoint1, endPoint2) position = V(0.0, 0.0, 0.0) ntChunk = nanotube.build(self.struct.name, self.win.assy, position) nanotube.computeEndPointsFromChunk(ntChunk) # Needed. self.struct.addchild(ntChunk) #WARNING 2008-03-05: #When we actually permit modifying a nanotube without recreating it, #then the following properties must be set in self._modifyStructure #as well. Needs more thought. props =(nanotube.getChirality(), nanotube.getType(), nanotube.getEndings(), nanotube.getEndPoints()) self.struct.setProps(props) return self.struct except (PluginBug, UserError): self.struct.kill() raise PluginBug("Internal error while trying to recreate a NanotubeSegment.") return None
def readmmp_info_opengroup_setitem(self, key, val, interp): """ [extends superclass method] """ #bruce 080507 refactoring (split this out of the superclass method) if key == ['nanotube-parameters']: # val includes all the parameters, separated by commas. n, m, type, endings = val.split(",") self.n = int(n) self.m = int(m) self.type = type.lstrip() self.endings = endings.lstrip() # Create the nanotube. from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() # Returns a 5x5 CNT. self.nanotube.setChirality(self.n, self.m) self.nanotube.setType(self.type) self.nanotube.setEndings(self.endings) # The endpoints are recomputed every time it is edited. else: _superclass.readmmp_info_opengroup_setitem(self, key, val, interp) return
def __init__( self, command ): """ Constructor for the Nanotube property manager. """ self.endPoint1 = None self.endPoint2 = None self.nanotube = NanotubeParameters() # A 5x5 CNT. _superclass.__init__( self, command) self.showTopRowButtons( PM_DONE_BUTTON | \ PM_CANCEL_BUTTON | \ PM_WHATS_THIS_BUTTON)
def setProps(self, props): """ Sets some properties. These will be used while editing the structure. (but if the structure is read from an mmp file, this won't work. As a fall back, it returns some constant values) @see: InsertNanotube_EditCommand.createStructure which calls this method. @see: self.getProps, EditNanotube_EditCommand.editStructure """ (_n, _m), _type, _endings, (_endPoint1, _endPoint2) = props from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() self.nanotube.setChirality(_n, _m) self.nanotube.setType(_type) self.nanotube.setEndings(_endings) self.nanotube.setEndPoints(_endPoint1, _endPoint2)
def readmmp_info_opengroup_setitem( self, key, val, interp ): """ [extends superclass method] """ #bruce 080507 refactoring (split this out of the superclass method) if key == ['nanotube-parameters']: # val includes all the parameters, separated by commas. n, m, type, endings = val.split(",") self.n = int(n) self.m = int(m) self.type = type.lstrip() self.endings = endings.lstrip() # Create the nanotube. from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() # Returns a 5x5 CNT. self.nanotube.setChirality(self.n, self.m) self.nanotube.setType(self.type) self.nanotube.setEndings(self.endings) # The endpoints are recomputed every time it is edited. else: _superclass.readmmp_info_opengroup_setitem( self, key, val, interp) return
class NanotubeSegment(LeafLikeGroup): """ Model object which represents a Nanotube Segment inside a Nanotube Group. Internally, this is just a specialized Group containing a single chunk, itself containing all the atoms of a nanotube. """ # This should be a tuple of classifications that appear in # files_mmp._GROUP_CLASSIFICATIONS, most general first. # See comment in class Group for more info. [bruce 080115] _mmp_group_classifications = ('NanotubeSegment', ) nanotube = None _endPoint1 = None _endPoint2 = None # TODO: undo or copy code for those attrs, # and updating them when the underlying structure changes. # But maybe that won't be needed, if they are replaced # by computing them from the atom geometry as needed. # [bruce 080227 comment] autodelete_when_empty = True # (but only if current command permits that for this class -- # see comment near Group.autodelete_when_empty for more info, # and implems of Command.keep_empty_group) iconPath = "ui/modeltree/NanotubeSegment.png" hide_iconPath = "ui/modeltree/NanotubeSegment-hide.png" # This partially fixes bug 2914. Copying now works, but the following # "warning" is printed to stdout: # ****************** needs _copyOfObject: <cnt.model.Nanotube.Nanotube instance at 0x164FC030> # I'm guessing this means that we need to override abstract method # _copyOfObject() of DataMixin, but I'd like to discuss this with Bruce first. # I also have confirmed that there is still a bug when editing the # copied nanotube (it will automatically move from the clipboard # to the part after it is resized). # Mark 2008-07-09. copyable_attrs = _superclass.copyable_attrs + ('nanotube', ) def writemmp_other_info_opengroup(self, mapping): #bruce 080507 refactoring """ """ #bruce 080507 refactoring (split this out of Group.writemmp) # (I think the following condition is always true, but I didn't # prove this just now, so I left in the test for now.) encoded_classifications = self._encoded_classifications() if encoded_classifications == "NanotubeSegment": # This is a nanotube segment, so write the parameters into an info # record so we can read and restore them in the next session. # --Mark 2008-04-12 assert self.nanotube mapping.write("info opengroup nanotube-parameters = %d, %d, %s, %s\n" \ % (self.nanotube.getChiralityN(), self.nanotube.getChiralityM(), self.nanotube.getType(), self.nanotube.getEndings())) pass return def readmmp_info_opengroup_setitem(self, key, val, interp): """ [extends superclass method] """ #bruce 080507 refactoring (split this out of the superclass method) if key == ['nanotube-parameters']: # val includes all the parameters, separated by commas. n, m, type, endings = val.split(",") self.n = int(n) self.m = int(m) self.type = type.lstrip() self.endings = endings.lstrip() # Create the nanotube. from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() # Returns a 5x5 CNT. self.nanotube.setChirality(self.n, self.m) self.nanotube.setType(self.type) self.nanotube.setEndings(self.endings) # The endpoints are recomputed every time it is edited. else: _superclass.readmmp_info_opengroup_setitem(self, key, val, interp) return def edit(self): """ Edit this NanotubeSegment. @see: EditNanotube_EditCommand """ commandSequencer = self.assy.w.commandSequencer commandSequencer.userEnterCommand('EDIT_NANOTUBE') assert commandSequencer.currentCommand.commandName == 'EDIT_NANOTUBE' commandSequencer.currentCommand.editStructure(self) return def getAxisVector(self, atomAtVectorOrigin=None): """ Returns the unit axis vector of the segment (vector between two axis end points) """ # REVIEW: use common code for this method? [bruce 081217 comment] endPoint1, endPoint2 = self.nanotube.getEndPoints() if endPoint1 is None or endPoint2 is None: return V(0, 0, 0) #@see: RotateAboutAPoint command. The following code is disabled #as it has bugs (not debugged but could be in #self.nanotube.getEndPoints). So, rotate about a point won't work for #rotating a nanotube. -- Ninad 2008-05-13 ##if atomAtVectorOrigin is not None: ###If atomAtVectorOrigin is specified, we will return a vector that ###starts at this atom and ends at endPoint1 or endPoint2 . ###Which endPoint to choose will be dicided by the distance between ###atomAtVectorOrigin and the respective endPoints. (will choose the ###frthest endPoint ##origin = atomAtVectorOrigin.posn() ##if vlen(endPoint2 - origin ) > vlen(endPoint1 - origin): ##return norm(endPoint2 - endPoint1) ##else: ##return norm(endPoint1 - endPoint2) return norm(endPoint2 - endPoint1) def setProps(self, props): """ Sets some properties. These will be used while editing the structure. (but if the structure is read from an mmp file, this won't work. As a fall back, it returns some constant values) @see: InsertNanotube_EditCommand.createStructure which calls this method. @see: self.getProps, EditNanotube_EditCommand.editStructure """ (_n, _m), _type, _endings, (_endPoint1, _endPoint2) = props from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() self.nanotube.setChirality(_n, _m) self.nanotube.setType(_type) self.nanotube.setEndings(_endings) self.nanotube.setEndPoints(_endPoint1, _endPoint2) def getProps(self): """ Returns nanotube parameters necessary for editing. @see: EditNanotube_EditCommand.editStructure where it is used. @see: EditNanotube_PropertyManager.getParameters @see: NanotubeSegmentEditCommand._createStructure """ # Recompute the endpoints in case this nanotube was read from # MMP file (which means this nanotube doesn't have endpoint # parameters yet). self.nanotube.computeEndPointsFromChunk(self.members[0]) return self.nanotube.getParameters() def isAncestorOf(self, obj): """ Checks whether the object <obj> is contained within the NanotubeSegment Example: If the object is an Atom, it checks whether the atom's chunk is a member of this NanotubeSegment (chunk.dad is self) It also considers all the logical contents of the NanotubeSegment to determine whether self is an ancestor. (returns True even for logical contents) @see: self.get_all_content_chunks() (inherited from LeafLikeGroup) @see: EditNanotube_GraphicsMode.leftDrag """ # TODO: this needs cleanup (it looks like it's made of two alternative # implems, one after the other), generalization (to use some centrally # defined notion of logical contents), and optimization. Also, if it # is still defined in more than one class, common code should be used. # [bruce 080507/081217 comment] c = None if isinstance(obj, Atom): c = obj.molecule elif isinstance(obj, Bond): chunk1 = obj.atom1.molecule chunk2 = obj.atom1.molecule if chunk1 is chunk2: c = chunk1 elif isinstance(obj, Chunk): c = obj if c is not None: if c in self.get_all_content_chunks(): # review: could optimize by (c.dad is self), at least in this class # [bruce 081217 comment] return True #NOTE: Need to check if the isinstance checks are acceptable (apparently #don't add any import cycle) if isinstance(obj, Atom): chunk = obj.molecule if chunk.dad is self: return True else: return False elif isinstance(obj, Bond): chunk1 = obj.atom1.molecule chunk2 = obj.atom1.molecule if (chunk1.dad is self) or (chunk2.dad is self): return True elif isinstance(obj, Chunk): if obj.dad is self: return True return False def node_icon(self, display_prefs): # REVIEW: use common code for this method? [bruce 081217 comment] del display_prefs if self.all_content_is_hidden(): return imagename_to_pixmap(self.hide_iconPath) else: return imagename_to_pixmap(self.iconPath) def permit_as_member(self, node, pre_updaters=True, **opts): """ [friend method for enforce_permitted_members_in_groups and subroutines] Does self permit node as a direct member, when called from enforce_permitted_members_in_groups with the same options as we are passed? @rtype: boolean [extends superclass method] """ # this method was copied from DnaStrandOrSegment and edited for this class if not LeafLikeGroup.permit_as_member(self, node, pre_updaters, ** opts): # reject if superclass would reject [bruce 081217] return False del opts assy = self.assy res = isinstance(node, assy.Chunk) #@ NEEDS SOMETHING MORE. return res pass # end of class NanotubeSegment
class NanotubeSegment(LeafLikeGroup): """ Model object which represents a Nanotube Segment inside a Nanotube Group. Internally, this is just a specialized Group containing a single chunk, itself containing all the atoms of a nanotube. """ # This should be a tuple of classifications that appear in # files_mmp._GROUP_CLASSIFICATIONS, most general first. # See comment in class Group for more info. [bruce 080115] _mmp_group_classifications = ('NanotubeSegment',) nanotube = None _endPoint1 = None _endPoint2 = None # TODO: undo or copy code for those attrs, # and updating them when the underlying structure changes. # But maybe that won't be needed, if they are replaced # by computing them from the atom geometry as needed. # [bruce 080227 comment] autodelete_when_empty = True # (but only if current command permits that for this class -- # see comment near Group.autodelete_when_empty for more info, # and implems of Command.keep_empty_group) iconPath = "ui/modeltree/NanotubeSegment.png" hide_iconPath = "ui/modeltree/NanotubeSegment-hide.png" # This partially fixes bug 2914. Copying now works, but the following # "warning" is printed to stdout: # ****************** needs _copyOfObject: <cnt.model.Nanotube.Nanotube instance at 0x164FC030> # I'm guessing this means that we need to override abstract method # _copyOfObject() of DataMixin, but I'd like to discuss this with Bruce first. # I also have confirmed that there is still a bug when editing the # copied nanotube (it will automatically move from the clipboard # to the part after it is resized). # Mark 2008-07-09. copyable_attrs = _superclass.copyable_attrs + ('nanotube',) def writemmp_other_info_opengroup(self, mapping): #bruce 080507 refactoring """ """ #bruce 080507 refactoring (split this out of Group.writemmp) # (I think the following condition is always true, but I didn't # prove this just now, so I left in the test for now.) encoded_classifications = self._encoded_classifications() if encoded_classifications == "NanotubeSegment": # This is a nanotube segment, so write the parameters into an info # record so we can read and restore them in the next session. # --Mark 2008-04-12 assert self.nanotube mapping.write("info opengroup nanotube-parameters = %d, %d, %s, %s\n" \ % (self.nanotube.getChiralityN(), self.nanotube.getChiralityM(), self.nanotube.getType(), self.nanotube.getEndings())) pass return def readmmp_info_opengroup_setitem( self, key, val, interp ): """ [extends superclass method] """ #bruce 080507 refactoring (split this out of the superclass method) if key == ['nanotube-parameters']: # val includes all the parameters, separated by commas. n, m, type, endings = val.split(",") self.n = int(n) self.m = int(m) self.type = type.lstrip() self.endings = endings.lstrip() # Create the nanotube. from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() # Returns a 5x5 CNT. self.nanotube.setChirality(self.n, self.m) self.nanotube.setType(self.type) self.nanotube.setEndings(self.endings) # The endpoints are recomputed every time it is edited. else: _superclass.readmmp_info_opengroup_setitem( self, key, val, interp) return def edit(self): """ Edit this NanotubeSegment. @see: EditNanotube_EditCommand """ commandSequencer = self.assy.w.commandSequencer commandSequencer.userEnterCommand('EDIT_NANOTUBE') assert commandSequencer.currentCommand.commandName == 'EDIT_NANOTUBE' commandSequencer.currentCommand.editStructure(self) return def getAxisVector(self, atomAtVectorOrigin = None): """ Returns the unit axis vector of the segment (vector between two axis end points) """ # REVIEW: use common code for this method? [bruce 081217 comment] endPoint1, endPoint2 = self.nanotube.getEndPoints() if endPoint1 is None or endPoint2 is None: return V(0, 0, 0) #@see: RotateAboutAPoint command. The following code is disabled #as it has bugs (not debugged but could be in #self.nanotube.getEndPoints). So, rotate about a point won't work for #rotating a nanotube. -- Ninad 2008-05-13 ##if atomAtVectorOrigin is not None: ###If atomAtVectorOrigin is specified, we will return a vector that ###starts at this atom and ends at endPoint1 or endPoint2 . ###Which endPoint to choose will be dicided by the distance between ###atomAtVectorOrigin and the respective endPoints. (will choose the ###frthest endPoint ##origin = atomAtVectorOrigin.posn() ##if vlen(endPoint2 - origin ) > vlen(endPoint1 - origin): ##return norm(endPoint2 - endPoint1) ##else: ##return norm(endPoint1 - endPoint2) return norm(endPoint2 - endPoint1) def setProps(self, props): """ Sets some properties. These will be used while editing the structure. (but if the structure is read from an mmp file, this won't work. As a fall back, it returns some constant values) @see: InsertNanotube_EditCommand.createStructure which calls this method. @see: self.getProps, EditNanotube_EditCommand.editStructure """ (_n, _m), _type, _endings, (_endPoint1, _endPoint2) = props from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() self.nanotube.setChirality(_n, _m) self.nanotube.setType(_type) self.nanotube.setEndings(_endings) self.nanotube.setEndPoints(_endPoint1, _endPoint2) def getProps(self): """ Returns nanotube parameters necessary for editing. @see: EditNanotube_EditCommand.editStructure where it is used. @see: EditNanotube_PropertyManager.getParameters @see: NanotubeSegmentEditCommand._createStructure """ # Recompute the endpoints in case this nanotube was read from # MMP file (which means this nanotube doesn't have endpoint # parameters yet). self.nanotube.computeEndPointsFromChunk(self.members[0]) return self.nanotube.getParameters() def isAncestorOf(self, obj): """ Checks whether the object <obj> is contained within the NanotubeSegment Example: If the object is an Atom, it checks whether the atom's chunk is a member of this NanotubeSegment (chunk.dad is self) It also considers all the logical contents of the NanotubeSegment to determine whether self is an ancestor. (returns True even for logical contents) @see: self.get_all_content_chunks() (inherited from LeafLikeGroup) @see: EditNanotube_GraphicsMode.leftDrag """ # TODO: this needs cleanup (it looks like it's made of two alternative # implems, one after the other), generalization (to use some centrally # defined notion of logical contents), and optimization. Also, if it # is still defined in more than one class, common code should be used. # [bruce 080507/081217 comment] c = None if isinstance(obj, Atom): c = obj.molecule elif isinstance(obj, Bond): chunk1 = obj.atom1.molecule chunk2 = obj.atom1.molecule if chunk1 is chunk2: c = chunk1 elif isinstance(obj, Chunk): c = obj if c is not None: if c in self.get_all_content_chunks(): # review: could optimize by (c.dad is self), at least in this class # [bruce 081217 comment] return True #NOTE: Need to check if the isinstance checks are acceptable (apparently #don't add any import cycle) if isinstance(obj, Atom): chunk = obj.molecule if chunk.dad is self: return True else: return False elif isinstance(obj, Bond): chunk1 = obj.atom1.molecule chunk2 = obj.atom1.molecule if (chunk1.dad is self) or (chunk2.dad is self): return True elif isinstance(obj, Chunk): if obj.dad is self: return True return False def node_icon(self, display_prefs): # REVIEW: use common code for this method? [bruce 081217 comment] del display_prefs if self.all_content_is_hidden(): return imagename_to_pixmap( self.hide_iconPath) else: return imagename_to_pixmap( self.iconPath) def permit_as_member(self, node, pre_updaters = True, **opts): """ [friend method for enforce_permitted_members_in_groups and subroutines] Does self permit node as a direct member, when called from enforce_permitted_members_in_groups with the same options as we are passed? @rtype: boolean [extends superclass method] """ # this method was copied from DnaStrandOrSegment and edited for this class if not LeafLikeGroup.permit_as_member(self, node, pre_updaters, **opts): # reject if superclass would reject [bruce 081217] return False del opts assy = self.assy res = isinstance( node, assy.Chunk) #@ NEEDS SOMETHING MORE. return res pass # end of class NanotubeSegment
class InsertNanotube_PropertyManager(DnaOrCnt_PropertyManager): """ The InsertNanotube_PropertyManager class provides a Property Manager for the B{Build > Nanotube > CNT} command. @ivar title: The title that appears in the property manager header. @type title: str @ivar pmName: The name of this property manager. This is used to set the name of the PM_Dialog object via setObjectName(). @type name: str @ivar iconPath: The relative path to the PNG file that contains a 22 x 22 icon image that appears in the PM header. @type iconPath: str """ title = "Insert Nanotube" pmName = title iconPath = "ui/actions/Command Toolbar/BuildNanotube/InsertNanotube.png" def __init__(self, command): """ Constructor for the Nanotube property manager. """ self.endPoint1 = None self.endPoint2 = None self.nanotube = NanotubeParameters() # A 5x5 CNT. _superclass.__init__(self, command) self.showTopRowButtons( PM_DONE_BUTTON | \ PM_CANCEL_BUTTON | \ PM_WHATS_THIS_BUTTON) def connect_or_disconnect_signals(self, isConnect): """ Connect or disconnect widget signals sent to their slot methods. This can be overridden in subclasses. By default it does nothing. @param isConnect: If True the widget will send the signals to the slot method. @type isConnect: boolean """ if isConnect: change_connect = self.win.connect else: change_connect = self.win.disconnect change_connect(self.ntTypeComboBox, SIGNAL("currentIndexChanged(const QString&)"), self._ntTypeComboBoxChanged) change_connect(self.chiralityNSpinBox, SIGNAL("valueChanged(int)"), self._chiralityFixup) change_connect(self.chiralityMSpinBox, SIGNAL("valueChanged(int)"), self._chiralityFixup) change_connect(self.endingsComboBox, SIGNAL("currentIndexChanged(const QString&)"), self._endingsComboBoxChanged) # This spin box is currently hidden. change_connect(self.bondLengthDoubleSpinBox, SIGNAL("valueChanged(double)"), self._bondLengthChanged) change_connect(self.showCursorTextCheckBox, SIGNAL('stateChanged(int)'), self._update_state_of_cursorTextGroupBox) def show(self): _superclass.show(self) self.updateMessage("Specify the nanotube parameters below, then click "\ "two endpoints in the graphics area to insert a nanotube.") def _update_widgets_in_PM_before_show(self): """ Update various widgets in this Property manager. Overrides MotorPropertyManager._update_widgets_in_PM_before_show. The various widgets , (e.g. spinboxes) will get values from the structure for which this propMgr is constructed for (self.editcCntroller.struct) @see: MotorPropertyManager._update_widgets_in_PM_before_show @see: self.show where it is called. """ pass def _addGroupBoxes(self): """ Add the Insert Nanotube Property Manager group boxes. """ self._pmGroupBox1 = PM_GroupBox(self, title="Endpoints") self._loadGroupBox1(self._pmGroupBox1) self._pmGroupBox1.hide() self._pmGroupBox2 = PM_GroupBox(self, title="Parameters") self._loadGroupBox2(self._pmGroupBox2) self._displayOptionsGroupBox = PM_GroupBox(self, title="Display Options") self._loadDisplayOptionsGroupBox(self._displayOptionsGroupBox) self._pmGroupBox3 = PM_GroupBox(self, title="Nanotube Distortion") self._loadGroupBox3(self._pmGroupBox3) self._pmGroupBox3.hide() #@ Temporary. self._pmGroupBox4 = PM_GroupBox(self, title="Multi-Walled CNTs") self._loadGroupBox4(self._pmGroupBox4) self._pmGroupBox4.hide() #@ Temporary. self._pmGroupBox5 = PM_GroupBox(self, title="Advanced Options") self._loadGroupBox5(self._pmGroupBox5) self._pmGroupBox5.hide() #@ Temporary. def _loadGroupBox1(self, pmGroupBox): """ Load widgets in group box 1. """ #Following toolbutton facilitates entering a temporary NanotubeLineMode #to create a CNT using endpoints of the specified line. self.specifyCntLineButton = PM_ToolButton( pmGroupBox, text="Specify Endpoints", iconPath="ui/actions/Properties Manager/Pencil.png", spanWidth=True) self.specifyCntLineButton.setCheckable(True) self.specifyCntLineButton.setAutoRaise(True) self.specifyCntLineButton.setToolButtonStyle( Qt.ToolButtonTextBesideIcon) #EndPoint1 and endPoint2 coordinates. These widgets are hidden # as of 2007- 12 - 05 self._endPoint1SpinBoxes = PM_CoordinateSpinBoxes(pmGroupBox, label="End Point 1") self.x1SpinBox = self._endPoint1SpinBoxes.xSpinBox self.y1SpinBox = self._endPoint1SpinBoxes.ySpinBox self.z1SpinBox = self._endPoint1SpinBoxes.zSpinBox self._endPoint2SpinBoxes = PM_CoordinateSpinBoxes(pmGroupBox, label="End Point 2") self.x2SpinBox = self._endPoint2SpinBoxes.xSpinBox self.y2SpinBox = self._endPoint2SpinBoxes.ySpinBox self.z2SpinBox = self._endPoint2SpinBoxes.zSpinBox self._endPoint1SpinBoxes.hide() self._endPoint2SpinBoxes.hide() def _loadGroupBox2(self, pmGroupBox): """ Load widgets in group box 2. """ _ntTypeChoices = ['Carbon', 'Boron Nitride'] self.ntTypeComboBox = \ PM_ComboBox( pmGroupBox, label = "Type:", choices = _ntTypeChoices, setAsDefault = True) self.ntRiseDoubleSpinBox = \ PM_DoubleSpinBox( pmGroupBox, label = "Rise:", value = self.nanotube.getRise(), setAsDefault = True, minimum = 2.0, maximum = 4.0, decimals = 3, singleStep = 0.01 ) self.ntRiseDoubleSpinBox.hide() # Nanotube Length self.ntLengthLineEdit = \ PM_LineEdit( pmGroupBox, label = "Nanotube Length: ", text = "0.0 Angstroms", setAsDefault = False) self.ntLengthLineEdit.setDisabled(True) self.ntLengthLineEdit.hide() # Nanotube diameter self.ntDiameterLineEdit = \ PM_LineEdit( pmGroupBox, label = "Diameter: ", setAsDefault = False) self.ntDiameterLineEdit.setDisabled(True) self.updateNanotubeDiameter() self.chiralityNSpinBox = \ PM_SpinBox( pmGroupBox, label = "Chirality (n):", value = self.nanotube.getChiralityN(), minimum = 2, maximum = 100, setAsDefault = True ) self.chiralityMSpinBox = \ PM_SpinBox( pmGroupBox, label = "Chirality (m):", value = self.nanotube.getChiralityM(), minimum = 0, maximum = 100, setAsDefault = True ) # How about having user prefs for CNT and BNNT bond lengths? # I'm guessing that if the user wants to set these values, they will # do it once and would like those bond length values persist forever. # Need to discuss with others to determine if this spinbox comes out. # --Mark 2008-03-29 self.bondLengthDoubleSpinBox = \ PM_DoubleSpinBox( pmGroupBox, label = "Bond length:", value = self.nanotube.getBondLength(), setAsDefault = True, minimum = 1.0, maximum = 3.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) #self.bondLengthDoubleSpinBox.hide() endingChoices = ["Hydrogen", "None"] # Removed:, "Nitrogen"] self.endingsComboBox= \ PM_ComboBox( pmGroupBox, label = "Endings:", choices = endingChoices, index = 0, setAsDefault = True, spanWidth = False ) def _loadGroupBox3(self, inPmGroupBox): """ Load widgets in group box 3. """ self.zDistortionDoubleSpinBox = \ PM_DoubleSpinBox( inPmGroupBox, label = "Z-distortion:", value = 0.0, setAsDefault = True, minimum = 0.0, maximum = 10.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) self.xyDistortionDoubleSpinBox = \ PM_DoubleSpinBox( inPmGroupBox, label = "XY-distortion:", value = 0.0, setAsDefault = True, minimum = 0.0, maximum = 2.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) self.twistSpinBox = \ PM_SpinBox( inPmGroupBox, label = "Twist:", value = 0, setAsDefault = True, minimum = 0, maximum = 100, # What should maximum be? suffix = " deg/A" ) self.bendSpinBox = \ PM_SpinBox( inPmGroupBox, label = "Bend:", value = 0, setAsDefault = True, minimum = 0, maximum = 360, suffix = " deg" ) def _loadGroupBox4(self, inPmGroupBox): """ Load widgets in group box 4. """ # "Number of Nanotubes" SpinBox self.mwntCountSpinBox = \ PM_SpinBox( inPmGroupBox, label = "Number:", value = 1, setAsDefault = True, minimum = 1, maximum = 10, suffix = " nanotubes" ) self.mwntCountSpinBox.setSpecialValueText("SWNT") # "Spacing" lineedit. self.mwntSpacingDoubleSpinBox = \ PM_DoubleSpinBox( inPmGroupBox, label = "Spacing:", value = 2.46, setAsDefault = True, minimum = 1.0, maximum = 10.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) def _loadGroupBox5(self, pmGroupBox): """ Load widgets in group box 5. """ self._rubberbandLineGroupBox = PM_GroupBox(pmGroupBox, title='Rubber band Line:') ntLineChoices = ['Ladder'] self.ntRubberBandLineDisplayComboBox = \ PM_ComboBox( self._rubberbandLineGroupBox , label = " Display as:", choices = ntLineChoices, setAsDefault = True) self.lineSnapCheckBox = \ PM_CheckBox(self._rubberbandLineGroupBox , text = 'Enable line snap' , widgetColumn = 1, state = Qt.Checked ) def _connect_showCursorTextCheckBox(self): """ Connect the show cursor text checkbox with user prefs_key. Overrides DnaOrCnt_PropertyManager._connect_showCursorTextCheckBox """ connect_checkbox_with_boolean_pref( self.showCursorTextCheckBox, insertNanotubeEditCommand_showCursorTextCheckBox_prefs_key) def _params_for_creating_cursorTextCheckBoxes(self): """ Returns params needed to create various cursor text checkboxes connected to prefs_keys that allow custom cursor texts. @return: A list containing tuples in the following format: ('checkBoxTextString' , preference_key). PM_PrefsCheckBoxes uses this data to create checkboxes with the the given names and connects them to the provided preference keys. (Note that PM_PrefsCheckBoxes puts thes within a GroupBox) @rtype: list @see: PM_PrefsCheckBoxes @see: self._loadDisplayOptionsGroupBox where this list is used. @see: Superclass method which is overridden here -- DnaOrCnt_PropertyManager._params_for_creating_cursorTextCheckBoxes() """ params = \ [ #Format: (" checkbox text", prefs_key) ("Nanotube length", insertNanotubeEditCommand_cursorTextCheckBox_length_prefs_key ), ("Angle", insertNanotubeEditCommand_cursorTextCheckBox_angle_prefs_key ) ] return params def _addToolTipText(self): """ Tool Tip text for widgets in the Insert Nanotube Property Manager. """ pass def _setEndPoints(self): """ Set the two endpoints of the nanotube using the values from the X, Y, Z coordinate spinboxes in the property manager. @note: The group box containing the 2 sets of XYZ spin boxes are currently hidden. """ # First endpoint (origin) of nanotube x1 = self.x1SpinBox.value() y1 = self.y1SpinBox.value() z1 = self.z1SpinBox.value() # Second endpoint (direction vector/axis) of nanotube. x2 = self.x2SpinBox.value() y2 = self.y2SpinBox.value() z2 = self.z2SpinBox.value() if not self.endPoint1: self.endPoint1 = V(x1, y1, z1) if not self.endPoint2: self.endPoint2 = V(x2, y2, z2) self.nanotube.setEndPoints(self.endPoint1, self.endPoint2) # Need arg "recompute=True", which will recompute the second # endpoint (endPoint2) using the nanotube rise. def getParameters(self): """ Return the parameters from this property manager to be used to create the nanotube. @return: A nanotube instance with its attrs set to the current parameters in the property manager. @rtype: L{Nanotube} @see: L{InsertNanotube_EditCommand._gatherParameters} where this is used """ self._setEndPoints() return (self.nanotube) def _ntTypeComboBoxChanged(self, type): """ Slot for the Type combobox. It is called whenever the Type choice is changed. @param inIndex: The new index. @type inIndex: int """ self.nanotube.setType(str(type)) self.bondLengthDoubleSpinBox.setValue(self.nanotube.getBondLength()) #self.bondLengthDoubleSpinBox.setValue(ntBondLengths[inIndex]) def _bondLengthChanged(self, bondLength): """ Slot for the B{Bond Length} spinbox. """ self.nanotube.setBondLength(bondLength) self.updateNanotubeDiameter() return def _chiralityFixup(self, spinBoxValueJunk=None): """ Slot for several validators for different parameters. This gets called whenever the user changes the n, m chirality values. @param spinBoxValueJunk: This is the Spinbox value from the valueChanged signal. It is not used. We just want to know that the spinbox value has changed. @type spinBoxValueJunk: double or None @see: PM_SpinBox.setValue() for a note about blockSignals. """ _n, _m = self.nanotube.setChirality(self.chiralityNSpinBox.value(), self.chiralityMSpinBox.value()) #self.n, self.m = self.nanotube.getChirality() #QSpinBox.setValue emits valueChanged signal. We don't want that here. #so temporarily blockSignal by passing the blockSignals flag. self.chiralityNSpinBox.setValue(_n, blockSignals=True) self.chiralityMSpinBox.setValue(_m, blockSignals=True) self.updateNanotubeDiameter() def updateNanotubeDiameter(self): """ Update the nanotube Diameter lineEdit widget. """ diameterText = "%-7.4f Angstroms" % (self.nanotube.getDiameter()) self.ntDiameterLineEdit.setText(diameterText) # ntRiseDoubleSpinBox is currently hidden. self.ntRiseDoubleSpinBox.setValue(self.nanotube.getRise()) def _endingsComboBoxChanged(self, endings): """ Slot for the B{Ending} combobox. @param endings: The option's text. @type endings: string """ self.nanotube.setEndings(str(endings)) return def _addWhatsThisText(self): """ What's This text for widgets in this Property Manager. """ whatsThis_InsertNanotube_PropertyManager(self) return
class EditNanotube_EditCommand(State_preMixin, EditCommand): """ Command to edit a NanotubeSegment (nanotube). """ # class constants GraphicsMode_class = EditNanotube_GraphicsMode PM_class = EditNanotube_PropertyManager commandName = 'EDIT_NANOTUBE' featurename = "Edit Nanotube" from utilities.constants import CL_SUBCOMMAND command_level = CL_SUBCOMMAND command_parent = 'BUILD_NANOTUBE' command_should_resume_prevMode = True command_has_its_own_PM = True flyoutToolbar = None call_makeMenus_for_each_event = True handlePoint1 = State( Point, ORIGIN) handlePoint2 = State( Point, ORIGIN) #The minimum 'stopper'length used for resize handles #@see: self._update_resizeHandle_stopper_length for details. _resizeHandle_stopper_length = State(Width, -100000) rotationHandleBasePoint1 = State( Point, ORIGIN) rotationHandleBasePoint2 = State( Point, ORIGIN) #See self._update_resizeHandle_radius where this gets changed. #also see EditNanotube_ResizeHandle to see how its implemented. handleSphereRadius1 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE) handleSphereRadius2 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE) cylinderWidth = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) cylinderWidth2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) #@TODO: modify the 'State params for rotation_distance rotation_distance1 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) rotation_distance2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) leftHandle = Instance( EditNanotube_ResizeHandle( command = _self, height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth'), origin = handlePoint1, fixedEndOfStructure = handlePoint2, direction = norm_Expr(handlePoint1 - handlePoint2), sphereRadius = handleSphereRadius1, range = (_resizeHandle_stopper_length, 10000) )) rightHandle = Instance( EditNanotube_ResizeHandle( command = _self, height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth2'), origin = handlePoint2, fixedEndOfStructure = handlePoint1, direction = norm_Expr(handlePoint2 - handlePoint1), sphereRadius = handleSphereRadius2, range = (_resizeHandle_stopper_length, 10000) )) rotationHandle1 = Instance( RotationHandle( command = _self, rotationDistanceRef = call_Expr( ObjAttr_StateRef, _self, 'rotation_distance1'), center = handlePoint1, axis = norm_Expr(handlePoint1 - handlePoint2), origin = rotationHandleBasePoint1, radiusVector = norm_Expr(rotationHandleBasePoint1 - handlePoint1) )) rotationHandle2 = Instance( RotationHandle( command = _self, rotationDistanceRef = call_Expr( ObjAttr_StateRef, _self, 'rotation_distance2'), center = handlePoint2, axis = norm_Expr(handlePoint2 - handlePoint1), origin = rotationHandleBasePoint2, radiusVector = norm_Expr(rotationHandleBasePoint2 - handlePoint2) )) def __init__(self, commandSequencer): """ Constructor for InsertDna_EditCommand """ glpane = commandSequencer.assy.glpane State_preMixin.__init__(self, glpane) EditCommand.__init__(self, commandSequencer) #Graphics handles for editing the structure . self.handles = [] self.grabbedHandle = None #Initialize DEBUG preference pref_nt_segment_resize_by_recreating_nanotube() return def editStructure(self, struct = None): EditCommand.editStructure(self, struct) if self.hasValidStructure(): #TODO 2008-03-25: better to get all parameters from self.struct and #set it in propMgr? This will mostly work except that reverse is #not true. i.e. we can not specify same set of params for #self.struct.setProps ...because endPoint1 and endPoint2 are derived. #by the structure when needed. self.propMgr.setParameters(self.struct.getProps()) #Store the previous parameters. Important to set it after you #set nanotube attrs in the propMgr. #self.previousParams is used in self._previewStructure and #self._finalizeStructure to check if self.struct changed. self.previousParams = self._gatherParameters() self._updateHandleList() self.updateHandlePositions() return def hasValidStructure(self): """ Tells the caller if this edit command has a valid structure. Overrides EditCommand.hasValidStructure() """ #(By Bruce 2008-02-13) isValid = EditCommand.hasValidStructure(self) if not isValid: return isValid # would like to check here whether it's empty of axis chunks; # instead, this will do for now (probably too slow, though): p1, p2 = self.struct.nanotube.getEndPoints() return (p1 is not None) def _getStructureType(self): """ Subclasses override this method to define their own structure type. Returns the type of the structure this editCommand supports. This is used in isinstance test. @see: EditCommand._getStructureType() (overridden here) """ return self.win.assy.NanotubeSegment def _updateHandleList(self): """ Updates the list of handles (self.handles) @see: self.editStructure @see: EditNanotube_GraphicsMode._drawHandles() """ # note: if handlePoint1 and/or handlePoint2 can change more often than this # runs, we'll need to rerun the two assignments above whenever they # change and before the handle is drawn. An easy way would be to rerun # these assignments in the draw method of our GM. [bruce 080128] self.handles = [] # guess, but seems like a good idea [bruce 080128] self.handles.append(self.leftHandle) self.handles.append(self.rightHandle) if DEBUG_ROTATION_HANDLES: self.handles.append(self.rotationHandle1) self.handles.append(self.rotationHandle2) return def updateHandlePositions(self): """ Update handle positions and also update the resize handle radii and their 'stopper' lengths. @see: self._update_resizeHandle_radius() @see: self._update_resizeHandle_stopper_length() @see: EditNanotube_GraphicsMode._drawHandles() """ self.handlePoint1 = None # Needed! self.handlePoint2 = None #TODO: Call this method less often by implementing model_changed #see bug 2729 for a planned optimization self.cylinderWidth = CYLINDER_WIDTH_DEFAULT_VALUE self.cylinderWidth2 = CYLINDER_WIDTH_DEFAULT_VALUE self._update_resizeHandle_radius() handlePoint1, handlePoint2 = self.struct.nanotube.getEndPoints() if 0: # Debug prints print "updateHandlePositions(): handlePoint1=", handlePoint1 print "updateHandlePositions(): handlePoint2=", handlePoint2 if handlePoint1 is not None and handlePoint2 is not None: # (that condition is bugfix for deleted axis segment, bruce 080213) self.handlePoint1, self.handlePoint2 = handlePoint1, handlePoint2 #Update the 'stopper' length where the resize handle being dragged #should stop. See self._update_resizeHandle_stopper_length() #for more details self._update_resizeHandle_stopper_length() if DEBUG_ROTATION_HANDLES: self.rotation_distance1 = CYLINDER_WIDTH_DEFAULT_VALUE self.rotation_distance2 = CYLINDER_WIDTH_DEFAULT_VALUE #Following computes the base points for rotation handles. #to be revised -- Ninad 2008-02-13 unitVectorAlongAxis = norm(self.handlePoint1 - self.handlePoint2) v = cross(self.glpane.lineOfSight, unitVectorAlongAxis) self.rotationHandleBasePoint1 = self.handlePoint1 + norm(v) * 4.0 self.rotationHandleBasePoint2 = self.handlePoint2 + norm(v) * 4.0 return def _update_resizeHandle_radius(self): """ Finds out the sphere radius to use for the resize handles, based on atom /chunk or glpane display (whichever decides the display of the end atoms. The default value is 1.2. @see: self.updateHandlePositions() """ self.handleSphereRadius1 = HANDLE_RADIUS_DEFAULT_VALUE self.handleSphereRadius2 = HANDLE_RADIUS_DEFAULT_VALUE return def _update_resizeHandle_stopper_length(self): """ Update the limiting length at which the resize handle being dragged should 'stop' without proceeding further in the drag direction. The segment resize handle stops when you are dragging it towards the other resizeend and the distance between the two ends reaches two duplexes. The self._resizeHandle_stopper_length computed in this method is used as a lower limit of the 'range' option provided in declaration of resize handle objects (see class definition for the details) @see: self.updateHandlePositions() """ total_length = vlen(self.handlePoint1 - self.handlePoint2) nanotubeRise = self.struct.nanotube.getRise() self._resizeHandle_stopper_length = - total_length + nanotubeRise return def _gatherParameters(self): """ Return the parameters from the property manager UI. @return: The endpoints of the nanotube. @rtype: tuple (endPoint1, endPoint2). """ return self.propMgr.getParameters() def _createStructure(self): """ Returns the current NanotubeSegment being edited with a new nanotube chunk. @return : Nanotube segment that include the new nanotube chunk. @rtype: L{NanotubeSegment} """ try: # Create a new nanotube chunk using new params. n, m, type, endings, endPoint1, endPoint2 = self._gatherParameters() from cnt.model.NanotubeParameters import NanotubeParameters self.nanotube = NanotubeParameters() nanotube = self.nanotube nanotube.setChirality(n, m) nanotube.setType(type) nanotube.setEndings(endings) nanotube.setEndPoints(endPoint1, endPoint2) position = V(0.0, 0.0, 0.0) ntChunk = nanotube.build(self.struct.name, self.win.assy, position) nanotube.computeEndPointsFromChunk(ntChunk) # Needed. self.struct.addchild(ntChunk) #WARNING 2008-03-05: #When we actually permit modifying a nanotube without recreating it, #then the following properties must be set in self._modifyStructure #as well. Needs more thought. props =(nanotube.getChirality(), nanotube.getType(), nanotube.getEndings(), nanotube.getEndPoints()) self.struct.setProps(props) return self.struct except (PluginBug, UserError): self.struct.kill() raise PluginBug("Internal error while trying to recreate a NanotubeSegment.") return None def _modifyStructure(self, params): """ Modify the structure based on the parameters specified. Overrides EditCommand._modifystructure. This method removes the old nanotube and replaces it with a new one using self._createStructure. """ if not pref_nt_segment_resize_by_recreating_nanotube(): self._modifyStructure_NEW_SEGMENT_RESIZE(params) return # To modify the structure, we need to: # 1. Delete the current nanotube chunk inside the NanotubeSegment group. # 2. Create a new new nanotube chunk using nanotube.build() # 3. Add the newly generated nanotube to this NanotubeSegment. # 4. Update all the nanotube parameters. # Note: Steps 2-4 are done in self._createStructure() assert self.struct self.struct.members[0].kill() # Delete the current nanotube chunk. self.previousParams = params self.struct = self._createStructure() return def _modifyStructure_NEW_SEGMENT_RESIZE(self, params): #@ NOT FIXED """ This resizes without recreating whole nanotube Overrides EditCommand._modifystructure. @attention: is not implemented. """ #@TODO: - rename this method from _modifyStructure_NEW_SEGMENT_RESIZE #to self._modifyStructure, after more testing #This method is used for debug prefence: #'Nanotube Segment: resize without recreating whole nanotube' #see also self.modifyStructure_NEW_SEGMENT_RESIZE assert self.struct from utilities.debug import print_compact_stack print_compact_stack("_modifyStructure_NEW_SEGMENT_RESIZE() not fixed!" ) print "Params =", params self.nanotube = params #@ length_diff = self._determine_how_to_change_length() #@ if length_diff == 0: print_compact_stack("BUG: length_diff is always ZERO." ) return elif length_diff > 0: print "Nanotube longer by ", length_diff, ", angstroms." else: print "Nanotube shorter by ", length_diff, ", angstroms." return if numberOfBasePairsToAddOrRemove != 0: #@@@@ Not reached. resizeEnd_final_position = self._get_resizeEnd_final_position( ladderEndAxisAtom, abs(numberOfBasePairsToAddOrRemove), nanotubeRise ) self.nanotube.modify(self.struct, length_diff, ladderEndAxisAtom.posn(), resizeEnd_final_position) #Find new end points of structure parameters after modification #and set these values in the propMgr. new_end1 , new_end2 = self.struct.nanotube.getEndPoints() #@ params_to_set_in_propMgr = (new_end1, new_end2) #TODO: Need to set these params in the PM #and then self.previousParams = params_to_set_in_propMgr self.previousParams = params return def _get_resizeEnd_final_position(self, ladderEndAxisAtom, numberOfBases, nanotubeRise): final_position = None if self.grabbedHandle: final_position = self.grabbedHandle.currentPosition else: other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom) axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn() segment_length_to_add = 0 #@ final_position = ladderEndAxisAtom.posn() + norm(axis_vector)*segment_length_to_add return final_position def getStructureName(self): """ Returns the name string of self.struct if there is a valid structure. Otherwise returns None. This information is used by the name edit field of this command's PM when we call self.propMgr.show() @see: EditNanotube_PropertyManager.show() @see: self.setStructureName """ if self.hasValidStructure(): return self.struct.name return None def setStructureName(self, name): """ Sets the name of self.struct to param <name> (if there is a valid structure. The PM of this command callss this method while closing itself @param name: name of the structure to be set. @type name: string @see: EditNanotube_PropertyManager.close() @see: self.getStructureName() """ #@BUG: We call this method in self.propMgr.close(). But propMgr.close() #is called even when the command is 'cancelled'. That means the #structure will get changed even when user hits cancel button or #exits the command by clicking on empty space. #This should really be done in self._finalizeStructure but that #method doesn't get called when you click on empty space to exit #the command. See EditNanotube_GraphicsMode.leftUp for a detailed #comment. if self.hasValidStructure(): self.struct.name = name return def getCursorText(self): """ This is used as a callback method in NanotubeLine mode @see: NanotubeLineMode.setParams, NanotubeLineMode_GM.Draw """ if self.grabbedHandle is None: return text = '' textColor = env.prefs[cursorTextColor_prefs_key] if not env.prefs[editNanotubeEditCommand_showCursorTextCheckBox_prefs_key]: return text, textColor currentPosition = self.grabbedHandle.currentPosition fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure nanotubeLength = vlen( currentPosition - fixedEndOfStructure ) nanotubeLengthString = self._getCursorText_length(nanotubeLength) text = nanotubeLengthString #@TODO: The following updates the PM as the cursor moves. #Need to rename this method so that you that it also does more things #than just to return a textString -- Ninad 2007-12-20 self.propMgr.ntLengthLineEdit.setText(nanotubeLengthString) return text, textColor def _getCursorText_length(self, nanotubeLength): """ Returns a string that gives the length of the Nanotube for the cursor text """ nanotubeLengthString = '' if env.prefs[editNanotubeEditCommand_cursorTextCheckBox_length_prefs_key]: lengthUnitString = 'A' #change the unit of length to nanometers if the length is > 10A #fixes part of bug 2856 if nanotubeLength > 10.0: lengthUnitString = 'nm' nanotubeLength = nanotubeLength * 0.1 nanotubeLengthString = "%5.3f%s"%(nanotubeLength, lengthUnitString) return nanotubeLengthString def modifyStructure(self): """ Called when a resize handle is dragged to change the length of the segment. (Called upon leftUp) . This method assigns the new parameters for the segment after it is resized and calls preview_or_finalize_structure which does the rest of the job. Note that Client should call this public method and should never call the private method self._modifyStructure. self._modifyStructure is called only by self.preview_or_finalize_structure @see: B{EditNanotube_ResizeHandle.on_release} (the caller) @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the the relevent method in DragHandler API. ) @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior} @see: B{self.preview_or_finalize_structure } @see: B{self._modifyStructure} As of 2008-02-01 it recreates the structure @see: a note in self._createStructure() about use of ntSegment.setProps """ if not pref_nt_segment_resize_by_recreating_nanotube(): self.modifyStructure_NEW_SEGMENT_RESIZE() return if self.grabbedHandle is None: return self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure self.propMgr.endPoint2 = self.grabbedHandle.currentPosition #@length = vlen(self.propMgr.endPoint1 - self.propMgr.endPoint2 ) #@ self.preview_or_finalize_structure(previewing = True) self.updateHandlePositions() self.glpane.gl_update() return def modifyStructure_NEW_SEGMENT_RESIZE(self): #@ NOT FIXED """ Called when a resize handle is dragged to change the length of the segment. (Called upon leftUp) . This method assigns the new parameters for the segment after it is resized and calls preview_or_finalize_structure which does the rest of the job. Note that Client should call this public method and should never call the private method self._modifyStructure. self._modifyStructure is called only by self.preview_or_finalize_structure @see: B{EditNanotube_ResizeHandle.on_release} (the caller) @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the the relevent method in DragHandler API. ) @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior} @see: B{self.preview_or_finalize_structure } @see: B{self._modifyStructure} As of 2008-02-01 it recreates the structure @see: a note in self._createStructure() about use of ntSegment.setProps """ #TODO: need to cleanup this and may be use use something like #self.previousParams = params in the end -- 2008-03-24 (midnight) #@TODO: - rename this method from modifyStructure_NEW_SEGMENT_RESIZE #to self.modifyStructure, after more testing #This method is used for debug prefence: #'Nanotube Segment: resize without recreating whole duplex' #see also self._modifyStructure_NEW_SEGMENT_RESIZE if self.grabbedHandle is None: return self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure self.propMgr.endPoint2 = self.grabbedHandle.currentPosition DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD = False if DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD: # TO DO: this entire block of code. --Mark 2008-04-03 print_compact_stack("modifyStructure_NEW_SEGMENT_RESIZE(): NOT FIXED") length = vlen(self.grabbedHandle.fixedEndOfStructure - \ self.grabbedHandle.currentPosition ) endAtom1, endAtom2 = self.struct.getAxisEndAtoms() #@ for atm in (endAtom1, endAtom2): if not same_vals(self.grabbedHandle.fixedEndOfStructure, atm.posn()): ladderEndAxisAtom = atm break endPoint1, endPoint2 = self.struct.nanotube.getEndPoints() old_dulex_length = vlen(endPoint1 - endPoint2) nanotubeRise = self.struct.getProps() #@ params_to_set_in_propMgr = ( self.grabbedHandle.origin, self.grabbedHandle.currentPosition, ) ##self._modifyStructure(params) ############################################ self.nanotube = NanotubeParameters() #@ Creates 5x5 CNT. Missing PM params. length_diff = self._determine_how_to_change_length() ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end() #@ #@ Nanotube class needs modify() method. self.nanotube.modify(self.struct, length_diff, ladderEndAxisAtom.posn(), self.grabbedHandle.currentPosition) #TODO: Important note: How does NE1 know that structure is modified? #Because number of base pairs parameter in the PropMgr changes as you #drag the handle . This is done in self.getCursorText() ... not the #right place to do it. OR that method needs to be renamed to reflect #this as suggested in that method -- Ninad 2008-03-25 self.preview_or_finalize_structure(previewing = True) ##self.previousParams = params_to_set_in_propMgr self.glpane.gl_update() return def get_axisEndAtom_at_resize_end(self): ladderEndAxisAtom = None if self.grabbedHandle is not None: ladderEndAxisAtom = self.struct.getAxisEndAtomAtPosition(self.grabbedHandle.origin) else: endAtom1, endAtom2 = self.struct.getAxisEndAtoms() ladderEndAxisAtom = endAtom2 return ladderEndAxisAtom def _determine_how_to_change_length(self): #@ NEEDS WORK """ Returns the difference in length between the original nanotube and the modified nanotube, where: 0 = no change in length > 0 = lengthen < 0 = trim """ nanotubeRise = self.struct.nanotube.getRise() endPoint1, endPoint2 = self.struct.nanotube.getEndPoints() #@ original_nanotube_length = vlen(endPoint1 - endPoint2) new_nanotube_length = vlen(endPoint1 - endPoint2) #@ return new_nanotube_length - original_nanotube_length #@ ALWAYS RETURNS ZERO def makeMenus(self): """ Create context menu for this command. """ if not hasattr(self, 'graphicsMode'): return selobj = self.glpane.selobj if selobj is None: return self.Menu_spec = [] highlightedChunk = None if isinstance(selobj, Chunk): highlightedChunk = selobj if isinstance(selobj, Atom): highlightedChunk = selobj.molecule elif isinstance(selobj, Bond): chunk1 = selobj.atom1.molecule chunk2 = selobj.atom2.molecule if chunk1 is chunk2 and chunk1 is not None: highlightedChunk = chunk1 if highlightedChunk is None: return highlightedChunk.make_glpane_cmenu_items(self.Menu_spec, self) return
def modifyStructure_NEW_SEGMENT_RESIZE(self): #@ NOT FIXED """ Called when a resize handle is dragged to change the length of the segment. (Called upon leftUp) . This method assigns the new parameters for the segment after it is resized and calls preview_or_finalize_structure which does the rest of the job. Note that Client should call this public method and should never call the private method self._modifyStructure. self._modifyStructure is called only by self.preview_or_finalize_structure @see: B{EditNanotube_ResizeHandle.on_release} (the caller) @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the the relevent method in DragHandler API. ) @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior} @see: B{self.preview_or_finalize_structure } @see: B{self._modifyStructure} As of 2008-02-01 it recreates the structure @see: a note in self._createStructure() about use of ntSegment.setProps """ #TODO: need to cleanup this and may be use use something like #self.previousParams = params in the end -- 2008-03-24 (midnight) #@TODO: - rename this method from modifyStructure_NEW_SEGMENT_RESIZE #to self.modifyStructure, after more testing #This method is used for debug prefence: #'Nanotube Segment: resize without recreating whole duplex' #see also self._modifyStructure_NEW_SEGMENT_RESIZE if self.grabbedHandle is None: return self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure self.propMgr.endPoint2 = self.grabbedHandle.currentPosition DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD = False if DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD: # TO DO: this entire block of code. --Mark 2008-04-03 print_compact_stack("modifyStructure_NEW_SEGMENT_RESIZE(): NOT FIXED") length = vlen(self.grabbedHandle.fixedEndOfStructure - \ self.grabbedHandle.currentPosition ) endAtom1, endAtom2 = self.struct.getAxisEndAtoms() #@ for atm in (endAtom1, endAtom2): if not same_vals(self.grabbedHandle.fixedEndOfStructure, atm.posn()): ladderEndAxisAtom = atm break endPoint1, endPoint2 = self.struct.nanotube.getEndPoints() old_dulex_length = vlen(endPoint1 - endPoint2) nanotubeRise = self.struct.getProps() #@ params_to_set_in_propMgr = ( self.grabbedHandle.origin, self.grabbedHandle.currentPosition, ) ##self._modifyStructure(params) ############################################ self.nanotube = NanotubeParameters() #@ Creates 5x5 CNT. Missing PM params. length_diff = self._determine_how_to_change_length() ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end() #@ #@ Nanotube class needs modify() method. self.nanotube.modify(self.struct, length_diff, ladderEndAxisAtom.posn(), self.grabbedHandle.currentPosition) #TODO: Important note: How does NE1 know that structure is modified? #Because number of base pairs parameter in the PropMgr changes as you #drag the handle . This is done in self.getCursorText() ... not the #right place to do it. OR that method needs to be renamed to reflect #this as suggested in that method -- Ninad 2008-03-25 self.preview_or_finalize_structure(previewing = True) ##self.previousParams = params_to_set_in_propMgr self.glpane.gl_update() return
class InsertNanotube_PropertyManager( DnaOrCnt_PropertyManager): """ The InsertNanotube_PropertyManager class provides a Property Manager for the B{Build > Nanotube > CNT} command. @ivar title: The title that appears in the property manager header. @type title: str @ivar pmName: The name of this property manager. This is used to set the name of the PM_Dialog object via setObjectName(). @type name: str @ivar iconPath: The relative path to the PNG file that contains a 22 x 22 icon image that appears in the PM header. @type iconPath: str """ title = "Insert Nanotube" pmName = title iconPath = "ui/actions/Command Toolbar/BuildNanotube/InsertNanotube.png" def __init__( self, command ): """ Constructor for the Nanotube property manager. """ self.endPoint1 = None self.endPoint2 = None self.nanotube = NanotubeParameters() # A 5x5 CNT. _superclass.__init__( self, command) self.showTopRowButtons( PM_DONE_BUTTON | \ PM_CANCEL_BUTTON | \ PM_WHATS_THIS_BUTTON) def connect_or_disconnect_signals(self, isConnect): """ Connect or disconnect widget signals sent to their slot methods. This can be overridden in subclasses. By default it does nothing. @param isConnect: If True the widget will send the signals to the slot method. @type isConnect: boolean """ if isConnect: change_connect = self.win.connect else: change_connect = self.win.disconnect change_connect( self.ntTypeComboBox, SIGNAL("currentIndexChanged(const QString&)"), self._ntTypeComboBoxChanged ) change_connect(self.chiralityNSpinBox, SIGNAL("valueChanged(int)"), self._chiralityFixup) change_connect(self.chiralityMSpinBox, SIGNAL("valueChanged(int)"), self._chiralityFixup) change_connect(self.endingsComboBox, SIGNAL("currentIndexChanged(const QString&)"), self._endingsComboBoxChanged ) # This spin box is currently hidden. change_connect(self.bondLengthDoubleSpinBox, SIGNAL("valueChanged(double)"), self._bondLengthChanged) change_connect(self.showCursorTextCheckBox, SIGNAL('stateChanged(int)'), self._update_state_of_cursorTextGroupBox) def show(self): _superclass.show(self) self.updateMessage("Specify the nanotube parameters below, then click "\ "two endpoints in the graphics area to insert a nanotube.") def _update_widgets_in_PM_before_show(self): """ Update various widgets in this Property manager. Overrides MotorPropertyManager._update_widgets_in_PM_before_show. The various widgets , (e.g. spinboxes) will get values from the structure for which this propMgr is constructed for (self.editcCntroller.struct) @see: MotorPropertyManager._update_widgets_in_PM_before_show @see: self.show where it is called. """ pass def _addGroupBoxes( self ): """ Add the Insert Nanotube Property Manager group boxes. """ self._pmGroupBox1 = PM_GroupBox( self, title = "Endpoints" ) self._loadGroupBox1( self._pmGroupBox1 ) self._pmGroupBox1.hide() self._pmGroupBox2 = PM_GroupBox( self, title = "Parameters" ) self._loadGroupBox2( self._pmGroupBox2 ) self._displayOptionsGroupBox = PM_GroupBox( self, title = "Display Options" ) self._loadDisplayOptionsGroupBox( self._displayOptionsGroupBox ) self._pmGroupBox3 = PM_GroupBox( self, title = "Nanotube Distortion" ) self._loadGroupBox3( self._pmGroupBox3 ) self._pmGroupBox3.hide() #@ Temporary. self._pmGroupBox4 = PM_GroupBox( self, title = "Multi-Walled CNTs" ) self._loadGroupBox4( self._pmGroupBox4 ) self._pmGroupBox4.hide() #@ Temporary. self._pmGroupBox5 = PM_GroupBox( self, title = "Advanced Options" ) self._loadGroupBox5( self._pmGroupBox5 ) self._pmGroupBox5.hide() #@ Temporary. def _loadGroupBox1(self, pmGroupBox): """ Load widgets in group box 1. """ #Following toolbutton facilitates entering a temporary NanotubeLineMode #to create a CNT using endpoints of the specified line. self.specifyCntLineButton = PM_ToolButton( pmGroupBox, text = "Specify Endpoints", iconPath = "ui/actions/Properties Manager/Pencil.png", spanWidth = True ) self.specifyCntLineButton.setCheckable(True) self.specifyCntLineButton.setAutoRaise(True) self.specifyCntLineButton.setToolButtonStyle( Qt.ToolButtonTextBesideIcon) #EndPoint1 and endPoint2 coordinates. These widgets are hidden # as of 2007- 12 - 05 self._endPoint1SpinBoxes = PM_CoordinateSpinBoxes(pmGroupBox, label = "End Point 1") self.x1SpinBox = self._endPoint1SpinBoxes.xSpinBox self.y1SpinBox = self._endPoint1SpinBoxes.ySpinBox self.z1SpinBox = self._endPoint1SpinBoxes.zSpinBox self._endPoint2SpinBoxes = PM_CoordinateSpinBoxes(pmGroupBox, label = "End Point 2") self.x2SpinBox = self._endPoint2SpinBoxes.xSpinBox self.y2SpinBox = self._endPoint2SpinBoxes.ySpinBox self.z2SpinBox = self._endPoint2SpinBoxes.zSpinBox self._endPoint1SpinBoxes.hide() self._endPoint2SpinBoxes.hide() def _loadGroupBox2(self, pmGroupBox): """ Load widgets in group box 2. """ _ntTypeChoices = ['Carbon', 'Boron Nitride'] self.ntTypeComboBox = \ PM_ComboBox( pmGroupBox, label = "Type:", choices = _ntTypeChoices, setAsDefault = True) self.ntRiseDoubleSpinBox = \ PM_DoubleSpinBox( pmGroupBox, label = "Rise:", value = self.nanotube.getRise(), setAsDefault = True, minimum = 2.0, maximum = 4.0, decimals = 3, singleStep = 0.01 ) self.ntRiseDoubleSpinBox.hide() # Nanotube Length self.ntLengthLineEdit = \ PM_LineEdit( pmGroupBox, label = "Nanotube Length: ", text = "0.0 Angstroms", setAsDefault = False) self.ntLengthLineEdit.setDisabled(True) self.ntLengthLineEdit.hide() # Nanotube diameter self.ntDiameterLineEdit = \ PM_LineEdit( pmGroupBox, label = "Diameter: ", setAsDefault = False) self.ntDiameterLineEdit.setDisabled(True) self.updateNanotubeDiameter() self.chiralityNSpinBox = \ PM_SpinBox( pmGroupBox, label = "Chirality (n):", value = self.nanotube.getChiralityN(), minimum = 2, maximum = 100, setAsDefault = True ) self.chiralityMSpinBox = \ PM_SpinBox( pmGroupBox, label = "Chirality (m):", value = self.nanotube.getChiralityM(), minimum = 0, maximum = 100, setAsDefault = True ) # How about having user prefs for CNT and BNNT bond lengths? # I'm guessing that if the user wants to set these values, they will # do it once and would like those bond length values persist forever. # Need to discuss with others to determine if this spinbox comes out. # --Mark 2008-03-29 self.bondLengthDoubleSpinBox = \ PM_DoubleSpinBox( pmGroupBox, label = "Bond length:", value = self.nanotube.getBondLength(), setAsDefault = True, minimum = 1.0, maximum = 3.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) #self.bondLengthDoubleSpinBox.hide() endingChoices = ["Hydrogen", "None"] # Removed:, "Nitrogen"] self.endingsComboBox= \ PM_ComboBox( pmGroupBox, label = "Endings:", choices = endingChoices, index = 0, setAsDefault = True, spanWidth = False ) def _loadGroupBox3(self, inPmGroupBox): """ Load widgets in group box 3. """ self.zDistortionDoubleSpinBox = \ PM_DoubleSpinBox( inPmGroupBox, label = "Z-distortion:", value = 0.0, setAsDefault = True, minimum = 0.0, maximum = 10.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) self.xyDistortionDoubleSpinBox = \ PM_DoubleSpinBox( inPmGroupBox, label = "XY-distortion:", value = 0.0, setAsDefault = True, minimum = 0.0, maximum = 2.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) self.twistSpinBox = \ PM_SpinBox( inPmGroupBox, label = "Twist:", value = 0, setAsDefault = True, minimum = 0, maximum = 100, # What should maximum be? suffix = " deg/A" ) self.bendSpinBox = \ PM_SpinBox( inPmGroupBox, label = "Bend:", value = 0, setAsDefault = True, minimum = 0, maximum = 360, suffix = " deg" ) def _loadGroupBox4(self, inPmGroupBox): """ Load widgets in group box 4. """ # "Number of Nanotubes" SpinBox self.mwntCountSpinBox = \ PM_SpinBox( inPmGroupBox, label = "Number:", value = 1, setAsDefault = True, minimum = 1, maximum = 10, suffix = " nanotubes" ) self.mwntCountSpinBox.setSpecialValueText("SWNT") # "Spacing" lineedit. self.mwntSpacingDoubleSpinBox = \ PM_DoubleSpinBox( inPmGroupBox, label = "Spacing:", value = 2.46, setAsDefault = True, minimum = 1.0, maximum = 10.0, singleStep = 0.1, decimals = 3, suffix = " Angstroms" ) def _loadGroupBox5(self, pmGroupBox): """ Load widgets in group box 5. """ self._rubberbandLineGroupBox = PM_GroupBox( pmGroupBox, title = 'Rubber band Line:') ntLineChoices = ['Ladder'] self.ntRubberBandLineDisplayComboBox = \ PM_ComboBox( self._rubberbandLineGroupBox , label = " Display as:", choices = ntLineChoices, setAsDefault = True) self.lineSnapCheckBox = \ PM_CheckBox(self._rubberbandLineGroupBox , text = 'Enable line snap' , widgetColumn = 1, state = Qt.Checked ) def _connect_showCursorTextCheckBox(self): """ Connect the show cursor text checkbox with user prefs_key. Overrides DnaOrCnt_PropertyManager._connect_showCursorTextCheckBox """ connect_checkbox_with_boolean_pref( self.showCursorTextCheckBox , insertNanotubeEditCommand_showCursorTextCheckBox_prefs_key ) def _params_for_creating_cursorTextCheckBoxes(self): """ Returns params needed to create various cursor text checkboxes connected to prefs_keys that allow custom cursor texts. @return: A list containing tuples in the following format: ('checkBoxTextString' , preference_key). PM_PrefsCheckBoxes uses this data to create checkboxes with the the given names and connects them to the provided preference keys. (Note that PM_PrefsCheckBoxes puts thes within a GroupBox) @rtype: list @see: PM_PrefsCheckBoxes @see: self._loadDisplayOptionsGroupBox where this list is used. @see: Superclass method which is overridden here -- DnaOrCnt_PropertyManager._params_for_creating_cursorTextCheckBoxes() """ params = \ [ #Format: (" checkbox text", prefs_key) ("Nanotube length", insertNanotubeEditCommand_cursorTextCheckBox_length_prefs_key ), ("Angle", insertNanotubeEditCommand_cursorTextCheckBox_angle_prefs_key ) ] return params def _addToolTipText(self): """ Tool Tip text for widgets in the Insert Nanotube Property Manager. """ pass def _setEndPoints(self): """ Set the two endpoints of the nanotube using the values from the X, Y, Z coordinate spinboxes in the property manager. @note: The group box containing the 2 sets of XYZ spin boxes are currently hidden. """ # First endpoint (origin) of nanotube x1 = self.x1SpinBox.value() y1 = self.y1SpinBox.value() z1 = self.z1SpinBox.value() # Second endpoint (direction vector/axis) of nanotube. x2 = self.x2SpinBox.value() y2 = self.y2SpinBox.value() z2 = self.z2SpinBox.value() if not self.endPoint1: self.endPoint1 = V(x1, y1, z1) if not self.endPoint2: self.endPoint2 = V(x2, y2, z2) self.nanotube.setEndPoints(self.endPoint1, self.endPoint2) # Need arg "recompute=True", which will recompute the second # endpoint (endPoint2) using the nanotube rise. def getParameters(self): """ Return the parameters from this property manager to be used to create the nanotube. @return: A nanotube instance with its attrs set to the current parameters in the property manager. @rtype: L{Nanotube} @see: L{InsertNanotube_EditCommand._gatherParameters} where this is used """ self._setEndPoints() return (self.nanotube) def _ntTypeComboBoxChanged( self, type ): """ Slot for the Type combobox. It is called whenever the Type choice is changed. @param inIndex: The new index. @type inIndex: int """ self.nanotube.setType(str(type)) self.bondLengthDoubleSpinBox.setValue(self.nanotube.getBondLength()) #self.bondLengthDoubleSpinBox.setValue(ntBondLengths[inIndex]) def _bondLengthChanged(self, bondLength): """ Slot for the B{Bond Length} spinbox. """ self.nanotube.setBondLength(bondLength) self.updateNanotubeDiameter() return def _chiralityFixup(self, spinBoxValueJunk = None): """ Slot for several validators for different parameters. This gets called whenever the user changes the n, m chirality values. @param spinBoxValueJunk: This is the Spinbox value from the valueChanged signal. It is not used. We just want to know that the spinbox value has changed. @type spinBoxValueJunk: double or None @see: PM_SpinBox.setValue() for a note about blockSignals. """ _n, _m = self.nanotube.setChirality(self.chiralityNSpinBox.value(), self.chiralityMSpinBox.value()) #self.n, self.m = self.nanotube.getChirality() #QSpinBox.setValue emits valueChanged signal. We don't want that here. #so temporarily blockSignal by passing the blockSignals flag. self.chiralityNSpinBox.setValue(_n, blockSignals = True) self.chiralityMSpinBox.setValue(_m, blockSignals = True) self.updateNanotubeDiameter() def updateNanotubeDiameter(self): """ Update the nanotube Diameter lineEdit widget. """ diameterText = "%-7.4f Angstroms" % (self.nanotube.getDiameter()) self.ntDiameterLineEdit.setText(diameterText) # ntRiseDoubleSpinBox is currently hidden. self.ntRiseDoubleSpinBox.setValue(self.nanotube.getRise()) def _endingsComboBoxChanged(self, endings): """ Slot for the B{Ending} combobox. @param endings: The option's text. @type endings: string """ self.nanotube.setEndings(str(endings)) return def _addWhatsThisText(self): """ What's This text for widgets in this Property Manager. """ whatsThis_InsertNanotube_PropertyManager(self) return