def numberOfBasesChanged(self, numberOfBases):
     """
     Slot for the B{Number of Bases} spinbox.
     """
     duplexRise = self.duplexRiseDoubleSpinBox.value()
     # Update the Duplex Length lineEdit widget.
     text = str(getDuplexLength(self._conformation, numberOfBases, duplexRise=duplexRise)) + " Angstroms"
     self.duplexLengthLineEdit.setText(text)
     return
Beispiel #2
0
 def numberOfBasesChanged(self, numberOfBases):
     """
     Slot for the B{Number of Bases} spinbox.
     """
     # Update the Duplex Length lineEdit widget.
     text = str(getDuplexLength(self._conformation,
                                numberOfBases,
                                self._duplexRise)) \
          + " Angstroms"
     self.duplexLengthLineEdit.setText(text)
     return
    def _get_resizeEnd_final_position(self, ladderEndAxisAtom, numberOfBases, duplexRise):

        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 = getDuplexLength("B-DNA", numberOfBases, duplexRise=duplexRise)

            final_position = ladderEndAxisAtom.posn() + norm(axis_vector) * segment_length_to_add

        return final_position
    def _get_resizeEnd_final_position(self, ladderEndAxisAtom, numberOfBases,
                                      duplexRise):

        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 = getDuplexLength('B-DNA',
                                                    numberOfBases,
                                                    duplexRise=duplexRise)

            final_position = ladderEndAxisAtom.posn(
            ) + norm(axis_vector) * segment_length_to_add

        return final_position
Beispiel #5
0
    def __init__(self, command):
        """
        Constructor for the DNA Duplex property manager.
        """
        self.endPoint1 = None
        self.endPoint2 = None

        self._conformation = "B-DNA"
        self._numberOfBases = 0
        self._basesPerTurn = getDuplexBasesPerTurn(self._conformation)
        self._duplexRise = getDuplexRise(self._conformation)
        self._duplexLength = getDuplexLength(self._conformation,
                                             self._numberOfBases)

        _superclass.__init__(self, command)

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_CANCEL_BUTTON | \
                                PM_WHATS_THIS_BUTTON)
Beispiel #6
0
    def _get_resizeEnd_final_position(self, 
                                      resizeEndAxisAtom, 
                                      numberOfBases, 
                                      duplexRise):

        final_position = None   
        if self.grabbedHandle:
            final_position = self.grabbedHandle.currentPosition
        else:
            dnaSegment = resizeEndAxisAtom.molecule.parent_node_of_class(self.assy.DnaSegment)
            other_axisEndAtom = dnaSegment.getOtherAxisEndAtom(resizeEndAxisAtom)
            axis_vector = resizeEndAxisAtom.posn() - other_axisEndAtom.posn()
            segment_length_to_add = getDuplexLength('B-DNA', 
                                                    numberOfBases, 
                                                    duplexRise = duplexRise)

            final_position = resizeEndAxisAtom.posn() + norm(axis_vector)*segment_length_to_add

        return final_position
    def _get_resizeEnd_final_position(self, 
                                      ladderEndAxisAtom, 
                                      numberOfBases, 
                                      duplexRise):
        """
        Returns the final position of the resize end. 
        Note that we can not use the grabbedHandle's currentPosition as the 
        final position because resize handle is placed at an 'average' position.
        So we must compute the correct vector using the 'self.currentStruct'
        """

        final_position = None  

        other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)

        if other_axisEndAtom is None:
            return None

        axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
        segment_length_to_add = getDuplexLength('B-DNA', 
                                                numberOfBases, 
                                                duplexRise = duplexRise)

        signFactor = + 1

        ##if self.grabbedHandle is not None:
            ##vec = self.grabbedHandle.currentPosition - \
            ##       self.grabbedHandle.fixedEndOfStructure

            ##if dot(vec, axis_vector) < 0:
                ##signFactor = -1
            ##else:
                ##signFactor = +1


        axis_vector = axis_vector * signFactor


        final_position = ladderEndAxisAtom.posn() + \
                       norm(axis_vector)*segment_length_to_add

        return final_position
    def __init__( self, command ):
        """
        Constructor for the DNA Duplex property manager.
        """
        self.endPoint1 = None
        self.endPoint2 = None

        self._conformation  = "B-DNA"
        self._numberOfBases = 0
        self._basesPerTurn  = getDuplexBasesPerTurn(self._conformation)
        self._duplexRise    = getDuplexRise(self._conformation)
        self._duplexLength  = getDuplexLength(self._conformation,
                                              self._numberOfBases)


        _superclass.__init__( self, command)

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_CANCEL_BUTTON | \
                                PM_WHATS_THIS_BUTTON)
    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)
        duplexRise = self.struct.getDuplexRise()

        # Length of the duplex for 2 base pairs
        two_bases_length = getDuplexLength("B-DNA", 2, duplexRise=duplexRise)

        self._resizeHandle_stopper_length = -total_length + two_bases_length
    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)
        duplexRise = self.struct.getDuplexRise()

        #Length of the duplex for 2 base pairs
        two_bases_length = getDuplexLength('B-DNA', 2, duplexRise=duplexRise)

        self._resizeHandle_stopper_length = -total_length + two_bases_length
