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 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