Beispiel #11
0
    def _get_resizeEnd_final_position(self, ladderEndAxisAtom, numberOfBases,
                                      duplexRise):
        """
        Returns the final position of the resize end.
        Note that we can not use the grabbedHandle's currentPosition as the
        final position because resize handle is placed at an 'average' position.
        So we must compute the correct vector using the 'self.currentStruct'
        """

        final_position = None

        other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)

        if other_axisEndAtom is None:
            return None

        axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
        segment_length_to_add = getDuplexLength('B-DNA',
                                                numberOfBases,
                                                duplexRise=duplexRise)

        signFactor = +1

        ##if self.grabbedHandle is not None:
        ##vec = self.grabbedHandle.currentPosition - \
        ##       self.grabbedHandle.fixedEndOfStructure

        ##if dot(vec, axis_vector) < 0:
        ##signFactor = -1
        ##else:
        ##signFactor = +1

        axis_vector = axis_vector * signFactor


        final_position = ladderEndAxisAtom.posn() + \
                       norm(axis_vector)*segment_length_to_add

        return final_position
    def _createSegment(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        numberOfBases, \
                     dnaForm, \
                     dnaModel, \
                     basesPerTurn, \
                     duplexRise, \
                     endPoint1, \
                     endPoint2 = params

        if numberOfBases < 2:
            #Don't create a duplex with only one or 0 bases! 
            #Reset a few variables. This should be done by calling a separate 
            #method on command (there is similar code in self.createStructures)
            #as well. 
            self.mouseClickPoints = []
            self.graphicsMode.resetVariables()

            msg  = redmsg("Cannot preview/insert a DNA duplex with less than 2 base pairs.")
            self.propMgr.updateMessage(msg)

            self.dna = None # Fixes bug 2530. Mark 2007-09-02
            return None
        else: 
            msg = "Specify two points in the 3D Graphics Area to define the "\
                "endpoints of the DNA duplex"
            self.propMgr.updateMessage(msg)


        #If user enters the number of basepairs and hits preview i.e. endPoint1
        #and endPoint2 are not entered by the user and thus have default value 
        #of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        #endPoint2 using the duplex length. 
        #Do not use '==' equality check on vectors! its a bug. Use same_vals 
        # or Veq instead. 
        if Veq(endPoint1 , endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + \
                      self.win.glpane.right * \
                      getDuplexLength('B-DNA', numberOfBases)

        if dnaForm == 'B-DNA':
            if dnaModel == 'PAM3':
                dna = B_Dna_PAM3()
            elif dnaModel == 'PAM5':
                dna = B_Dna_PAM5()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna  =  dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name


        # Create the model tree group node. 
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()

        if self._parentDnaGroup is None:
            print_compact_stack("bug: Parent DnaGroup in DnaDuplex_EditCommand"\
                                "is None. This means the previous command "\
                                "was not 'BuildDna_EditCommand' Ignoring for now")
            if self._fallbackDnaGroup is None:
                self._createFallbackDnaGroup()

            dnaGroup = self._fallbackDnaGroup
        else:
            dnaGroup = self._parentDnaGroup


        dnaSegment = DnaSegment(self.name, 
                                self.win.assy,
                                dnaGroup,
                                editCommand = self  )
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis
            dna.make(dnaSegment, 
                     numberOfBases, 
                     basesPerTurn, 
                     duplexRise,
                     endPoint1,
                     endPoint2)

            #set some properties such as duplexRise and number of bases per turn
            #This information will be stored on the DnaSegment object so that
            #it can be retrieved while editing this object. 
            #This works with or without dna_updater. Now the question is 
            #should these props be assigned to the DnaSegment in 
            #dnaDuplex.make() itself ? This needs to be answered while modifying
            #make() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls 
            #self._createStructure() (which in turn calls self._createSegment() 
            #in this case) If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole 
            #structre, then the following properties must be set in 
            #self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)

            dnaSegment.setProps(props)

            return dnaSegment


        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill_with_contents()
            raise PluginBug("Internal error while trying to create DNA duplex.")
    def getCursorText(self):
        """
        This is used as a callback method in DnaLine mode 
        @see: DnaLineMode.setParams, DnaLineMode_GM.Draw
        """
        # @TODO: Refactor this. Similar code exists in
        # DnaStrand_EditCommand.getCursorText() -- Ninad 2008-04-12
        if self.grabbedHandle is None:
            return

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

        duplexRise = self.struct.getDuplexRise()

        #############

        raw_numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change()

        # Following fixes bugs like 2904 and 2906
        # Note that we are using numberOfBasePairsToAddOrRemove in self._modifyStructure()
        # if self._determine_numberOfBasePairs_to_change() returns the number of basepairs
        # to add, it returns 1 more than the actual number of basepairs. Because
        # while creating the dna, it removes the first base pair of the newly created
        # dna. So, for cursor text and for PM spinbox, we should make adjustments to the
        # raw_numberOfBasePairsToAddOrRemove so that it reflects the correct value
        # in the spinbox and in the PM

        if raw_numberOfBasePairsToAddOrRemove > 1:
            numberOfBasePairsToAddOrRemove = raw_numberOfBasePairsToAddOrRemove - 1

        else:
            numberOfBasePairsToAddOrRemove = raw_numberOfBasePairsToAddOrRemove

        current_numberOfBasePairs = self.struct.getNumberOfBasePairs()

        numberOfBasePairs = current_numberOfBasePairs + numberOfBasePairsToAddOrRemove

        if hasattr(self.propMgr, "numberOfBasePairsSpinBox"):
            # @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.numberOfBasePairsSpinBox.setValue(numberOfBasePairs)

        text = ""
        textColor = env.prefs[cursorTextColor_prefs_key]  # Mark 2008-08-28

        if not env.prefs[dnaSegmentEditCommand_showCursorTextCheckBox_prefs_key]:
            return text, textColor

        # @@TODO: refactor.
        # this duplex length canculation fixes bug 2906
        duplexLength = getDuplexLength("B-DNA", numberOfBasePairs, duplexRise=duplexRise)

        # Cursor text strings --
        duplexLengthString = str(round(duplexLength, 3))

        numberOfBasePairsString = self._getCursorText_numberOfBasePairs(numberOfBasePairs)

        duplexLengthString = self._getCursorText_length(duplexLength)

        changedBasePairsString = self._getCursorText_changedBasePairs(numberOfBasePairs)

        # Add commas (to be refactored)
        commaString = ", "
        text = numberOfBasePairsString

        if text and changedBasePairsString:
            text += " "  # commaString not needed here. Mark 2008-07-03

        text += changedBasePairsString

        if text and duplexLengthString:
            text += commaString

        text += duplexLengthString

        return (text, textColor)
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        number_of_basePairs_from_struct, numberOfBases, dnaForm, dnaModel, basesPerTurn, duplexRise, endPoint1, endPoint2, color_junk = (
            params
        )
        # Note: color_junk is not used. Ideally it should do struct.setColor(color)
        # but the color combobox in the PM directly sets the color of the
        # structure to the specified one when the current index in the combobx
        # changes

        # If user enters the number of basepairs and hits preview i.e. endPoint1
        # and endPoint2 are not entered by the user and thus have default value
        # of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        # endPoint2 using the duplex length.
        # Do not use '==' equality check on vectors! its a bug. Use same_vals
        # or Veq instead.
        if Veq(endPoint1, endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + self.win.glpane.right * getDuplexLength("B-DNA", numberOfBases)

        if numberOfBases < 1:
            msg = redmsg("Cannot preview/insert a DNA duplex with 0 bases.")
            self.propMgr.updateMessage(msg)
            self.dna = None  # Fixes bug 2530. Mark 2007-09-02
            return None

        if dnaForm == "B-DNA":
            if dnaModel == "PAM3":
                dna = B_Dna_PAM3_Generator()
            elif dnaModel == "PAM5":
                dna = B_Dna_PAM5_Generator()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna = dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        dnaSegment = DnaSegment(self.name, self.win.assy, self.win.assy.part.topnode, editCommand=self)
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis

            dna.make(dnaSegment, numberOfBases, basesPerTurn, duplexRise, endPoint1, endPoint2)

            # set some properties such as duplexRise and number of bases per turn
            # This information will be stored on the DnaSegment object so that
            # it can be retrieved while editing this object.
            # This works with or without dna_updater. Now the question is
            # should these props be assigned to the DnaSegment in
            # dnaDuplex.make() itself ? This needs to be answered while modifying
            # make() method to fit in the dna data model. --Ninad 2008-03-05

            # WARNING 2008-03-05: Since self._modifyStructure calls
            # self._createStructure()
            # If in the near future, we actually permit modifying a
            # structure (such as dna) without actually recreating the whole
            # structre, then the following properties must be set in
            # self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)
            dnaSegment.setProps(props)

            return dnaSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill()
            raise PluginBug("Internal error while trying to create DNA duplex.")
    def getCursorText(self):
        """
        This is used as a callback method in DnaLine mode 
        @see: DnaLineMode.setParams, DnaLineMode_GM.Draw
        """
        #@TODO: Refactor this. Similar code exists in
        #DnaStrand_EditCommand.getCursorText() -- Ninad 2008-04-12
        if self.grabbedHandle is None:
            return

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

        duplexRise = self.struct.getDuplexRise()

        #############

        raw_numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change(
        )

        #Following fixes bugs like 2904 and 2906
        #Note that we are using numberOfBasePairsToAddOrRemove in self._modifyStructure()
        #if self._determine_numberOfBasePairs_to_change() returns the number of basepairs
        #to add, it returns 1 more than the actual number of basepairs. Because
        #while creating the dna, it removes the first base pair of the newly created
        #dna. So, for cursor text and for PM spinbox, we should make adjustments to the
        #raw_numberOfBasePairsToAddOrRemove so that it reflects the correct value
        #in the spinbox and in the PM

        if raw_numberOfBasePairsToAddOrRemove > 1:
            numberOfBasePairsToAddOrRemove = raw_numberOfBasePairsToAddOrRemove - 1

        else:
            numberOfBasePairsToAddOrRemove = raw_numberOfBasePairsToAddOrRemove

        ##############

        current_numberOfBasePairs = self.struct.getNumberOfBasePairs()

        numberOfBasePairs = current_numberOfBasePairs + numberOfBasePairsToAddOrRemove
        #@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.numberOfBasePairsSpinBox.setValue(numberOfBasePairs)

        #Note: for Rattlesnake rc2, the text color is green when bases are added
        #, red when subtracted black when no change. But this implementation is
        #changed based on Mark's user experience. The text is now always shown
        #in black color. -- Ninad 2008-04-17
        textColor = black

        text = ""

        if not env.prefs[
                dnaSegmentEditCommand_showCursorTextCheckBox_prefs_key]:
            return '', black

        #@@TODO: refactor.
        #this duplex length canculation fixes bug 2906
        duplexLength = getDuplexLength('B-DNA',
                                       numberOfBasePairs,
                                       duplexRise=duplexRise)

        #Cursor text strings --
        duplexLengthString = str(round(duplexLength, 3))

        numberOfBasePairsString = self._getCursorText_numberOfBasePairs(
            numberOfBasePairs)

        duplexLengthString = self._getCursorText_length(duplexLength)

        changedBasePairsString = self._getCursorText_changedBasePairs(
            numberOfBasePairs)

        #Add commas (to be refactored)
        commaString = ", "
        text = numberOfBasePairsString

        if text and changedBasePairsString:
            text += " "  # commaString not needed here. Mark 2008-07-03

        text += changedBasePairsString

        if text and duplexLengthString:
            text += commaString

        text += duplexLengthString

        return (text, textColor)
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        number_of_basePairs_from_struct,\
                                       numberOfBases, \
                                       dnaForm, \
                                       dnaModel, \
                                       basesPerTurn, \
                                       duplexRise, \
                                       endPoint1, \
                                       endPoint2, \
                                       color_junk = params
        #Note: color_junk is not used. Ideally it should do struct.setColor(color)
        #but the color combobox in the PM directly sets the color of the
        #structure to the specified one when the current index in the combobx
        #changes

        #If user enters the number of basepairs and hits preview i.e. endPoint1
        #and endPoint2 are not entered by the user and thus have default value
        #of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        #endPoint2 using the duplex length.
        #Do not use '==' equality check on vectors! its a bug. Use same_vals
        # or Veq instead.
        if Veq(endPoint1, endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + \
                      self.win.glpane.right*getDuplexLength('B-DNA',
                                                            numberOfBases)

        if numberOfBases < 1:
            msg = redmsg("Cannot preview/insert a DNA duplex with 0 bases.")
            self.propMgr.updateMessage(msg)
            self.dna = None  # Fixes bug 2530. Mark 2007-09-02
            return None

        if dnaForm == 'B-DNA':
            if dnaModel == 'PAM3':
                dna = B_Dna_PAM3()
            elif dnaModel == 'PAM5':
                dna = B_Dna_PAM5()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna = dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        dnaSegment = DnaSegment(self.name,
                                self.win.assy,
                                self.win.assy.part.topnode,
                                editCommand=self)
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis

            dna.make(dnaSegment, numberOfBases, basesPerTurn, duplexRise,
                     endPoint1, endPoint2)

            #set some properties such as duplexRise and number of bases per turn
            #This information will be stored on the DnaSegment object so that
            #it can be retrieved while editing this object.
            #This works with or without dna_updater. Now the question is
            #should these props be assigned to the DnaSegment in
            #dnaDuplex.make() itself ? This needs to be answered while modifying
            #make() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls
            #self._createStructure()
            #If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole
            #structre, then the following properties must be set in
            #self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)
            dnaSegment.setProps(props)

            return dnaSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill()
            raise PluginBug(
                "Internal error while trying to create DNA duplex.")