def _loadSelectedAtomPosGroupBox(self, inPmGroupBox):
        """
        Load the selected Atoms position groupbox It is a sub-gropbox of 
        L{self.selectionOptionsGroupBox)
        @param inPmGroupBox: 'The Selected Atom Position Groupbox'
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.selectedAtomLineEdit = PM_LineEdit(inPmGroupBox,
                                                label="Selected Atom:",
                                                text="",
                                                setAsDefault=False,
                                                spanWidth=False)

        self.selectedAtomLineEdit.setReadOnly(True)
        self.selectedAtomLineEdit.setEnabled(False)

        self.coordinateSpinboxes = PM_CoordinateSpinBoxes(inPmGroupBox)

        # User input to specify x-coordinate
        self.xCoordOfSelectedAtom = self.coordinateSpinboxes.xSpinBox
        # User input to specify y-coordinate
        self.yCoordOfSelectedAtom = self.coordinateSpinboxes.ySpinBox
        # User input to specify z-coordinate
        self.zCoordOfSelectedAtom = self.coordinateSpinboxes.zSpinBox
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """

        self.structure1LineEdit = PM_LineEdit(pmGroupBox, label="First structure:", setAsDefault=False)
        self.structure1LineEdit.setEnabled(False)

        self.structure2LineEdit = PM_LineEdit(pmGroupBox, label="Second structure:", setAsDefault=False)
        self.structure2LineEdit.setEnabled(False)

        self.thresholdDoubleSpinBox = PM_DoubleSpinBox(
            pmGroupBox,
            label="Threshold:",
            value=self.threshold,
            setAsDefault=True,
            minimum=0.0,
            maximum=360.0,
            decimals=1,
            singleStep=30.0,
            suffix=" deg",
            spanWidth=False,
        )

        self.comparePushButton = PM_PushButton(pmGroupBox, text="Compare", setAsDefault=True)

        self.hidePushButton = PM_PushButton(pmGroupBox, text="Hide differences", setAsDefault=True)
        return
 def _loadGroupBox1(self, pmGroupBox):
     """
     Load widgets in group box.
     """
     
     includeStrandsChoices = ["All strands in model",
                              "Selected strands only"]
     
     self.includeStrandsComboBox  = \
         PM_ComboBox( pmGroupBox,
                      label         =  "Include strands:", 
                      choices       =  includeStrandsChoices,
                      setAsDefault  =  True)
     
     self.numberOfBasesLineEdit  = \
         PM_LineEdit( pmGroupBox,
                      label  =  "Total nucleotides:",
                      text   = str(self.getNumberOfBases()))
     self.numberOfBasesLineEdit.setEnabled(False)
     
     self.numberOfXBasesLineEdit  = \
         PM_LineEdit( pmGroupBox,
                      label  =  "Unassigned:",
                      text   = str(self.getNumberOfBases(unassignedOnly = True)))
     self.numberOfXBasesLineEdit.setEnabled(False)
     
     self.viewDnaOrderFileButton = \
         PM_PushButton( pmGroupBox,
                        label     = "",
                        text      = "View DNA Order File...",
                        spanWidth = True)
     return
Beispiel #4
0
    def loadLineEditGroupBox(self, inPmGroupBox):
        """
        Load PM_LineEdit test widgets in group box.
        """

        self.lineEdit1 = \
            PM_LineEdit( inPmGroupBox,
                         label        = "Name:",
                         text         = "RotaryMotor-1",
                         setAsDefault = True,
                         spanWidth    = False)

        self.lineEdit2 = \
            PM_LineEdit( inPmGroupBox,
                         label        = ":Name",
                         labelColumn  = 1,
                         text         = "RotaryMotor-1",
                         setAsDefault = True,
                         spanWidth    = False)

        self.lineEdit3 = \
            PM_LineEdit( inPmGroupBox,
                         label        = "LineEdit (spanWidth = True):",
                         text         = "RotaryMotor-1",
                         setAsDefault = False,
                         spanWidth    = True)
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """

        includeStrandsChoices = [
            "All strands in model", "Selected strands only"
        ]

        self.includeStrandsComboBox  = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Include strands:",
                         choices       =  includeStrandsChoices,
                         setAsDefault  =  True)

        self.numberOfBasesLineEdit  = \
            PM_LineEdit( pmGroupBox,
                         label  =  "Total nucleotides:",
                         text   = str(self.getNumberOfBases()))
        self.numberOfBasesLineEdit.setEnabled(False)

        self.numberOfXBasesLineEdit  = \
            PM_LineEdit( pmGroupBox,
                         label  =  "Unassigned:",
                         text   = str(self.getNumberOfBases(unassignedOnly = True)))
        self.numberOfXBasesLineEdit.setEnabled(False)

        self.viewDnaOrderFileButton = \
            PM_PushButton( pmGroupBox,
                           label     = "",
                           text      = "View DNA Order File...",
                           spanWidth = True)
        return
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 1.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox,
                                        label="Name:",
                                        text="",
                                        setAsDefault=False)

        self.numberOfBasesSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Number of bases:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.disableStructHighlightingCheckbox = \
            PM_CheckBox( pmGroupBox,
                         text         = "Don't highlight while editing DNA",
                         widgetColumn  = 0,
                         state        = Qt.Unchecked,
                         setAsDefault = True,
                         spanWidth = True
                         )

        #As of 2008-03-31, the properties such as number of bases will be
        #editable only by using the resize handles.
        self.numberOfBasesSpinBox.setEnabled(False)
        return
Beispiel #7
0
    def _loadGroupBox2(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.conformationComboBox  = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Conformation:",
                         choices       =  ["B-DNA"],
                         setAsDefault  =  True)

        dnaModelChoices = ['PAM3', 'PAM5']
        self.dnaModelComboBox = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Model:",
                         choices       =  dnaModelChoices,
                         setAsDefault  =  True)


        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  env.prefs[bdnaBasesPerTurn_prefs_key],
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )


        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  env.prefs[bdnaRise_prefs_key],
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Base pairs:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  0,
                        maximum       =  10000 )

        self.numberOfBasePairsSpinBox.setDisabled(True)

        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)
Beispiel #8
0
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox, label="Name:")

        self.lengthLineEdit = PM_LineEdit(pmGroupBox, label="Length:")
        self.lengthLineEdit.setEnabled(False)

        self.currentResidueComboBox = PM_ComboBox(pmGroupBox,
                                                  label="Current residue:",
                                                  setAsDefault=False)

        BUTTON_LIST = [
            ("QToolButton", 1, "Previous residue",
             "ui/actions/Properties Manager/Previous.png", "",
             "Previous residue", 0),
            ("QToolButton", 2, "Next residue",
             "ui/actions/Properties Manager/Next.png", "", "Next residue", 1)
        ]

        self.prevNextButtonRow = \
            PM_ToolButtonRow( pmGroupBox,
                              title        =  "",
                              buttonList   =  BUTTON_LIST,
                              label        =  'Previous / Next:',
                              isAutoRaise  =  True,
                              isCheckable  =  False
                            )
        self.prevButton = self.prevNextButtonRow.getButtonById(1)
        self.nextButton = self.prevNextButtonRow.getButtonById(2)

        self.recenterViewCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Center view on current residue",
                         setAsDefault  =  True,
                         state         =  Qt.Unchecked,
                         widgetColumn  =  0,
                         spanWidth     =  True)

        self.lockEditedCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Lock edited rotamers",
                         setAsDefault  =  True,
                         state         =  Qt.Checked,
                         widgetColumn  =  0,
                         spanWidth     =  True)

        self.showAllResiduesCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Show all residues",
                         setAsDefault  =  False,
                         state         =  Qt.Unchecked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        return
    def _loadSelectionOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Selection Options group box.
        @param inPmGroupBox: The Selection Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.selectionFilterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text  = "Enable atom selection filter",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )
        self.selectionFilterCheckBox.setDefaultValue(False)

        self.filterlistLE = PM_LineEdit(inPmGroupBox,
                                        label="",
                                        text="",
                                        setAsDefault=False,
                                        spanWidth=True)
        self.filterlistLE.setReadOnly(True)

        if self.selectionFilterCheckBox.isChecked():
            self.filterlistLE.setEnabled(True)
        else:
            self.filterlistLE.setEnabled(False)

        self.showSelectedAtomInfoCheckBox = \
            PM_CheckBox(
                inPmGroupBox,
                text  = "Show Selected Atom Info",
                widgetColumn = 0,
                state        = Qt.Unchecked)

        self.selectedAtomPosGroupBox = \
            PM_GroupBox( inPmGroupBox, title = "")
        self._loadSelectedAtomPosGroupBox(self.selectedAtomPosGroupBox)

        self.toggle_selectedAtomPosGroupBox(show=0)
        self.enable_or_disable_selectedAtomPosGroupBox(bool_enable=False)

        self.reshapeSelectionCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Dragging reshapes selection',
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )

        connect_checkbox_with_boolean_pref(self.reshapeSelectionCheckBox,
                                           reshapeAtomsSelection_prefs_key)

        env.prefs[reshapeAtomsSelection_prefs_key] = False

        self.waterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = "Z depth filter (water surface)",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )
    def _loadLayerPropertiesGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Layer Properties group box.
        @param inPmGroupBox: The Layer Properties groupbox in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.currentLayerComboBox = \
            PM_ComboBox( inPmGroupBox,
                         index     = 0,
                         spanWidth = True
                        )

        self.addLayerButton = PM_PushButton(inPmGroupBox)
        self.addLayerButton.setIcon(
            geticon('ui/actions/Properties Manager/addlayer.png'))
        self.addLayerButton.setFixedSize(QSize(26, 26))
        self.addLayerButton.setIconSize(QSize(22, 22))

        # A widget list to create a widget row.
        # Format:
        # - Widget type,
        # - widget object,
        # - column

        firstRowWidgetList = [('PM_ComboBox', self.currentLayerComboBox, 1),
                              ('PM_PushButton', self.addLayerButton, 2)]

        widgetRow = PM_WidgetRow(
            inPmGroupBox,
            title='',
            widgetList=firstRowWidgetList,
            label="Layer:",
            labelColumn=0,
        )

        self.layerCellsSpinBox = \
             PM_SpinBox( inPmGroupBox,
                        label         =  "Lattice cells:",
                        labelColumn   =  0,
                        value         =  2,
                        minimum       =  1,
                        maximum       =  25
                      )

        self.layerThicknessLineEdit = PM_LineEdit(inPmGroupBox,
                                                  label="Thickness:",
                                                  text="",
                                                  setAsDefault=False,
                                                  spanWidth=False)

        #self.layerThicknessLineEdit.setReadOnly(True)
        self.layerThicknessLineEdit.setDisabled(True)
        tooltip = "Thickness of layer in Angstroms"
        self.layerThicknessLineEdit.setToolTip(tooltip)
Beispiel #11
0
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox,
                                        label="Strand name:",
                                        text="",
                                        setAsDefault=False)

        self.numberOfBasesSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Number of bases:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  self.basesPerTurn,
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )

        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  self.duplexRise,
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )

        self.disableStructHighlightingCheckbox = \
            PM_CheckBox( pmGroupBox,
                         text         = "Don't highlight while editing DNA",
                         widgetColumn  = 0,
                         state        = Qt.Unchecked,
                         setAsDefault = True,
                         spanWidth = True
                         )

        #As of 2008-03-31, the properties such as number of bases will be
        #editable only by using the resize handles. post FNANO we will support
        #the
        self.numberOfBasesSpinBox.setEnabled(False)
        self.basesPerTurnDoubleSpinBox.setEnabled(False)
        self.duplexRiseDoubleSpinBox.setEnabled(False)
Beispiel #12
0
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox,
                                        label="Segment name:",
                                        text="",
                                        setAsDefault=False)

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Base pairs:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  self.basesPerTurn,
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )
        self.basesPerTurnDoubleSpinBox.setDisabled(True)

        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  self.duplexRise,
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )
        self.duplexRiseDoubleSpinBox.setDisabled(True)

        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 1.
        """

        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                         label         =  "Name:",
                         text          =  "",
                         setAsDefault  =  False)

        self.numberOfBasesSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Number of bases:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.disableStructHighlightingCheckbox = \
            PM_CheckBox( pmGroupBox,
                         text         = "Don't highlight while editing DNA",
                         widgetColumn  = 0,
                         state        = Qt.Unchecked,
                         setAsDefault = True,
                         spanWidth = True
                         )

        #As of 2008-03-31, the properties such as number of bases will be
        #editable only by using the resize handles.
        self.numberOfBasesSpinBox.setEnabled(False)
        return
    def _loadSelectedAtomPosGroupBox(self, inPmGroupBox):
        """
        Load the selected Atoms position groupbox It is a sub-gropbox of
        L{self.selectionOptionsGroupBox)
        @param inPmGroupBox: 'The Selected Atom Position Groupbox'
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.selectedAtomLineEdit = PM_LineEdit( inPmGroupBox,
                                         label        = "Selected Atom:",
                                         text         = "",
                                         setAsDefault = False,
                                         spanWidth    = False )

        self.selectedAtomLineEdit.setReadOnly(True)
        self.selectedAtomLineEdit.setEnabled(False)

        self.coordinateSpinboxes = PM_CoordinateSpinBoxes(inPmGroupBox)

        # User input to specify x-coordinate
        self.xCoordOfSelectedAtom  =  self.coordinateSpinboxes.xSpinBox
        # User input to specify y-coordinate
        self.yCoordOfSelectedAtom  =  self.coordinateSpinboxes.ySpinBox
        # User input to specify z-coordinate
        self.zCoordOfSelectedAtom  =  self.coordinateSpinboxes.zSpinBox
    def _loadSelectionOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Selection Options group box.
        @param inPmGroupBox: The Selection Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.selectionFilterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text  = "Enable atom selection filter",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )
        self.selectionFilterCheckBox.setDefaultValue(False)

        self.filterlistLE = PM_LineEdit( inPmGroupBox,
                                         label        = "",
                                         text         = "",
                                         setAsDefault = False,
                                         spanWidth    = True )
        self.filterlistLE.setReadOnly(True)

        if self.selectionFilterCheckBox.isChecked():
            self.filterlistLE.setEnabled(True)
        else:
            self.filterlistLE.setEnabled(False)

        self.showSelectedAtomInfoCheckBox = \
            PM_CheckBox(
                inPmGroupBox,
                text  = "Show Selected Atom Info",
                widgetColumn = 0,
                state        = Qt.Unchecked)

        self.selectedAtomPosGroupBox = \
            PM_GroupBox( inPmGroupBox, title = "")
        self._loadSelectedAtomPosGroupBox(self.selectedAtomPosGroupBox)

        self.toggle_selectedAtomPosGroupBox(show = 0)
        self.enable_or_disable_selectedAtomPosGroupBox( bool_enable = False)

        self.reshapeSelectionCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Dragging reshapes selection',
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )

        connect_checkbox_with_boolean_pref( self.reshapeSelectionCheckBox,
                                            reshapeAtomsSelection_prefs_key )

        env.prefs[reshapeAtomsSelection_prefs_key] = False

        self.waterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = "Z depth filter (water surface)",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )
Beispiel #16
0
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox,
                                        label="Name:",
                                        text="",
                                        setAsDefault=False)

        # Nanotube Length
        self.ntLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.ntLengthLineEdit.setDisabled(True)

        # Nanotube Radius
        self.ntDiameterLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Nanotube Diameter: ",
                         setAsDefault  =  False)

        self.ntDiameterLineEdit.setDisabled(True)

        # Nanotube chirality. These are disabled (read-only) for now. --Mark
        self.chiralityNSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label        = "Chirality (n) :",
                        minimum      =  2,
                        maximum      =  100,
                        setAsDefault = True )
        self.chiralityNSpinBox.setDisabled(True)

        self.chiralityMSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label        = "Chirality (m) :",
                        minimum      =  0,
                        maximum      =  100,
                        setAsDefault = True )
        self.chiralityMSpinBox.setDisabled(True)
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                         label         =  "Name:",
                         text          =  "",
                         setAsDefault  =  False)

        # Nanotube Length
        self.ntLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.ntLengthLineEdit.setDisabled(True)

        # Nanotube Radius
        self.ntDiameterLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Nanotube Diameter: ",
                         setAsDefault  =  False)

        self.ntDiameterLineEdit.setDisabled(True)

        # Nanotube chirality. These are disabled (read-only) for now. --Mark
        self.chiralityNSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label        = "Chirality (n) :",
                        minimum      =  2,
                        maximum      =  100,
                        setAsDefault = True )
        self.chiralityNSpinBox.setDisabled(True)

        self.chiralityMSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label        = "Chirality (m) :",
                        minimum      =  0,
                        maximum      =  100,
                        setAsDefault = True )
        self.chiralityMSpinBox.setDisabled(True)
    def _loadLayerPropertiesGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Layer Properties group box.
        @param inPmGroupBox: The Layer Properties groupbox in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.currentLayerComboBox = \
            PM_ComboBox( inPmGroupBox,
                         index     = 0,
                         spanWidth = True
                        )

        self.addLayerButton = PM_PushButton(inPmGroupBox)
        self.addLayerButton.setIcon(
            geticon('ui/actions/Properties Manager/addlayer.png'))
        self.addLayerButton.setFixedSize(QSize(26, 26))
        self.addLayerButton.setIconSize(QSize(22, 22))

        # A widget list to create a widget row.
        # Format:
        # - Widget type,
        # - widget object,
        # - column

        firstRowWidgetList = [('PM_ComboBox', self.currentLayerComboBox, 1),
                              ('PM_PushButton', self.addLayerButton, 2)
                              ]

        widgetRow = PM_WidgetRow(inPmGroupBox,
                                 title     = '',
                                 widgetList = firstRowWidgetList,
                                 label = "Layer:",
                                 labelColumn  = 0,
                                 )

        self.layerCellsSpinBox = \
             PM_SpinBox( inPmGroupBox,
                        label         =  "Lattice cells:",
                        labelColumn   =  0,
                        value         =  2,
                        minimum       =  1,
                        maximum       =  25
                      )

        self.layerThicknessLineEdit = PM_LineEdit(inPmGroupBox,
                                         label        = "Thickness:",
                                         text         = "",
                                         setAsDefault = False,
                                         spanWidth    = False )

        #self.layerThicknessLineEdit.setReadOnly(True)
        self.layerThicknessLineEdit.setDisabled(True)
        tooltip = "Thickness of layer in Angstroms"
        self.layerThicknessLineEdit.setToolTip(tooltip)
 def _loadGroupBox1(self, pmGroupBox):
     """
     Load widgets in group box 4.
     """
     
     self.nameLineEdit = PM_LineEdit( pmGroupBox,
                      label         =  "Strand name:",
                      text          =  "",
                      setAsDefault  =  False)
     
     self.numberOfBasesSpinBox = \
         PM_SpinBox( pmGroupBox, 
                     label         =  "Number of bases:", 
                     value         =  self._numberOfBases,
                     setAsDefault  =  False,
                     minimum       =  2,
                     maximum       =  10000 )
     
     self.basesPerTurnDoubleSpinBox  =  \
         PM_DoubleSpinBox( pmGroupBox,
                           label         =  "Bases per turn:",
                           value         =  self.basesPerTurn,
                           setAsDefault  =  True,
                           minimum       =  8.0,
                           maximum       =  20.0,
                           decimals      =  2,
                           singleStep    =  0.1 )
     
     self.duplexRiseDoubleSpinBox  =  \
         PM_DoubleSpinBox( pmGroupBox,
                           label         =  "Rise:",
                           value         =  self.duplexRise,
                           setAsDefault  =  True,
                           minimum       =  2.0,
                           maximum       =  4.0,
                           decimals      =  3,
                           singleStep    =  0.01 )
     
     self.disableStructHighlightingCheckbox = \
         PM_CheckBox( pmGroupBox,
                      text         = "Don't highlight while editing DNA",
                      widgetColumn  = 0,
                      state        = Qt.Unchecked,
                      setAsDefault = True,
                      spanWidth = True
                      )
     
     #As of 2008-03-31, the properties such as number of bases will be 
     #editable only by using the resize handles. post FNANO we will support 
     #the 
     self.numberOfBasesSpinBox.setEnabled(False)
     self.basesPerTurnDoubleSpinBox.setEnabled(False)
     self.duplexRiseDoubleSpinBox.setEnabled(False)
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """

        self.structure1LineEdit = \
            PM_LineEdit( pmGroupBox,
                         label         =  "First structure:",
                         setAsDefault  =  False)
        self.structure1LineEdit.setEnabled(False)

        self.structure2LineEdit = \
            PM_LineEdit( pmGroupBox,
                         label         =  "Second structure:",
                         setAsDefault  =  False)
        self.structure2LineEdit.setEnabled(False)

        self.thresholdDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Threshold:",
                              value         =  self.threshold,
                              setAsDefault  =  True,
                              minimum       =  0.0,
                              maximum       =  360.0,
                              decimals      =  1,
                              singleStep    =  30.0,
                              suffix        = " deg",
                              spanWidth = False)

        self.comparePushButton  = \
            PM_PushButton( pmGroupBox,
                         text         =  "Compare",
                         setAsDefault  =  True)

        self.hidePushButton  = \
            PM_PushButton( pmGroupBox,
                         text         =  "Hide differences",
                         setAsDefault  =  True)
        return
Beispiel #21
0
 def _loadGroupBox2(self, groupbox):
     """
     Load widgets into group box 2.
     """
     self.someLineEdit  =  \
         PM_LineEdit( groupbox,
                      label        = "Text:",
                      text         = "initial text (pm)",
                      setAsDefault = True,
                      spanWidth    = False )
     ### TODO: self.someLineEdit.connectWithState( ... some text state ...)
     # and then connect the TextState text to the same state
     # (or just use that state? no, it needs an address outside that complicated expr)
     return
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                                         label         =  "Segment name:",
                                         text          =  "",
                                         setAsDefault  =  False)

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Base pairs:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  self.basesPerTurn,
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )
        self.basesPerTurnDoubleSpinBox.setDisabled(True)

        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  self.duplexRise,
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )
        self.duplexRiseDoubleSpinBox.setDisabled(True)

        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox,
                                        label="Protein chunk name:",
                                        text="",
                                        setAsDefault=False)

        #Urmi 20080713: May be useful to set the minimum value to not zero
        self.numberOfAASpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Number of amino acids:",
                        value         =  0,
                        setAsDefault  =  False,
                        minimum       =  0,
                        maximum       =  10000 )
        self.numberOfAASpinBox.setEnabled(False)
    def _loadFreeDragRotateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Free Drag Rotate group box, which is 
        present within the Rotate groupbox.
        @param inPmGroupBox: The Free Drag Rotate group box in the Rotate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column

        BUTTON_LIST = [
            ( "QToolButton", 1,  "ROTATEDEFAULT",
              "ui/actions/Properties Manager/Rotate_Free.png", "", "F", 0 ),

            ( "QToolButton", 2,  "ROTATEX",
              "ui/actions/Properties Manager/RotateX.png", "", "X", 1 ),

            ( "QToolButton", 3,  "ROTATEY",
              "ui/actions/Properties Manager/RotateY.png", "", "Y", 2 ),

            ( "QToolButton", 4,  "ROTATEZ",
              "ui/actions/Properties Manager/RotateZ.png", "", "Z", 3 ),

            ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",
              "ui/actions/Properties Manager/translate+rotate-A.png", "", \
              "A", 4 )

            ]

        self.freeDragRotateButtonGroup = \
            PM_ToolButtonRow( inPmGroupBox,
                               title        = "",
                               buttonList   = BUTTON_LIST,
                               spanWidth = True,
                               checkedId    = 1,
                               setAsDefault = True,
                            )

        self.rotateFreeButton = self.freeDragRotateButtonGroup.getButtonById(1)
        self.rotateXButton = self.freeDragRotateButtonGroup.getButtonById(2)
        self.rotateYButton = self.freeDragRotateButtonGroup.getButtonById(3)
        self.rotateZButton = self.freeDragRotateButtonGroup.getButtonById(4)
        self.rotAlongAxisButton = \
            self.freeDragRotateButtonGroup.getButtonById(5)

        inPmGroupBox.setStyleSheet(
            self.freeDragRotateButtonGroup._getStyleSheet())

        X_ROW_LABELS = [("QLabel", "Delta Theta X:", 0), ("QLabel", "", 1),
                        ("QLabel", "0.00", 2), ("QLabel", "Degrees", 3)]

        Y_ROW_LABELS = [("QLabel", "Delta Theta Y:", 0), ("QLabel", "", 1),
                        ("QLabel", "0.00", 2), ("QLabel", "Degrees", 3)]

        Z_ROW_LABELS = [("QLabel", "Delta Theta Z:", 0), ("QLabel", "", 1),
                        ("QLabel", "0.00", 2), ("QLabel", "Degrees", 3)]

        self.rotateXLabelRow = PM_LabelRow(inPmGroupBox,
                                           title="",
                                           labelList=X_ROW_LABELS)
        self.deltaThetaX_lbl = self.rotateXLabelRow.labels[2]

        self.rotateYLabelRow = PM_LabelRow(inPmGroupBox,
                                           title="",
                                           labelList=Y_ROW_LABELS)
        self.deltaThetaY_lbl = self.rotateYLabelRow.labels[2]

        self.rotateZLabelRow = PM_LabelRow(inPmGroupBox,
                                           title="",
                                           labelList=Z_ROW_LABELS)
        self.deltaThetaZ_lbl = self.rotateZLabelRow.labels[2]

        self.rotateAboutPointButton = PM_ToolButton(
                    inPmGroupBox,
                    text = "Rotate selection about a point",
                    iconPath  = "ui/actions/Properties Manager"\
                    "/Rotate_Components.png",
                    spanWidth = True
                    )
        self.rotateAboutPointButton.setCheckable(True)
        self.rotateAboutPointButton.setAutoRaise(True)
        self.rotateAboutPointButton.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)


        self.rotateStartCoordLineEdit = PM_LineEdit(
            inPmGroupBox,
            label        = "ui/actions/Properties Manager"\
                    "/Move_Start_Point.png",
            text         = "Define 3 points",
            setAsDefault = False,
            )
        self.rotateStartCoordLineEdit.setReadOnly(True)
        self.rotateStartCoordLineEdit.setEnabled(False)
Beispiel #25
0
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """
        
        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                                                label = "Name:")
        
        self.lengthLineEdit = PM_LineEdit( pmGroupBox,
                                           label = "Length:")
        self.lengthLineEdit.setEnabled(False)
        
        self.currentResidueComboBox = PM_ComboBox( pmGroupBox,
                                 label         =  "Current residue:",
                                 setAsDefault  =  False)
        
        BUTTON_LIST = [
            ("QToolButton", 1, "Previous residue", 
             "ui/actions/Properties Manager/Previous.png", 
             "", "Previous residue", 0 ),

            ( "QToolButton", 2, "Next residue",  
              "ui/actions/Properties Manager/Next.png", 
              "", "Next residue", 1 )
            ]
        
        self.prevNextButtonRow = \
            PM_ToolButtonRow( pmGroupBox, 
                              title        =  "",
                              buttonList   =  BUTTON_LIST,
                              label        =  'Previous / Next:',
                              isAutoRaise  =  True,
                              isCheckable  =  False
                            )
        self.prevButton = self.prevNextButtonRow.getButtonById(1)
        self.nextButton = self.prevNextButtonRow.getButtonById(2)
        
        self.recenterViewCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Center view on current residue",
                         setAsDefault  =  True,
                         state         =  Qt.Unchecked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        
        self.lockEditedCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Lock edited rotamers",
                         setAsDefault  =  True,
                         state         =  Qt.Checked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        
        self.showAllResiduesCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Show all residues",
                         setAsDefault  =  False,
                         state         =  Qt.Unchecked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        return
class Ui_DnaSequenceEditor(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in while editing
    a DnaStrand.
    
    It is a DockWidget that is docked at the bottom of the MainWindow.
    """
    _title = "Sequence Editor"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """

        self.win = win
        # Should parentWidget for a docwidget always be win?
        #Not necessary but most likely it will be the case.
        parentWidget = win

        _superclass.__init__(self, parentWidget, title=self._title)

        #A flag used to restore the state of the Reports dock widget
        #(which can be accessed through View  >  Reports) see self.show() and
        #self.closeEvent() for more details.
        self._reportsDockWidget_closed_in_show_method = False
        self.setFixedHeight(90)
        return

    def show(self):
        """
        Shows the sequence editor. While doing this, it also closes the reports
        dock widget (if visible) the state of the reports dockwidget will be
        restored when the sequence editor is closed. 
        @see:self.closeEvent()
        """
        self._reportsDockWidget_closed_in_show_method = False
        #hide the history widget first
        #(It will be shown back during self.close)
        #The history widget is hidden or shown only when both
        # 'View > Full Screen' and View > Semi Full Screen actions
        # are *unchecked*
        #Thus show or close methods won't do anything to history widget
        # if either of the above mentioned actions is checked.
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self.win.reportsDockWidget.isVisible():
                self.win.reportsDockWidget.close()
                self._reportsDockWidget_closed_in_show_method = True

        _superclass.show(self)
        return

    def closeEvent(self, event):
        """
        Overrides close event. Makes sure that the visible state of the reports
        widgetis restored when the sequence editor is closed. 
        @see: self.show()
        """
        _superclass.closeEvent(self, event)

        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self._reportsDockWidget_closed_in_show_method:
                self.win.viewReportsAction.setChecked(True)
                self._reportsDockWidget_closed_in_show_method = False
        return

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTextEditWidget()
        return

    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Open.png")

        self.saveSequenceButton = PM_ToolButton(
            self,
            iconPath="ui/actions/Properties Manager/Save_Strand_Sequence.png")

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        # Only supporting 5' to 3' direction until bug 2956 is fixed.
        # Mark 2008-12-19
        editDirectionChoices = ["5' to 3'"]  # , "3' to 5'"]
        self.baseDirectionChoiceComboBox = \
            PM_ComboBox( self,
                         choices = editDirectionChoices,
                         index     = 0,
                         spanWidth = False )

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text="Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done --

        # When the widget type starts with the word 'PM_' , the
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned
        # labels (and if they were recreated as mentioned above),
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2]
        #Cleanup in PM_widgetGrid could be to check if the widget starts with
        #'Q'  instead of 'PM_'

        #Widgets to include in the widget row.
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Sequence direction:", 2),
                      ('PM_ComboBox', self.baseDirectionChoiceComboBox, 3),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8),
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10),
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14)]

        widgetRow = PM_WidgetRow(self,
                                 title='',
                                 widgetList=widgetList,
                                 label="",
                                 spanWidth=True)
        return

    def _loadTextEditWidget(self):
        """
        Load the SequenceTexteditWidgets.         
        """
        self.sequenceTextEdit = \
            PM_TextEdit( self,
                         label = " Sequence: ",
                         spanWidth = False,
                         permit_enter_keystroke = False)
        self.sequenceTextEdit.setCursorWidth(2)
        self.sequenceTextEdit.setWordWrapMode(QTextOption.WrapAnywhere)
        self.sequenceTextEdit.setFixedHeight(20)

        #The StrandSequence 'Mate' it is a read only etxtedit that shows
        #the complementary strand sequence.
        self.sequenceTextEdit_mate = \
            PM_TextEdit(self,
                        label = "",
                        spanWidth = False,
                        permit_enter_keystroke = False
                        )
        palette = getPalette(None, QPalette.Base,
                             sequenceEditStrandMateBaseColor)
        self.sequenceTextEdit_mate.setPalette(palette)
        self.sequenceTextEdit_mate.setFixedHeight(20)
        self.sequenceTextEdit_mate.setReadOnly(True)
        self.sequenceTextEdit_mate.setWordWrapMode(QTextOption.WrapAnywhere)

        #Important to make sure that the horizontal and vertical scrollbars
        #for these text edits are never displayed.
        for textEdit in (self.sequenceTextEdit, self.sequenceTextEdit_mate):
            textEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            textEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        return

    def _getFindLineEditStyleSheet(self):
        """
        Return the style sheet for the findLineEdit. This sets the following 
        properties only:
         - background-color

        This style is set whenever the searchStrig can't be found (sets
        a light red color background to the lineedit when this happens)   

        @return: The line edit style sheet.
        @rtype:  str

        """
        styleSheet = "QLineEdit {"\
                   "background-color: rgb(255, 102, 102)"\
                   "}"
        #Not used:
        #  background-color: rgb(217, 255, 216)\

        return styleSheet

    def _setFindOptionsToolButtonMenu(self):
        """
        Sets the menu for the findOptionstoolbutton that appears a small 
        menu button next to the findLineEdit.
        """
        self.findOptionsMenu = QMenu(self.findOptionsToolButton)

        self.caseSensitiveFindAction = QAction(self.findOptionsToolButton)
        self.caseSensitiveFindAction.setText('Match Case')
        self.caseSensitiveFindAction.setCheckable(True)
        self.caseSensitiveFindAction.setChecked(False)

        self.findOptionsMenu.addAction(self.caseSensitiveFindAction)
        self.findOptionsMenu.addSeparator()

        self.findOptionsToolButton.setMenu(self.findOptionsMenu)
        return

    def _addToolTipText(self):
        """
            What's Tool Tip text for widgets in this Property Manager.  
            """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_DnaSequenceEditor
        ToolTip_DnaSequenceEditor(self)
        return

    def _addWhatsThisText(self):
        """
            What's This text for widgets in this Property Manager.  

            """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_DnaSequenceEditor
        whatsThis_DnaSequenceEditor(self)
        return
Beispiel #27
0
class EditProtein_PropertyManager(Command_PropertyManager):
    """
    The ProteinDisplayStyle_PropertyManager class provides a Property Manager 
    for the B{Edit Protein} command on the Build Protein command toolbar. 
    
    The selected peptide/protein is displayed in the protein reduced display
    style. The user can select individual rotamers and edit their chi angles.
    This is useful for certain types of protein design protocols using a
    3rd party program like Rosetta.

    @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         =  "Protein Properties"
    pmName        =  title
    iconPath      =  "ui/actions/Command Toolbar/BuildProtein/EditProtein.png"
    
    current_protein  = None # The currently selected peptide.
    previous_protein = None # The last peptide selected.
    current_aa       = None # The current amino acid.

    
    def __init__( self, command ):
        """
        Constructor for the property manager.
        """
        self.currentWorkingDirectory = env.prefs[workingDirectory_prefs_key]
        
        _superclass.__init__(self, command)
        
        self.sequenceEditor = self.win.createProteinSequenceEditorIfNeeded()
        
        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_WHATS_THIS_BUTTON)
        return
        
    def connect_or_disconnect_signals(self, isConnect = True):
        
        if isConnect:
            change_connect = self.win.connect
        else:
            change_connect = self.win.disconnect 
        
        change_connect(self.nameLineEdit,
                       SIGNAL("editingFinished()"),
                       self._nameChanged)
        
        change_connect(self.currentResidueComboBox,
                       SIGNAL("activated(int)"),
                       self.setCurrentAminoAcid)
        
        change_connect(self.prevButton, 
                       SIGNAL("clicked()"), 
                       self._expandPreviousRotamer)
        
        change_connect(self.nextButton, 
                       SIGNAL("clicked()"), 
                       self._expandNextRotamer)
        
        change_connect(self.recenterViewCheckBox,
                       SIGNAL("toggled(bool)"),
                       self._centerViewToggled)
        
        change_connect(self.showAllResiduesCheckBox,
                       SIGNAL("toggled(bool)"),
                       self._showAllResidues)
        
        # Rotamer control widgets.
        
        change_connect(self.chi1Dial,
                       SIGNAL("valueChanged(int)"),
                       self._rotateChi1)
        
                
        change_connect(self.chi2Dial,
                       SIGNAL("valueChanged(int)"),
                       self._rotateChi2)
        
        change_connect(self.chi3Dial,
                       SIGNAL("valueChanged(int)"),
                       self._rotateChi3)
        
        # Chi4 dial is hidden.
        change_connect(self.chi4Dial,
                       SIGNAL("valueChanged(double)"),
                       self._rotateChi4)
        return
    
    #==
    
    def _nameChanged(self):
        """
        Slot for "Name" field.
        
        @TODO: Include a validator for the name field.
        """
        if not self.current_protein:
            return
        
        _name = str(self.nameLineEdit.text())
        
        if not _name: # Minimal test. Need to implement a validator.
            if self.current_protein:
                self.nameLineEdit.setText(self.current_protein.name)
            return
        
        self.current_protein.name = _name
        msg = "Editing structure <b>%s</b>." % _name
        self.updateMessage(msg)
        
        return
    
    def update_name_field(self):
        """
        Update the name field showing the name of the currently selected protein.
        clear the combobox list.
        """
        if not self.current_protein:
            self.nameLineEdit.setText("")
        else:
            self.nameLineEdit.setText(self.current_protein.name)
        return
    
    def update_length_field(self):
        """
        Update the name field showing the name of the currently selected protein.
        clear the combobox list.
        """
        if not self.current_protein:
            self.lengthLineEdit.setText("")
        else:
            length_str = "%d residues" % self.current_protein.protein.count_amino_acids()
            self.lengthLineEdit.setText(length_str)
        return
    
    def update_residue_combobox(self):
        """
        Update the residue combobox with the amino acid sequence of the
        currently selected protein. If there is no currently selected protein,
        clear the combobox list.
        """
        
        self.currentResidueComboBox.clear()
        
        if not self.current_protein:
            return
        
        aa_list = self.current_protein.protein.get_amino_acid_id_list()
        for j in range(len(aa_list)):
            aa_id, residue_id = aa_list[j].strip().split(":")
            self.currentResidueComboBox.addItem(residue_id)
            pass
        
        self.setCurrentAminoAcid()
        return
    
    def close(self):
        """
        Closes the Property Manager. Overrides EditCommand_PM.close()
        """
        self.sequenceEditor.hide()
        env.history.statusbar_msg("")
        if self.current_protein:
            self.current_protein.setDisplayStyle(self.previous_protein_display_style)
            self.previous_protein = None
            
            # Update name in case the it was changed by the user.
            self.current_protein.name = str(self.nameLineEdit.text())
            
        _superclass.close(self)
        return
    
    def show(self):
        """
        Show the PM. Extends superclass method.
        @note: _update_UI_do_updates() gets called immediately after this and
               updates PM widgets with their correct values/settings.
        """
        _superclass.show(self)
        env.history.statusbar_msg("")
        return
    
    def _addGroupBoxes( self ):
        """
        Add the Property Manager group boxes.
        """
        self._pmGroupBox1 = PM_GroupBox( self,
                                         title = "Parameters")
        self._loadGroupBox1( self._pmGroupBox1 )

        self._pmGroupBox2 = PM_GroupBox( self,
                                         title = "Rotamer Controls")
        self._loadGroupBox2( self._pmGroupBox2 )
        return


    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """
        
        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                                                label = "Name:")
        
        self.lengthLineEdit = PM_LineEdit( pmGroupBox,
                                           label = "Length:")
        self.lengthLineEdit.setEnabled(False)
        
        self.currentResidueComboBox = PM_ComboBox( pmGroupBox,
                                 label         =  "Current residue:",
                                 setAsDefault  =  False)
        
        BUTTON_LIST = [
            ("QToolButton", 1, "Previous residue", 
             "ui/actions/Properties Manager/Previous.png", 
             "", "Previous residue", 0 ),

            ( "QToolButton", 2, "Next residue",  
              "ui/actions/Properties Manager/Next.png", 
              "", "Next residue", 1 )
            ]
        
        self.prevNextButtonRow = \
            PM_ToolButtonRow( pmGroupBox, 
                              title        =  "",
                              buttonList   =  BUTTON_LIST,
                              label        =  'Previous / Next:',
                              isAutoRaise  =  True,
                              isCheckable  =  False
                            )
        self.prevButton = self.prevNextButtonRow.getButtonById(1)
        self.nextButton = self.prevNextButtonRow.getButtonById(2)
        
        self.recenterViewCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Center view on current residue",
                         setAsDefault  =  True,
                         state         =  Qt.Unchecked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        
        self.lockEditedCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Lock edited rotamers",
                         setAsDefault  =  True,
                         state         =  Qt.Checked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        
        self.showAllResiduesCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Show all residues",
                         setAsDefault  =  False,
                         state         =  Qt.Unchecked,
                         widgetColumn  =  0,
                         spanWidth     =  True)
        return
        
    def _loadGroupBox2(self, pmGroupBox):
        """
        Load widgets in group box.
        """
        
        self.discreteStepsCheckBox  = \
            PM_CheckBox( pmGroupBox,
                         text          =  "Use discrete steps",
                         setAsDefault  =  True,
                         state         = Qt.Unchecked)
        
        self.chi1Dial  =  \
            PM_Dial( pmGroupBox,
                              label         =  "Chi1:",
                              value         =  0.000,
                              setAsDefault  =  True,
                              minimum       =  -180.0,
                              maximum       =  180.0,
                              wrapping      =  True,
                              suffix        =  "deg",
                              spanWidth = False)
        
        self.chi1Dial.setEnabled(False)
        
        self.chi2Dial  =  \
            PM_Dial( pmGroupBox,
                              label         =  "Chi2:",
                              value         =  0.000,
                              setAsDefault  =  True,
                              minimum       =  -180.0,
                              maximum       =  180.0,
                              suffix        =  "deg",
                              spanWidth = False)
        
        self.chi2Dial.setEnabled(False)
        
        self.chi3Dial  =  \
            PM_Dial( pmGroupBox,
                              label         =  "Chi3:",
                              value         =  0.000,
                              setAsDefault  =  True,
                              minimum       =  -180.0,
                              maximum       =  180.0,
                              suffix        =  "deg",
                              spanWidth = False)
        
        self.chi3Dial.setEnabled(False)
        
        self.chi4Dial  =  \
            PM_Dial( pmGroupBox,
                              label         =  "Chi4:",
                              value         =  0.000,
                              setAsDefault  =  True,
                              minimum       =  -180.0,
                              maximum       =  180.0,
                              suffix        =  "deg",
                              spanWidth = False)
        
        self.chi4Dial.setEnabled(False)
        
        self.chi4Dial.hide()
        
        return
        
    def _addWhatsThisText( self ):
        
        pass
    
    def _addToolTipText(self):
        
        pass

    def _expandNextRotamer(self):
        """
        Displays the next rotamer in the chain.
        
        @attention: this only works when the GDS is a reduced display style.
        """
        if not self.current_protein:
            return
        
        self.current_protein.protein.traverse_forward()
        self.setCurrentAminoAcid()
        return
    
    def _expandPreviousRotamer(self):
        """
        Displays the previous rotamer in the chain.
        
        @attention: this only works when the GDS is a reduced display style.
        """
        if not self.current_protein:
            return
        
        self.current_protein.protein.traverse_backward()
        self.setCurrentAminoAcid()
        return
    
    def _centerViewToggled(self, checked):
        """
        Slot for "Center view on current residue" checkbox.
        """
        if checked:
            self.display_and_recenter()
        return
    
    def _showAllResidues(self, show):
        """
        Slot for "Show all residues" checkbox.
        """
        if not self.current_protein:
            return
        
        print "Show =",show
        if show:
            self._expandAllRotamers()
        else:
            self._collapseAllRotamers()
        return
    
    def _collapseAllRotamers(self):
        """
        Hides all the rotamers (except the current rotamer).
        """
        self.display_and_recenter()
        return
    
    def _expandAllRotamers(self):
        """
        Displays all the rotamers.
        """
        if not self.current_protein:
            return
        
        self.current_protein.protein.expand_all_rotamers()
        self.win.glpane.gl_update()
        return
        
    def display_and_recenter(self):
        """
        Recenter the view on the current amino acid selected in the 
        residue combobox (or the sequence editor). All rotamers 
        except the current rotamer are collapsed (hidden).
        """
        if not self.current_protein:
            return
        
        # Uncheck the "Show all residues" checkbox since they are being collapsed.
        # Disconnect signals so that showAllResiduesCheckBox won't general a signal.
        self.connect_or_disconnect_signals(isConnect = False)
        self.showAllResiduesCheckBox.setChecked(False)
        self.connect_or_disconnect_signals(isConnect = True)
        
        self.current_protein.protein.collapse_all_rotamers()
        
        # Display the current amino acid and center it in the view if the
        # "Center view on current residue" is checked.
        if self.current_aa:
            self.current_protein.protein.expand_rotamer(self.current_aa)
            self._update_chi_angles(self.current_aa)
            if self.recenterViewCheckBox.isChecked():
                ca_atom = self.current_aa.get_c_alpha_atom()
                if ca_atom:
                    self.win.glpane.pov = -ca_atom.posn()                            
            
            self.win.glpane.gl_update()
        return
    
    def _update_chi_angles(self, aa):
        """
        """
        angle = aa.get_chi_angle(0)
        if angle:
            self.chi1Dial.setEnabled(True)
            self.chi1Dial.setValue(angle)
        else:
            self.chi1Dial.setEnabled(False)
            self.chi1Dial.setValue(0.0)
        
        angle = aa.get_chi_angle(1)
        if angle:
            self.chi2Dial.setEnabled(True)
            self.chi2Dial.setValue(angle)
        else:
            self.chi2Dial.setEnabled(False)
            self.chi2Dial.setValue(0.0)
        
        angle = aa.get_chi_angle(2)
        if angle:
            self.chi3Dial.setEnabled(True)
            self.chi3Dial.setValue(angle)
        else:
            self.chi3Dial.setEnabled(False)
            self.chi3Dial.setValue(0.0)
        
        angle = aa.get_chi_angle(3)
        if angle:
            self.chi4Dial.setEnabled(True)
            self.chi4Dial.setValue(angle)
        else:
            self.chi4Dial.setEnabled(False)
            self.chi4Dial.setValue(0.0)
        return
        
    def setCurrentAminoAcid(self, aa_index = -1):
        """
        Set the current amino acid to I{aa_index} and update the 
        "Current residue" combobox and the sequence editor.
        @param aa_index: the amino acid index. If negative, update the PM and 
                         sequence editor based on the current aa_index.
        @type  aa_index: int
        @note: This is the slot for the "Current residue" combobox.
        """
        if not self.current_protein:
            return
        
        if aa_index < 0:
            aa_index = self.current_protein.protein.get_current_amino_acid_index()
        
        if 0: # Debugging statement
            print"setCurrentAminoAcid(): aa_index=", aa_index
        self.currentResidueComboBox.setCurrentIndex(aa_index)
        self.current_protein.protein.set_current_amino_acid_index(aa_index)
        self.current_aa = self.current_protein.protein.get_current_amino_acid()
        self.display_and_recenter()
        self.sequenceEditor.setCursorPosition(aa_index)
        return
    
    def _rotateChiAngle(self, chi, angle):
        """
        Rotate around chi1 angle.
        """
        if not self.current_protein:
            return
        
        if self.current_aa:
            self.current_protein.protein.expand_rotamer(self.current_aa)
            self.current_aa.set_chi_angle(chi, angle)
            self.win.glpane.gl_update()

        return
    
    def _rotateChi1(self, angle):
        """
        Slot for Chi1 dial.
        """
        self._rotateChiAngle(0, angle)
        self.chi1Dial.updateValueLabel()
        return
    
    def _rotateChi2(self, angle):
        """
        Slot for Chi2 dial.
        """
        self._rotateChiAngle(1, angle)
        self.chi2Dial.updateValueLabel()
        return
        
    def _rotateChi3(self, angle):
        """
        Slot for Chi3 dial.
        """
        self._rotateChiAngle(2, angle)
        self.chi3Dial.updateValueLabel()
        return
        
    def _rotateChi4(self, angle):
        """
        Slot for Chi4 dial. 
        @note: this dial is currently hidden and unused.
        """
        self._rotateChiAngle(3, angle)
        return
   
    def _update_UI_do_updates(self):
        """
        Overrides superclass method. 
        
        @see: Command_PropertyManager._update_UI_do_updates()
        """
        
        self.current_protein = self.win.assy.getSelectedProteinChunk()
        
        if self.current_protein is self.previous_protein:
            if 0:
                print "Edit Protein: _update_UI_do_updates() - DO NOTHING."
            return
        
        # It is common that the user will unselect the current protein.
        # If so, set current_protein to previous_protein so that it 
        # (the previously selected protein) remains the current protein 
        # in the PM and sequence editor.
        if not self.current_protein:
            self.current_protein = self.previous_protein
            return
        
        # Update all PM widgets that need to be since something has changed.
        if 0:
            print "Edit Protein: _update_UI_do_updates() - UPDATING THE PMGR."
        self.update_name_field()
        self.update_length_field()
        self.sequenceEditor.updateSequence(proteinChunk = self.current_protein)
        self.update_residue_combobox()
        
        # NOTE: Changing the display style of the protein chunks can take some
        # time. We should put up the wait (hourglass) cursor and restore 
        # before returning.
        if self.previous_protein:
            self.previous_protein.setDisplayStyle(self.previous_protein_display_style)
            
        self.previous_protein = self.current_protein
        
        if self.current_protein:
            self.previous_protein_display_style = self.current_protein.getDisplayStyle()
            self.current_protein.setDisplayStyle(diPROTEIN)
            
        if self.current_protein:
            msg = "Editing structure <b>%s</b>." % self.current_protein.name
        else:
            msg = "Select a single structure to edit."
        self.updateMessage(msg)
        
        return
    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 )
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/Tools/Build Structures/InsertNanotube.png"

    def __init__( self, win, editCommand ):
        """
        Constructor for the Nanotube property manager.
        """
        self.endPoint1 = None
        self.endPoint2 = None

        self.nanotube = Nanotube() # A 5x5 CNT.

        _superclass.__init__( self,
                                 win,
                                 editCommand)

        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 ok_btn_clicked(self):
        """
        Slot for the OK button
        """
        if self.editCommand:
            self.editCommand.preview_or_finalize_structure(previewing = False)
            ##env.history.message(self.editCommand.logMessage)
        self.win.toolsDone()

    def cancel_btn_clicked(self):
        """
        Slot for the Cancel button.
        """
        if self.editCommand:
            self.editCommand.cancelStructure()
        self.win.toolsCancel()


    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 getFlyoutActionList(self):
        """
        Returns custom actionlist that will be used in a specific mode
        or editing a feature etc Example: while in movie mode,
        the _createFlyoutToolBar method calls this.
        """
        #'allActionsList' returns all actions in the flyout toolbar
        #including the subcontrolArea actions
        allActionsList = []

        #Action List for  subcontrol Area buttons.
        #In this mode there is really no subcontrol area.
        #We will treat subcontrol area same as 'command area'
        #(subcontrol area buttons will have an empty list as their command area
        #list). We will set  the Comamnd Area palette background color to the
        #subcontrol area.

        subControlAreaActionList =[]

        self.exitEditCommandAction.setChecked(True)
        subControlAreaActionList.append(self.exitEditCommandAction)

        separator = QAction(self.w)
        separator.setSeparator(True)
        subControlAreaActionList.append(separator)


        allActionsList.extend(subControlAreaActionList)

        #Empty actionlist for the 'Command Area'
        commandActionLists = []

        #Append empty 'lists' in 'commandActionLists equal to the
        #number of actions in subControlArea
        for i in range(len(subControlAreaActionList)):
            lst = []
            commandActionLists.append(lst)

        params = (subControlAreaActionList, commandActionLists, allActionsList)

        return params

    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))
        print "Bond Length =", self.nanotube.getBondLength()
        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
        """
        _n, _m = self.nanotube.setChirality(self.chiralityNSpinBox.value(),
                                            self.chiralityMSpinBox.value())

        #self.n, self.m = self.nanotube.getChirality()

        self.connect_or_disconnect_signals(isConnect = False)
        self.chiralityNSpinBox.setValue(_n)
        self.chiralityMSpinBox.setValue(_m)
        self.connect_or_disconnect_signals(isConnect = 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 Ui_BuildAtomsPropertyManager(Command_PropertyManager):
    """
    The Ui_BuildAtomsPropertyManager class defines UI elements for the Property 
    Manager of the B{Build Atoms mode}.
    
    @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
    """

    # The title that appears in the Property Manager header
    title = "Build Atoms"
    # The name of this Property Manager. This will be set to
    # the name of the PM_Dialog object via setObjectName().
    pmName = title
    # The relative path to the PNG file that appears in the header
    iconPath = "ui/actions/Tools/Build Structures/BuildAtoms.png"

    def __init__(self, command):
        """
        Constructor for the B{Build Atoms} property manager class that defines 
        its UI.
        
        @param command: The parent mode where this Property Manager is used
        @type  command: L{depositMode}        
        """

        self.previewGroupBox = None
        self.regularElementChooser = None
        self.PAM5Chooser = None
        self.PAM3Chooser = None
        self.elementChooser = None
        self.advancedOptionsGroupBox = None
        self.bondToolsGroupBox = None

        self.selectionFilterCheckBox = None
        self.filterlistLE = None
        self.selectedAtomInfoLabel = None

        #Initialize the following to None. (subclasses may not define this)
        #Make sure you initialize it before adding groupboxes!
        self.selectedAtomPosGroupBox = None
        self.showSelectedAtomInfoCheckBox = None

        _superclass.__init__(self, command)

        self.showTopRowButtons(PM_DONE_BUTTON | PM_WHATS_THIS_BUTTON)
        msg = ''
        self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault=False)

    def _addGroupBoxes(self):
        """
        Add various group boxes to the Build Atoms Property manager. 
        """
        self._addPreviewGroupBox()
        self._addAtomChooserGroupBox()
        self._addBondToolsGroupBox()

        #@@@TODO HIDE the bonds tool groupbox initially as the
        #by default, the atoms tool is active when BuildAtoms command is
        #finist invoked.
        self.bondToolsGroupBox.hide()

        self._addSelectionOptionsGroupBox()
        self._addAdvancedOptionsGroupBox()

    def _addPreviewGroupBox(self):
        """
        Adde the preview groupbox that shows the element selected in the 
        element chooser. 
        """
        self.previewGroupBox = PM_PreviewGroupBox(self, glpane=self.o)

    def _addAtomChooserGroupBox(self):
        """
        Add the Atom Chooser groupbox. This groupbox displays one of the 
        following three groupboxes depending on the choice selected in the 
        combobox:
          a) Periodic Table Elements L{self.regularElementChooser}
          b) PAM5 Atoms  L{self.PAM5Chooser}
          c) PAM3 Atoms  L{self.PAM3Chooser}
        @see: L{self.__updateAtomChooserGroupBoxes}
        """
        self.atomChooserGroupBox = \
            PM_GroupBox(self, title = "Atom Chooser")
        self._loadAtomChooserGroupBox(self.atomChooserGroupBox)

        self._updateAtomChooserGroupBoxes(currentIndex=0)

    def _addElementChooserGroupBox(self, inPmGroupBox):
        """
        Add the 'Element Chooser' groupbox. (present within the 
        Atom Chooser Groupbox) 
        """
        if not self.previewGroupBox:
            return

        elementViewer = self.previewGroupBox.elementViewer
        self.regularElementChooser = \
            PM_ElementChooser( inPmGroupBox,
                               parentPropMgr = self,
                               elementViewer = elementViewer)

    def _add_PAM5_AtomChooserGroupBox(self, inPmGroupBox):
        """
        Add the 'PAM5 Atom Chooser' groupbox (present within the 
        Atom Chooser Groupbox) 
        """
        if not self.previewGroupBox:
            return

        elementViewer = self.previewGroupBox.elementViewer
        self.PAM5Chooser = \
            PM_PAM5_AtomChooser( inPmGroupBox,
                                 parentPropMgr = self,
                                 elementViewer = elementViewer)

    def _add_PAM3_AtomChooserGroupBox(self, inPmGroupBox):
        """
        Add the 'PAM3 Atom Chooser' groupbox (present within the 
        Atom Chooser Groupbox)
        """
        if not self.previewGroupBox:
            return

        elementViewer = self.previewGroupBox.elementViewer
        self.PAM3Chooser = \
            PM_PAM3_AtomChooser( inPmGroupBox,
                                 parentPropMgr = self,
                                 elementViewer = elementViewer)

    def _hideAllAtomChooserGroupBoxes(self):
        """
        Hides all Atom Chooser group boxes.
        """
        if self.regularElementChooser:
            self.regularElementChooser.hide()
        if self.PAM5Chooser:
            self.PAM5Chooser.hide()
        if self.PAM3Chooser:
            self.PAM3Chooser.hide()

    def _addBondToolsGroupBox(self):
        """
        Add the 'Bond Tools' groupbox.
        """
        self.bondToolsGroupBox = \
            PM_GroupBox( self, title = "Bond Tools")

        self._loadBondToolsGroupBox(self.bondToolsGroupBox)

    def _addSelectionOptionsGroupBox(self):
        """
        Add 'Selection Options' groupbox
        """
        self.selectionOptionsGroupBox = \
            PM_GroupBox( self, title = "Selection Options" )

        self._loadSelectionOptionsGroupBox(self.selectionOptionsGroupBox)

    def _loadAtomChooserGroupBox(self, inPmGroupBox):
        """
        Load the widgets inside the Atom Chooser groupbox.
        @param inPmGroupBox: The Atom Chooser box in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """
        atomChooserChoices = [
            "Periodic Table Elements", "PAM5 Atoms", "PAM3 Atoms"
        ]

        self.atomChooserComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = '',
                         choices      = atomChooserChoices,
                         index        = 0,
                         setAsDefault = False,
                         spanWidth    = True )

        #Following fixes bug 2550
        self.atomChooserComboBox.setFocusPolicy(Qt.NoFocus)

        self._addElementChooserGroupBox(inPmGroupBox)
        self._add_PAM5_AtomChooserGroupBox(inPmGroupBox)
        self._add_PAM3_AtomChooserGroupBox(inPmGroupBox)

    def _loadSelectionOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Selection Options group box.
        @param inPmGroupBox: The Selection Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.selectionFilterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text  = "Enable atom selection filter",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )
        self.selectionFilterCheckBox.setDefaultValue(False)

        self.filterlistLE = PM_LineEdit(inPmGroupBox,
                                        label="",
                                        text="",
                                        setAsDefault=False,
                                        spanWidth=True)
        self.filterlistLE.setReadOnly(True)

        if self.selectionFilterCheckBox.isChecked():
            self.filterlistLE.setEnabled(True)
        else:
            self.filterlistLE.setEnabled(False)

        self.showSelectedAtomInfoCheckBox = \
            PM_CheckBox(
                inPmGroupBox,
                text  = "Show Selected Atom Info",
                widgetColumn = 0,
                state        = Qt.Unchecked)

        self.selectedAtomPosGroupBox = \
            PM_GroupBox( inPmGroupBox, title = "")
        self._loadSelectedAtomPosGroupBox(self.selectedAtomPosGroupBox)

        self.toggle_selectedAtomPosGroupBox(show=0)
        self.enable_or_disable_selectedAtomPosGroupBox(bool_enable=False)

        self.reshapeSelectionCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Dragging reshapes selection',
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )

        connect_checkbox_with_boolean_pref(self.reshapeSelectionCheckBox,
                                           reshapeAtomsSelection_prefs_key)

        env.prefs[reshapeAtomsSelection_prefs_key] = False

        self.waterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = "Z depth filter (water surface)",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )

    def _loadSelectedAtomPosGroupBox(self, inPmGroupBox):
        """
        Load the selected Atoms position groupbox It is a sub-gropbox of 
        L{self.selectionOptionsGroupBox)
        @param inPmGroupBox: 'The Selected Atom Position Groupbox'
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.selectedAtomLineEdit = PM_LineEdit(inPmGroupBox,
                                                label="Selected Atom:",
                                                text="",
                                                setAsDefault=False,
                                                spanWidth=False)

        self.selectedAtomLineEdit.setReadOnly(True)
        self.selectedAtomLineEdit.setEnabled(False)

        self.coordinateSpinboxes = PM_CoordinateSpinBoxes(inPmGroupBox)

        # User input to specify x-coordinate
        self.xCoordOfSelectedAtom = self.coordinateSpinboxes.xSpinBox
        # User input to specify y-coordinate
        self.yCoordOfSelectedAtom = self.coordinateSpinboxes.ySpinBox
        # User input to specify z-coordinate
        self.zCoordOfSelectedAtom = self.coordinateSpinboxes.zSpinBox

    def _addAdvancedOptionsGroupBox(self):
        """
        Add 'Advanced Options' groupbox
        """
        self.advancedOptionsGroupBox = \
            PM_GroupBox( self, title = "Advanced Options" )

        self._loadAdvancedOptionsGroupBox(self.advancedOptionsGroupBox)

    def _loadAdvancedOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Advanced Options group box.
        @param inPmGroupBox: The Advanced Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.autoBondCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Auto bond',
                         widgetColumn = 0,
                         state        = Qt.Checked  )

        self.highlightingCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = "Hover highlighting",
                         widgetColumn = 0,
                         state        = Qt.Checked )

    def _loadBondToolsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Bond Tools group box.
        @param inPmGroupBox: The Bond Tools box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}        
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        BOND_TOOL_BUTTONS = \
                          [ ( "QToolButton", 0,  "SINGLE",    "", "", None, 0),
                            ( "QToolButton", 1,  "DOUBLE",    "", "", None, 1),
                            ( "QToolButton", 2,  "TRIPLE",    "", "", None, 2),
                            ( "QToolButton", 3,  "AROMATIC",  "", "", None, 3),
                            ( "QToolButton", 4,  "GRAPHITIC", "", "", None, 4),
                            ( "QToolButton", 5,  "CUTBONDS",  "", "", None, 5)
                          ]


        self.bondToolButtonRow = \
            PM_ToolButtonRow(
                inPmGroupBox,
                title        = "",
                buttonList   = BOND_TOOL_BUTTONS,
                checkedId    = None,
                setAsDefault = True )

    def _addWhatsThisText(self):
        """
        "What's This" text for widgets in this Property Manager.
        """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_BuildAtomsPropertyManager
        whatsThis_BuildAtomsPropertyManager(self)

    def _addToolTipText(self):
        """
        Tool Tip text for widgets in this Property Manager.  
        """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_BuildAtomsPropertyManager
        ToolTip_BuildAtomsPropertyManager(self)

    def toggle_selectedAtomPosGroupBox(self, show=0):
        """
        Show or hide L{self.selectedAtomPosGroupBox} depending on the state of
        the checkbox (L{self.showSelectedAtomInfoCheckBox}) 
        @param show: Flag that shows or hides the groupbox (can have values 
                     0 or 1
        @type  show: int
        """
        if show:
            self.selectedAtomPosGroupBox.show()
        else:
            self.selectedAtomPosGroupBox.hide()

    def enable_or_disable_selectedAtomPosGroupBox(self, bool_enable=False):
        """
        Enable or disable Selected AtomPosGroupBox present within 
        'selection options' and also the checkbox that shows or hide this 
        groupbox. These two widgets are enabled when only a single atom is 
        selected from the 3D workspace. 
        @param bool_enable: Flag that enables or disables widgets
        @type  bool_enable: boolean
        """
        if self.showSelectedAtomInfoCheckBox:
            self.showSelectedAtomInfoCheckBox.setEnabled(bool_enable)
        if self.selectedAtomPosGroupBox:
            self.selectedAtomPosGroupBox.setEnabled(bool_enable)

    def _updateAtomChooserGroupBoxes(self, currentIndex):
        """
        Updates the Atom Chooser Groupbox. It displays one of the 
        following three groupboxes depending on the choice selected in the 
        combobox:
          a) Periodic Table Elements L{self.regularElementChooser}
          b) PAM5 Atoms  L{self.PAM5Chooser}
          c) PAM3 Atoms  L{self.PAM3Chooser}
        It also sets self.elementChooser to the current active Atom chooser 
        and updates the display accordingly in the Preview groupbox.
        """
        self._hideAllAtomChooserGroupBoxes()

        if currentIndex is 0:
            self.elementChooser = self.regularElementChooser
            self.regularElementChooser.show()
        if currentIndex is 1:
            self.elementChooser = self.PAM5Chooser
            self.PAM5Chooser.show()
        if currentIndex is 2:
            self.elementChooser = self.PAM3Chooser
            self.PAM3Chooser.show()

        if self.elementChooser:
            self.elementChooser.updateElementViewer()

        self.updateMessage()

    def updateMessage(self):
        """
        Update the Message groupbox with informative message. 
        Subclasses should override this.
        """
        pass
class Ui_BuildCrystal_PropertyManager(Command_PropertyManager):
    """
    The Ui_BuildCrystal_PropertyManager class defines UI elements for the Property
    Manager of the B{Crystal mode}.

    @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> - the title that appears in the property manager header.
    title = "Build Crystal"
    # <iconPath> - full path to PNG file that appears in the header.
    # The name of this Property Manager. This will be set to
    # the name of the PM_Dialog object via setObjectName().
    pmName = title
    iconPath = "ui/actions/Tools/Build Structures/Build Crystal.png"

    def __init__(self, command):
        """
        Constructor for the B{Crystal} property manager class that defines
        its UI.

        @param command: The parent mode where this Property Manager is used
        @type  command: L{BuildCrystal_Command}
        """


        _superclass.__init__(self, command)

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_CANCEL_BUTTON | \
                                PM_WHATS_THIS_BUTTON)


    def _addGroupBoxes(self):
        """
        Add various group boxes to the Property manager.
        """
        self._addCrystalSpecsGroupbox()
        self._addLayerPropertiesGroupBox()
        self._addDisplayOptionsGroupBox()
        self._addAdvancedOptionsGroupBox()

    def _addCrystalSpecsGroupbox(self):
        """
        Add 'Crystal groupbox' to the PM
        """
        self.crystalSpecsGroupBox = \
            PM_GroupBox(self, title = "Crystal Specifications")
        self._loadCrystalSpecsGroupBox(self.crystalSpecsGroupBox)

    def _addLayerPropertiesGroupBox(self):
        """
        Add 'Layer Properties' groupbox to the PM
        """
        self.layerPropertiesGroupBox = \
            PM_GroupBox(self, title = "Layer Properties")
        self._loadLayerPropertiesGroupBox(self.layerPropertiesGroupBox)


    def _addAdvancedOptionsGroupBox(self):
        """
        Add 'Advanced Options' groupbox
        """
        self.advancedOptionsGroupBox = \
            PM_GroupBox( self, title = "Advanced Options" )
        self._loadAdvancedOptionsGroupBox(self.advancedOptionsGroupBox)

    def _addDisplayOptionsGroupBox(self):
        """
        Add 'Display Options' groupbox
        """
        self.displayOptionsGroupBox = PM_GroupBox(self,
                                                  title = 'Display Options')
        self._loadDisplayOptionsGroupBox(self.displayOptionsGroupBox)

    def _loadCrystalSpecsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Crystal Specifications group box.
        @param inPmGroupBox: The Crystal Specifications groupbox in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        latticeChoices = ["Diamond", "Lonsdaleite"]

        self.latticeCBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = 'Lattice:',
                         labelColumn  = 0,
                         choices      = latticeChoices,
                         index        = 0,
                         setAsDefault = True,
                         spanWidth    = False )

        # Button list to create a toolbutton row.
        # Format:
        # - buttonType,
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        BUTTON_LIST = [
            ( "QToolButton", 0,  "Surface 100",
              "ui/actions/Properties Manager/Surface100.png",
              "Surface 100", "", 0),

            ( "QToolButton", 1,  "Surface 110",
              "ui/actions/Properties Manager/Surface110.png",
              "Surface 110", "", 1),

            ( "QToolButton", 2,  "Surface 111",
              "ui/actions/Properties Manager/Surface111.png",
              "Surface 110", "", 2)
            ]
        self.gridOrientationButtonRow = \
            PM_ToolButtonRow(inPmGroupBox,
                               title        = "",
                               label        = "Orientation:",
                               buttonList   = BUTTON_LIST,
                               checkedId    = 0,
                               setAsDefault = True,
                               spanWidth   = False
                               )

        self.orientButtonGroup = self.gridOrientationButtonRow.buttonGroup
        self.surface100_btn = self.gridOrientationButtonRow.getButtonById(0)
        self.surface110_btn = self.gridOrientationButtonRow.getButtonById(1)
        self.surface111_btn = self.gridOrientationButtonRow.getButtonById(2)

        self.rotateGridByAngleSpinBox = \
            PM_SpinBox( inPmGroupBox,
                        label         =  "Rotate by: ",
                        labelColumn   =  0,
                        value         =  45,
                        minimum       =  0,
                        maximum       =  360,
                        singleStep    =  5,
                        suffix        = " degrees")

        GRID_ANGLE_BUTTONS = [
                        ("QToolButton", 0,  "Anticlockwise",
                         "ui/actions/Properties Manager/rotate_minus.png",
                         "", "+", 0 ),

                        ( "QToolButton", 1,  "Clockwise",
                          "ui/actions/Properties Manager/rotate_plus.png",
                          "", "-", 1 )
                        ]

        self.gridRotateButtonRow = \
            PM_ToolButtonRow( inPmGroupBox,
                              title        = "",
                              buttonList   = GRID_ANGLE_BUTTONS,
                              label        = 'Rotate grid:',
                              isAutoRaise  =  False,
                              isCheckable  =  False
                            )
        self.rotGridAntiClockwiseButton = \
            self.gridRotateButtonRow.getButtonById(0)
        self.rotGridClockwiseButton = \
            self.gridRotateButtonRow.getButtonById(1)

    def _loadLayerPropertiesGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Layer Properties group box.
        @param inPmGroupBox: The Layer Properties groupbox in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.currentLayerComboBox = \
            PM_ComboBox( inPmGroupBox,
                         index     = 0,
                         spanWidth = True
                        )

        self.addLayerButton = PM_PushButton(inPmGroupBox)
        self.addLayerButton.setIcon(
            geticon('ui/actions/Properties Manager/addlayer.png'))
        self.addLayerButton.setFixedSize(QSize(26, 26))
        self.addLayerButton.setIconSize(QSize(22, 22))

        # A widget list to create a widget row.
        # Format:
        # - Widget type,
        # - widget object,
        # - column

        firstRowWidgetList = [('PM_ComboBox', self.currentLayerComboBox, 1),
                              ('PM_PushButton', self.addLayerButton, 2)
                              ]

        widgetRow = PM_WidgetRow(inPmGroupBox,
                                 title     = '',
                                 widgetList = firstRowWidgetList,
                                 label = "Layer:",
                                 labelColumn  = 0,
                                 )

        self.layerCellsSpinBox = \
             PM_SpinBox( inPmGroupBox,
                        label         =  "Lattice cells:",
                        labelColumn   =  0,
                        value         =  2,
                        minimum       =  1,
                        maximum       =  25
                      )

        self.layerThicknessLineEdit = PM_LineEdit(inPmGroupBox,
                                         label        = "Thickness:",
                                         text         = "",
                                         setAsDefault = False,
                                         spanWidth    = False )

        #self.layerThicknessLineEdit.setReadOnly(True)
        self.layerThicknessLineEdit.setDisabled(True)
        tooltip = "Thickness of layer in Angstroms"
        self.layerThicknessLineEdit.setToolTip(tooltip)


    def _loadAdvancedOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Advanced Options group box.
        @param inPmGroupBox: The Advanced Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        self.snapGridCheckBox = \
            PM_CheckBox(inPmGroupBox,
                        text = "Snap to grid",
                        state = Qt.Checked
                        )
        tooltip = "Snap selection point to a nearest cell grid point."
        self.snapGridCheckBox.setToolTip(tooltip)

        self.freeViewCheckBox = \
            PM_CheckBox(inPmGroupBox,
                        text = "Enable free view",
                        state = Qt.Unchecked
                    )

    def _loadDisplayOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Display Options groupbox.
        @param inPmGroupBox: The Display Options groupbox
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        displayChoices = ['Tubes', 'Spheres']

        self.dispModeComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = 'Display style:',
                         choices      = displayChoices,
                         index        = 0,
                         setAsDefault = False,
                         spanWidth    = False )


        self.gridLineCheckBox = PM_CheckBox(inPmGroupBox,
                                            text = "Show grid lines",
                                            widgetColumn = 0,
                                            state        = Qt.Checked)


        self.fullModelCheckBox = PM_CheckBox(inPmGroupBox,
                                            text = "Show model",
                                            widgetColumn = 0,
                                            state        = Qt.Unchecked)

    def _addWhatsThisText(self):
        """
        What's This text for widgets in this Property Manager.

        @note: Many PM widgets are still missing their "What's This" text.
        """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_CookiePropertyManager
        whatsThis_CookiePropertyManager(self)

    def _addToolTipText(self):
        """
        What's Tool Tip text for widgets in this Property Manager.
        """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_CookiePropertyManager
        ToolTip_CookiePropertyManager(self)
Beispiel #32
0
class DnaSegment_PropertyManager(DnaOrCnt_PropertyManager):
    """
    The DnaSegmenta_PropertyManager class provides a Property Manager
    for the DnaSegment_EditCommand.

    @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 = "DnaSegment Properties"
    iconPath = "ui/actions/Tools/Build Structures/DNA.png"

    def __init__(self, command):
        """
        Constructor for the Build DNA property manager.
        """

        self.endPoint1 = V(0, 0, 0)
        self.endPoint2 = V(0, 0, 0)

        self._numberOfBases = 0
        self._conformation = 'B-DNA'
        self.duplexRise = 3.18
        self.basesPerTurn = 10
        self.dnaModel = 'PAM3'

        _superclass.__init__(self, command)


        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_CANCEL_BUTTON | \
                                PM_PREVIEW_BUTTON | \
                                PM_WHATS_THIS_BUTTON)

        msg = "Use resize handles to resize the segment. Drag any axis or sugar"\
            " atom for translation or rotation about axis respectively. Dragging"\
            " any bond will freely move the whole segment."
        self.updateMessage(msg)

    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

        _superclass.connect_or_disconnect_signals(self, isConnect)

        change_connect(self.numberOfBasePairsSpinBox,
                       SIGNAL("valueChanged(int)"), self.numberOfBasesChanged)

        change_connect(self.basesPerTurnDoubleSpinBox,
                       SIGNAL("valueChanged(double)"),
                       self.basesPerTurnChanged)

        change_connect(self.duplexRiseDoubleSpinBox,
                       SIGNAL("valueChanged(double)"), self.duplexRiseChanged)

        change_connect(self.showCursorTextCheckBox,
                       SIGNAL('stateChanged(int)'),
                       self._update_state_of_cursorTextGroupBox)

    def _update_UI_do_updates(self):
        """
        @see: Command_PropertyManager._update_UI_do_updates()
        @see: DnaSegment_EditCommand.command_update_UI()
        @see: DnaSegment_EditCommand.hasResizableStructure()
        @see: self._current_model_changed_params()
        """
        currentParams = self._current_model_changed_params()
        #Optimization. Return from the model_changed method if the
        #params are the same.
        if same_vals(currentParams, self._previous_model_changed_params):
            return

        isStructResizable, why_not = currentParams
        #update the self._previous_model_changed_params with this new param set.
        self._previous_model_changed_params = currentParams

        if not isStructResizable:
            #disable all widgets
            if self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(False)
                msg = redmsg("DnaSegment is not resizable. Reason: %s" %
                             (why_not))
                self.updateMessage(msg)
        else:
            if not self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(True)
                msg = "Use resize handles to resize the segment. Drag any axis or sugar"\
                    " atom for translation or rotation about axis respectively. Dragging"\
                    " any bond will freely move the whole segment."
                self.updateMessage(msg)

    def _current_model_changed_params(self):
        """
        Returns a tuple containing the parameters that will be compared
        against the previously stored parameters. This provides a quick test
        to determine whether to do more things in self.model_changed()
        @see: self.model_changed() which calls this
        @see: self._previous_model_changed_params attr.
        """
        params = None

        if self.command:
            isStructResizable, why_not = self.command.hasResizableStructure()
            params = (isStructResizable, why_not)

        return params

    def show(self):
        """
        Show this property manager. Overrides EditCommand_PM.show()
        This method also retrives the name information from the
        command's structure for its name line edit field.
        @see: DnaSegment_EditCommand.getStructureName()
        @see: self.close()
        """
        _superclass.show(self)
        if self.command is not None:
            name = self.command.getStructureName()
            if name is not None:
                self.nameLineEdit.setText(name)

    def close(self):
        """
        Close this property manager.
        Also sets the name of the self.command's structure to the one
        displayed in the line edit field.
        @see self.show()
        @see: DnaSegment_EditCommand.setStructureName
        """
        if self.command is not None:
            name = str(self.nameLineEdit.text())
            self.command.setStructureName(name)
        _superclass.close(self)

    def setParameters(self, params):
        """
        This is usually called when you are editing an existing structure.
        Some property manager ui elements then display the information
        obtained from the object being edited.
        TODO:
        - Make this a EditCommand_PM API method?
        - See also the routines GraphicsMode.setParams or object.setProps
        ..better to name them all in one style?
        """
        numberOfBasePairs, \
                         dnaForm, \
                         dnaModel,\
                         basesPerTurn, \
                         duplexRise, \
                         endPoint1, \
                         endPoint2 , \
                         color = params

        if numberOfBasePairs is not None:
            self.numberOfBasePairsSpinBox.setValue(numberOfBasePairs)
        if dnaForm is not None:
            self._conformation = dnaForm
        if dnaModel is not None:
            self.dnaModel = dnaModel
        if duplexRise is not None:
            self.duplexRiseDoubleSpinBox.setValue(duplexRise)
        if basesPerTurn is not None:
            self.basesPerTurnDoubleSpinBox.setValue(basesPerTurn)
        if endPoint1 is not None:
            self.endPoint1 = endPoint1
        if endPoint2 is not None:
            self.endPoint2 = endPoint2
        if color is not None:
            self._colorChooser.setColor(color)

    def getParameters(self):
        """
        """
        #See bug 2802 for details about the parameter
        #'number_of_basePairs_from_struct'. Basically it is used to check
        #if the structure got modified (e.g. because of undo)
        #The numberOfBases parameter obtained from the propMgr is given as a
        #separate parameter for the reasons mentioned in bug 2802
        #-- Ninad 2008-04-12
        number_of_basePairs_from_struct = None
        if self.command.hasValidStructure():
            number_of_basePairs_from_struct = self.command.struct.getNumberOfBasePairs(
            )
        numberOfBases = self.numberOfBasePairsSpinBox.value()
        dnaForm = self._conformation
        dnaModel = self.dnaModel
        basesPerTurn = self.basesPerTurn
        duplexRise = self.duplexRise
        color = self._colorChooser.getColor()

        return (number_of_basePairs_from_struct, numberOfBases, dnaForm,
                dnaModel, basesPerTurn, duplexRise, self.endPoint1,
                self.endPoint2, color)

    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

    def basesPerTurnChanged(self, basesPerTurn):
        """
        Slot for the B{Bases per turn} spinbox.
        """
        self.basesPerTurn = basesPerTurn

    def duplexRiseChanged(self, rise):
        """
        Slot for the B{Rise} spinbox.
        """
        self.duplexRise = rise

    def _addGroupBoxes(self):
        """
        Add the DNA Property Manager group boxes.
        """

        self._pmGroupBox1 = PM_GroupBox(self, title="Parameters")
        self._loadGroupBox1(self._pmGroupBox1)

        self._displayOptionsGroupBox = PM_GroupBox(self,
                                                   title="Display Options")
        self._loadDisplayOptionsGroupBox(self._displayOptionsGroupBox)

    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit(pmGroupBox,
                                        label="Segment name:",
                                        text="",
                                        setAsDefault=False)

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Base pairs:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  self.basesPerTurn,
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )
        self.basesPerTurnDoubleSpinBox.setDisabled(True)

        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  self.duplexRise,
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )
        self.duplexRiseDoubleSpinBox.setDisabled(True)

        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)

    def _loadDisplayOptionsGroupBox(self, pmGroupBox):
        """
        Overrides superclass method.
        Also loads the color chooser widget.
        """
        self._loadColorChooser(pmGroupBox)
        _superclass._loadDisplayOptionsGroupBox(self, pmGroupBox)

    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,
            dnaSegmentEditCommand_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)
                  ("Number of base pairs",
                   dnaSegmentEditCommand_cursorTextCheckBox_numberOfBasePairs_prefs_key),

                   ("Duplex length",
                    dnaSegmentEditCommand_cursorTextCheckBox_length_prefs_key),

                    ("Number of basepairs to be changed",
                     dnaSegmentEditCommand_cursorTextCheckBox_changedBasePairs_prefs_key)
                 ]

        return params

    def _addWhatsThisText(self):
        """
        Add what's this text.
        """
        pass

    def _addToolTipText(self):
        """
        Add Tooltip text
        """
        pass
    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")  

        self.saveSequenceButton = PM_ToolButton(
            self, 
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png") 

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        
        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        

        #Widgets to include in the widget row. 
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8), 
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10), 
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )
Beispiel #34
0
    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options,
        Find and replace widgets etc.
        """

        self.loadSequenceButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Open.png")

        self.saveSequenceButton = PM_ToolButton(
            self,
            iconPath="ui/actions/Properties Manager/Save_Strand_Sequence.png")

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        # Hide load and save buttons until they are implemented. -Mark 2008-12-20.
        self.loadSequenceButton.hide()
        self.saveSequenceButton.hide()

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)

        self.replaceLineEdit = \
            PM_LineEdit( self,
                         label        = "  Replace:",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text="Replace")

        # Hide Replace widgets until we add support for transmuting residues.
        # Mark 2008-12-19
        #self.replaceLabel.hide()
        self.replacePushButton.hide()
        self.replaceLineEdit.hide()

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        #Widgets to include in the widget row.
        widgetList = [
            ('PM_ToolButton', self.loadSequenceButton, 0),
            ('PM_ToolButton', self.saveSequenceButton, 1),
            ('QLabel', "                                     Find:", 4),
            ('PM_LineEdit', self.findLineEdit, 5),
            ('PM_ToolButton', self.findOptionsToolButton, 6),
            ('PM_ToolButton', self.findPreviousToolButton, 7),
            ('PM_ToolButton', self.findNextToolButton, 8),
            #('PM_Label',      self.replaceLabel, 9),
            ('PM_TextEdit', self.replaceLineEdit, 9),
            ('PM_PushButton', self.replacePushButton, 10),
            ('PM_Label', self.warningSign, 11),
            ('PM_Label', self.phraseNotFoundLabel, 12),
            ('QSpacerItem', 5, 5, 13)
        ]

        widgetRow = PM_WidgetRow(self,
                                 title='',
                                 widgetList=widgetList,
                                 label="",
                                 spanWidth=True)
        return
class DnaStrand_PropertyManager( DnaOrCnt_PropertyManager):
    """
    The DnaStrand_PropertyManager class provides a Property Manager
    for the DnaStrand_EditCommand.

    @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         =  "DnaStrand Properties"
    iconPath      =  "ui/actions/Properties Manager/Strand.png"

    def __init__( self, command ):
        """
        Constructor for the Build DNA property manager.
        """

        self.sequenceEditor = None

        self._numberOfBases = 0
        self._conformation = 'B-DNA'
        self.dnaModel = 'PAM3'

        _superclass.__init__( self, command)

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_WHATS_THIS_BUTTON)
        return

    def _addGroupBoxes( self ):
        """
        Add group boxes to this PM.
        """

        self._pmGroupBox1 = PM_GroupBox( self, title = "Parameters" )
        self._loadGroupBox1( self._pmGroupBox1 )
        self._displayOptionsGroupBox = PM_GroupBox( self,
                                                    title = "Display Options" )
        self._loadDisplayOptionsGroupBox( self._displayOptionsGroupBox )

        #Sequence Editor. This is NOT a groupbox, needs cleanup. Doing it here
        #so that the sequence editor gets connected! Perhaps
        #superclass should define _loadAdditionalWidgets. -- Ninad2008-10-03
        self._loadSequenceEditor()
        return

    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 1.
        """

        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                         label         =  "Name:",
                         text          =  "",
                         setAsDefault  =  False)

        self.numberOfBasesSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Number of bases:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.disableStructHighlightingCheckbox = \
            PM_CheckBox( pmGroupBox,
                         text         = "Don't highlight while editing DNA",
                         widgetColumn  = 0,
                         state        = Qt.Unchecked,
                         setAsDefault = True,
                         spanWidth = True
                         )

        #As of 2008-03-31, the properties such as number of bases will be
        #editable only by using the resize handles.
        self.numberOfBasesSpinBox.setEnabled(False)
        return

    def _loadSequenceEditor(self):
        """
        Temporary code  that shows the Sequence editor ..a doc widget docked
        at the bottom of the mainwindow. The implementation is going to change
        before 'rattleSnake' product release.
        As of 2007-11-20: This feature (sequence editor) is waiting
        for the ongoing dna model work to complete.
        """
        self.sequenceEditor = self.win.createDnaSequenceEditorIfNeeded()
        self.sequenceEditor.hide()
        return

    def _loadDisplayOptionsGroupBox(self, pmGroupBox):
        """
        Overrides superclass method.
        Also loads the color chooser widget.
        """
        self._loadColorChooser(pmGroupBox)
        _superclass._loadDisplayOptionsGroupBox(self, pmGroupBox)
        return

    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 ,
            dnaStrandEditCommand_showCursorTextCheckBox_prefs_key)
        return


    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)
                  ("Number of bases",
                   dnaStrandEditCommand_cursorTextCheckBox_numberOfBases_prefs_key),

                  ("Number of bases to be changed",
                   dnaStrandEditCommand_cursorTextCheckBox_changedBases_prefs_key)
                 ]

        return params


    def getParameters(self):
        name = self.nameLineEdit.text()
        numberOfBases = self.numberOfBasesSpinBox.value()
        dnaForm  = self._conformation
        dnaModel = self.dnaModel
        color = self._colorChooser.getColor()

        return (numberOfBases,
                dnaForm,
                dnaModel,
                color,
                name
                )

    def setParameters(self, params):
        """
        This is usually called when you are editing an existing structure.
        It also gets called when selecting a new strand (within this command).
        Some property manager ui elements then display the information
        obtained from the object being edited.
        TODO:
        - Make this a EditCommand_PM API method?
        - See also the routines GraphicsMode.setParams or object.setProps
        ..better to name them all in one style?
        """
        numberOfBases, \
            dnaForm, \
            dnaModel, \
            color, \
            name = params

        if numberOfBases is not None:
            self.numberOfBasesSpinBox.setValue(numberOfBases)
        if dnaForm is not None:
            self._conformation = dnaForm
        if dnaModel is not None:
            self.dnaModel = dnaModel

        if color is not None:
            self._colorChooser.setColor(color)

        if name:  # Minimal test. Should add a validator. --Mark 2008-12-16
            self.nameLineEdit.setText(name)

        # This gets called when we enter the command *and* when selecting a new
        # strand. In either case, we must update the sequence in the sequenece
        # editor. Fixes bug 2951. --Mark 2008-12-16
        if self.command and self.command.hasValidStructure():
            #print "setParameters(): loading sequence in sequence editor for ", name
            self.updateSequence(strand = self.command.struct)
        return

    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
        """
        #TODO: This is a temporary fix for a bug. When you invoke a temporary
        # mode Entering such a temporary mode keeps the signals of
        #PM from the previous mode connected (
        #but while exiting that temporary mode and reentering the
        #previous mode, it atucally reconnects the signal! This gives rise to
        #lots  of bugs. This needs more general fix in Temporary mode API.
        # -- Ninad 2008-01-09 (similar comment exists in MovePropertyManager.py

        if isConnect and self.isAlreadyConnected:
            if debug_flags.atom_debug:
                print_compact_stack("warning: attempt to connect widgets"\
                                    "in this PM that are already connected." )
            return

        if not isConnect and self.isAlreadyDisconnected:
            if debug_flags.atom_debug:
                print_compact_stack("warning: attempt to disconnect widgets"\
                                    "in this PM that are already disconnected.")
            return

        self.isAlreadyConnected = isConnect
        self.isAlreadyDisconnected = not isConnect

        if isConnect:
            change_connect = self.win.connect
        else:
            change_connect = self.win.disconnect


        if self.sequenceEditor:
            self.sequenceEditor.connect_or_disconnect_signals(isConnect)

        _superclass.connect_or_disconnect_signals(self, isConnect)


        change_connect(self.disableStructHighlightingCheckbox,
                       SIGNAL('stateChanged(int)'),
                       self.change_struct_highlightPolicy)

        change_connect(self.showCursorTextCheckBox,
                       SIGNAL('stateChanged(int)'),
                       self._update_state_of_cursorTextGroupBox)

        change_connect(self.nameLineEdit,
                       SIGNAL("editingFinished()"),
                       self._nameChanged)
        return

    def _update_UI_do_updates(self):
        """
        @see: Command_PropertyManager. _update_UI_do_updates()
        @see: DnaStrand_EditCommand.command_update_UI()
        @see: DnaStrand_EditCommand.hasResizableStructure()
        """
        if not self.command.hasValidStructure():
            print "DnaStrand not a valid structure."
            self._pmGroupBox1.setEnabled(False)
            self._displayOptionsGroupBox.setEnabled(False)
            self.sequenceEditor.updateSequence(strand = " ")
            self.sequenceEditor.setEnabled(False)
            self.nameLineEdit.setText("")
            self.numberOfBasesSpinBox.setValue(0)
            return
        else:
            self._pmGroupBox1.setEnabled(True)
            self._displayOptionsGroupBox.setEnabled(True)
            self.sequenceEditor.setEnabled(True)

        isStructResizable, why_not = self.command.hasResizableStructure()
        if not isStructResizable:
            #disable all widgets
            if self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(False)
                msg1 = ("Attention: ") % (self.command.struct.name)
                msg2 = redmsg("DnaStrand <b>%s</b> is not resizable. Reason: %s" % \
                              (self.command.struct.name, why_not))
                self.updateMessage(msg1 + msg2)
        else:
            if not self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(True)
            msg1 = ("Editing <b>%s</b>. ") % (self.command.struct.name)
            msg2 = "Use resize handles to resize the strand. "\
                 "Use the <i>Sequence Editor</i> to edit the sequence."
            self.updateMessage(msg1 + msg2)
        return

    def close(self):
        """
        Close this property manager.
        Also sets the name of the self.command's structure to the one
        displayed in the line edit field.
        @see self.show()
        @see: DnaSegment_EditCommand.setStructureName
        """
        if self.command is not None:
            name = str(self.nameLineEdit.text())
            self.command.setStructureName(name)

        if self.sequenceEditor:
            self.sequenceEditor.close()

        _superclass.close(self)
        return

    def updateSequence(self, strand = None):
        """
        Public method provided for convenience. If any callers outside of this
        command need to update the sequence in the sequence editor, they can simply
        do DnaStrand_ProprtyManager.updateSequence() rather than
        DnaStrand_ProprtyManager.sequenceEditor.updateSequence()
        @see: Ui_DnaSequenceEditor.updateSequence()
        """
        if self.sequenceEditor:
            self.sequenceEditor.updateSequence(strand = strand)
        return

    def change_struct_highlightPolicy(self,checkedState = False):
        """
        Change the 'highlight policy' of the structure being edited
        (i.e. self.command.struct) .
        @param checkedState: The checked state of the checkbox that says
                             'Don't highlight while editing DNA'. So, it
                             its True, the structure being edited won't get
                             highlighted.
        @see: DnaStrand.setHighlightPolicy for more comments
        """
        if self.command and self.command.hasValidStructure():
            highlight = not checkedState
            self.command.struct.setHighlightPolicy(highlight = highlight)
        return

    def _addWhatsThisText(self):
        """
        Add what's this text.
        Abstract method.
        """
        pass

    def _nameChanged(self): # Added by Mark. 2008-12-16
        """
        Slot for "Name" field. Changes the name of the strand if the user types
        in a new name.

        @warning: this lacks a validator. User can type in a name with invalid
                  characters.
        """
        if not self.command.hasValidStructure():
            return

        name = str(self.nameLineEdit.text())

        if not name: # Minimal test. Should add a validator. Ask Bruce for example validator code somewhere. --Mark 2008-12-16
            if self.command.hasValidStructure():
                self.nameLineEdit.setText(self.command.getStructureName())

            return

        self.command.setStructureName(name)

        self._update_UI_do_updates() # Updates the message box.

        return
Beispiel #36
0
class InsertDna_PropertyManager(DnaOrCnt_PropertyManager):
    """
    The InsertDna_PropertyManager class provides a Property Manager
    for the B{Insert Dna} 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 DNA"
    pmName = title
    iconPath = "ui/actions/Command Toolbar/BuildDna/InsertDna.png"

    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 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._placementOptions.buttonGroup,
                       SIGNAL("buttonClicked(int)"),
                       self.activateSpecifyReferencePlaneTool)

        change_connect(self.conformationComboBox,
                       SIGNAL("currentIndexChanged(int)"),
                       self.conformationComboBoxChanged)

        change_connect(self.numberOfBasePairsSpinBox,
                       SIGNAL("valueChanged(int)"), self.numberOfBasesChanged)

        change_connect(self.basesPerTurnDoubleSpinBox,
                       SIGNAL("valueChanged(double)"),
                       self.basesPerTurnChanged)

        change_connect(self.duplexRiseDoubleSpinBox,
                       SIGNAL("valueChanged(double)"), self.duplexRiseChanged)

        change_connect(self.showCursorTextCheckBox,
                       SIGNAL('stateChanged(int)'),
                       self._update_state_of_cursorTextGroupBox)

        self.duplexRiseDoubleSpinBox.connectWithState(
            Preferences_StateRef_double(bdnaRise_prefs_key,
                                        env.prefs[bdnaRise_prefs_key]))

        self.basesPerTurnDoubleSpinBox.connectWithState(
            Preferences_StateRef_double(bdnaBasesPerTurn_prefs_key,
                                        env.prefs[bdnaBasesPerTurn_prefs_key]))

    def show(self):
        _superclass.show(self)
        self.updateMessage("Specify the DNA parameters below, then click "\
                           "two endpoints in the graphics area to insert a DNA duplex.")

    def _addGroupBoxes(self):
        """
        Add the DNA Property Manager group boxes.
        """
        self._pmReferencePlaneGroupBox = PM_GroupBox(self,
                                                     title="Placement Options")
        self._loadReferencePlaneGroupBox(self._pmReferencePlaneGroupBox)

        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)

    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 3.
        """
        #Folllowing toolbutton facilitates entering a temporary DnaLineMode
        #to create a DNA using endpoints of the specified line.
        self.specifyDnaLineButton = PM_ToolButton(
            pmGroupBox,
            text="Specify Endpoints",
            iconPath="ui/actions/Properties Manager/Pencil.png",
            spanWidth=True)
        self.specifyDnaLineButton.setCheckable(True)
        self.specifyDnaLineButton.setAutoRaise(True)
        self.specifyDnaLineButton.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 4.
        """

        self.conformationComboBox  = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Conformation:",
                         choices       =  ["B-DNA"],
                         setAsDefault  =  True)

        dnaModelChoices = ['PAM3', 'PAM5']
        self.dnaModelComboBox = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Model:",
                         choices       =  dnaModelChoices,
                         setAsDefault  =  True)


        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  env.prefs[bdnaBasesPerTurn_prefs_key],
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )


        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  env.prefs[bdnaRise_prefs_key],
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Base pairs:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  0,
                        maximum       =  10000 )

        self.numberOfBasePairsSpinBox.setDisabled(True)

        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)

    def _loadDisplayOptionsGroupBox(self, pmGroupBox):
        """
        Load widgets in the Display Options GroupBox
        @see: DnaOrCnt_PropertyManager. _loadDisplayOptionsGroupBox
        """
        #Call the superclass method that loads the cursor text checkboxes.
        #Note, as of 2008-05-19, the superclass, DnaOrCnt_PropertyManager
        #only loads the cursor text groupboxes. Subclasses like this can
        #call custom methods like self._loadCursorTextCheckBoxes etc if they
        #don't need all groupboxes that the superclass loads.
        _superclass._loadDisplayOptionsGroupBox(self, pmGroupBox)

        self._rubberbandLineGroupBox = PM_GroupBox(pmGroupBox,
                                                   title='Rubber band line:')

        dnaLineChoices = ['Ribbons', 'Ladder']
        self.dnaRubberBandLineDisplayComboBox = \
            PM_ComboBox( self._rubberbandLineGroupBox ,
                         label         =  " Display as:",
                         choices       =  dnaLineChoices,
                         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,
            dnaDuplexEditCommand_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)
            ("Number of base pairs",
             dnaDuplexEditCommand_cursorTextCheckBox_numberOfBasePairs_prefs_key),

            ("Number of turns",
             dnaDuplexEditCommand_cursorTextCheckBox_numberOfTurns_prefs_key),

            ("Duplex length",
             dnaDuplexEditCommand_cursorTextCheckBox_length_prefs_key),

            ("Angle",
             dnaDuplexEditCommand_cursorTextCheckBox_angle_prefs_key) ]

        return params

    def _addToolTipText(self):
        """
        Tool Tip text for widgets in the DNA Property Manager.
        """
        pass

    def conformationComboBoxChanged(self, inIndex):
        """
        Slot for the Conformation combobox. It is called whenever the
        Conformation choice is changed.

        @param inIndex: The new index.
        @type  inIndex: int
        """
        conformation = self.conformationComboBox.currentText()

        if conformation == "B-DNA":
            self.basesPerTurnDoubleSpinBox.setValue("10.0")

        elif conformation == "Z-DNA":
            self.basesPerTurnDoubleSpinBox.setValue("12.0")

        else:
            msg = redmsg("conformationComboBoxChanged(): \
                         Error - unknown DNA conformation. Index = " + inIndex)
            env.history.message(msg)

        self.duplexLengthSpinBox.setSingleStep(getDuplexRise(conformation))

    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 basesPerTurnChanged(self, basesPerTurn):
        """
        Slot for the B{Bases per turn} spinbox.
        """
        self.command.basesPerTurn = basesPerTurn
        self._basesPerTurn = basesPerTurn
        return

    def duplexRiseChanged(self, rise):
        """
        Slot for the B{Rise} spinbox.
        """
        self.command.duplexRise = rise
        self._duplexRise = rise
        return

    def getParameters(self):
        """
        Return the parameters from this property manager
        to be used to create the DNA duplex.
        @return: A tuple containing the parameters
        @rtype: tuple
        @see: L{InsertDna_EditCommand._gatherParameters} where this is used
        """
        numberOfBases = self.numberOfBasePairsSpinBox.value()
        dnaForm = str(self.conformationComboBox.currentText())
        basesPerTurn = self.basesPerTurnDoubleSpinBox.value()
        duplexRise = self.duplexRiseDoubleSpinBox.value()

        dnaModel = str(self.dnaModelComboBox.currentText())

        # First endpoint (origin) of DNA duplex
        x1 = self.x1SpinBox.value()
        y1 = self.y1SpinBox.value()
        z1 = self.z1SpinBox.value()

        # Second endpoint (direction vector/axis) of DNA duplex.
        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)

        return (numberOfBases, dnaForm, dnaModel, basesPerTurn, duplexRise,
                self.endPoint1, self.endPoint2)

    def _addWhatsThisText(self):
        """
        What's This text for widgets in this Property Manager.
        """
        whatsThis_InsertDna_PropertyManager(self)
class Ui_ProteinSequenceEditor(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in DNA edit mode.
    It is a DockWidget that is doced at the bottom of the MainWindow
    """
    _title         =  "Sequence Editor"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """
        
        self.win = win
        # Should parentWidget for a docwidget always be win? 
        #Not necessary but most likely it will be the case.        
        parentWidget = win 
        
        _superclass.__init__(self, parentWidget, title = self._title)
        
        #A flag used to restore the state of the Reports dock widget 
        #(which can be accessed through View  >  Reports) see self.show() and
        #self.closeEvent() for more details. 
        self._reportsDockWidget_closed_in_show_method = False
        self.setFixedHeight(90)

    def show(self):
        """
        Shows the sequence editor. While doing this, it also closes the reports
        dock widget (if visible) the state of the reports dockwidget will be
        restored when the sequence editor is closed. 
        @see:self.closeEvent()
        """
        self._reportsDockWidget_closed_in_show_method = False
        
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self.win.reportsDockWidget.isVisible():
                self.win.reportsDockWidget.close()
                self._reportsDockWidget_closed_in_show_method = True

        _superclass.show(self)  
        
    def closeEvent(self, event):
        """
        Overrides close event. Makes sure that the visible state of the reports
        widgetis restored when the sequence editor is closed. 
        @see: self.show()
        """
        _superclass.closeEvent(self, event)
       
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self._reportsDockWidget_closed_in_show_method:
                self.win.viewReportsAction.setChecked(True) 
                self._reportsDockWidget_closed_in_show_method = False

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTextEditWidget()


    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")  

        self.saveSequenceButton = PM_ToolButton(
            self, 
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png") 

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        
        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        

        #Widgets to include in the widget row. 
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8), 
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10), 
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )
        
        

    def _loadTextEditWidget(self):
        """
        Load the SequenceTexteditWidgets.         
        """        
        self.aaRulerTextEdit = \
            PM_TextEdit( self, 
                         label = "", 
                         spanWidth = False,
                         permit_enter_keystroke = False) 
        
        palette = getPalette(None, 
                             QPalette.Base, 
                             pmGrpBoxColor)
        self.aaRulerTextEdit.setPalette(palette)     
        self.aaRulerTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.aaRulerTextEdit.setFixedHeight(20)
        self.aaRulerTextEdit.setReadOnly(True)
        
        self.sequenceTextEdit = \
            PM_TextEdit( self, 
                         label = " Sequence: ", 
                         spanWidth = False,
                         permit_enter_keystroke = False) 
        
        
        self.sequenceTextEdit.setCursorWidth(2)
        self.sequenceTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.sequenceTextEdit.setFixedHeight(20)
        
        self.secStrucTextEdit = \
            PM_TextEdit( self, 
                         label = " Secondary structure: ", 
                         spanWidth = False,
                         permit_enter_keystroke = False) 
        
        palette = getPalette(None, 
                             QPalette.Base, 
                             sequenceEditStrandMateBaseColor)
        self.secStrucTextEdit.setPalette(palette)     
        self.secStrucTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.secStrucTextEdit.setFixedHeight(20)
        self.secStrucTextEdit.setReadOnly(True)

        #Important to make sure that the horizontal and vertical scrollbars 
        #for these text edits are never displayed. 
        
        self.sequenceTextEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.sequenceTextEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.secStrucTextEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.secStrucTextEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.aaRulerTextEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.aaRulerTextEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def _getFindLineEditStyleSheet(self):
        """
        Return the style sheet for the findLineEdit. This sets the following 
        properties only:
         - background-color

        This style is set whenever the searchStrig can't be found (sets
        a light red color background to the lineedit when this happens)   

        @return: The line edit style sheet.
        @rtype:  str

        """
        styleSheet = \
                   "QLineEdit {\
                   background-color: rgb(255, 102, 102)\
                   }"
        #Not used:
        #  background-color: rgb(217, 255, 216)\       

        return styleSheet

    def _setFindOptionsToolButtonMenu(self):
        """
        Sets the menu for the findOptionstoolbutton that appears a small 
        menu button next to the findLineEdit.
        """
        self.findOptionsMenu = QMenu(self.findOptionsToolButton)

        self.caseSensitiveFindAction = QAction(self.findOptionsToolButton)
        self.caseSensitiveFindAction.setText('Match Case')
        self.caseSensitiveFindAction.setCheckable(True)
        self.caseSensitiveFindAction.setChecked(False)

        self.findOptionsMenu.addAction(self.caseSensitiveFindAction)
        self.findOptionsMenu.addSeparator()

        self.findOptionsToolButton.setMenu(self.findOptionsMenu)

    def _addToolTipText(self):
        """
            What's Tool Tip text for widgets in this Property Manager.  
            """ 
        pass

    def _addWhatsThisText(self):
        """
            What's This text for widgets in this Property Manager.  

            """
        pass
class DnaDuplexPropertyManager( DnaOrCnt_PropertyManager ):
    """
    The DnaDuplexPropertyManager class provides a Property Manager
    for the B{Build > DNA > Duplex} 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 DNA"
    pmName        =  title
    iconPath      =  "ui/actions/Tools/Build Structures/InsertDsDna.png"

    def __init__( self, win, editCommand ):
        """
        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,
                              win,
                              editCommand)

        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._placementOptions.buttonGroup,
                       SIGNAL("buttonClicked(int)"),
                       self.activateSpecifyReferencePlaneTool)

        change_connect( self.conformationComboBox,
                        SIGNAL("currentIndexChanged(int)"),
                        self.conformationComboBoxChanged )

        change_connect( self.numberOfBasePairsSpinBox,
                        SIGNAL("valueChanged(int)"),
                        self.numberOfBasesChanged )

        change_connect( self.basesPerTurnDoubleSpinBox,
                        SIGNAL("valueChanged(double)"),
                        self.basesPerTurnChanged )

        change_connect( self.duplexRiseDoubleSpinBox,
                        SIGNAL("valueChanged(double)"),
                        self.duplexRiseChanged )

        change_connect(self.showCursorTextCheckBox,
                       SIGNAL('stateChanged(int)'),
                       self._update_state_of_cursorTextGroupBox)
        
        self.duplexRiseDoubleSpinBox.connectWithState(
            Preferences_StateRef_double( bdnaRise_prefs_key, 
                                         env.prefs[bdnaRise_prefs_key] )
            )
        
        self.basesPerTurnDoubleSpinBox.connectWithState(
            Preferences_StateRef_double( bdnaBasesPerTurn_prefs_key, 
                                         env.prefs[bdnaBasesPerTurn_prefs_key] )
            )

        
                
 
    def ok_btn_clicked(self):
        """
        Slot for the OK button
        """
        if self.editCommand:
            self.editCommand.preview_or_finalize_structure(previewing = False)
            ##env.history.message(self.editCommand.logMessage)
        self.win.toolsDone()

    def cancel_btn_clicked(self):
        """
        Slot for the Cancel button.
        """
        if self.editCommand:
            self.editCommand.cancelStructure()
        self.win.toolsCancel()


    def _update_widgets_in_PM_before_show(self):
        """
        Update various widgets  in this Property manager.
        Overrides superclass method

        @see: MotorPropertyManager._update_widgets_in_PM_before_show
        @see: self.show where it is called.
        """
        pass

    def getFlyoutActionList(self):
        """ returns custom actionlist that will be used in a specific mode
        or editing a feature etc Example: while in movie mode,
        the _createFlyoutToolBar method calls
        this """


        #'allActionsList' returns all actions in the flyout toolbar
        #including the subcontrolArea actions
        allActionsList = []

        #Action List for  subcontrol Area buttons.
        #In this mode there is really no subcontrol area.
        #We will treat subcontrol area same as 'command area'
        #(subcontrol area buttons will have an empty list as their command area
        #list). We will set  the Comamnd Area palette background color to the
        #subcontrol area.

        subControlAreaActionList =[]

        self.exitEditCommandAction.setChecked(True)
        subControlAreaActionList.append(self.exitEditCommandAction)

        separator = QAction(self.w)
        separator.setSeparator(True)
        subControlAreaActionList.append(separator)


        allActionsList.extend(subControlAreaActionList)

        #Empty actionlist for the 'Command Area'
        commandActionLists = []

        #Append empty 'lists' in 'commandActionLists equal to the
        #number of actions in subControlArea
        for i in range(len(subControlAreaActionList)):
            lst = []
            commandActionLists.append(lst)

        params = (subControlAreaActionList, commandActionLists, allActionsList)

        return params

    def _addGroupBoxes( self ):
        """
        Add the DNA Property Manager group boxes.
        """
        self._pmReferencePlaneGroupBox = PM_GroupBox( self,
                                                      title = "Placement Options" )
        self._loadReferencePlaneGroupBox( self._pmReferencePlaneGroupBox )

        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 )


    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 3.
        """
        #Folllowing toolbutton facilitates entering a temporary DnaLineMode
        #to create a DNA using endpoints of the specified line.
        self.specifyDnaLineButton = PM_ToolButton(
            pmGroupBox,
            text = "Specify Endpoints",
            iconPath  = "ui/actions/Properties Manager/Pencil.png",
            spanWidth = True
        )
        self.specifyDnaLineButton.setCheckable(True)
        self.specifyDnaLineButton.setAutoRaise(True)
        self.specifyDnaLineButton.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 4.
        """

        self.conformationComboBox  = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Conformation:",
                         choices       =  ["B-DNA"],
                         setAsDefault  =  True)

        dnaModelChoices = ['PAM3', 'PAM5']
        self.dnaModelComboBox = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Model:",
                         choices       =  dnaModelChoices,
                         setAsDefault  =  True)


        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  env.prefs[bdnaBasesPerTurn_prefs_key],
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )
        
        
        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  env.prefs[bdnaRise_prefs_key],
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )
        
        
        

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label         =  "Base pairs:",
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  0,
                        maximum       =  10000 )

        self.numberOfBasePairsSpinBox.setDisabled(True)

        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)


    def _loadDisplayOptionsGroupBox(self, pmGroupBox):
        """
        Load widgets in the Display Options GroupBox
        @see: DnaOrCnt_PropertyManager. _loadDisplayOptionsGroupBox
        """
        #Call the superclass method that loads the cursor text checkboxes.
        #Note, as of 2008-05-19, the superclass, DnaOrCnt_PropertyManager
        #only loads the cursor text groupboxes. Subclasses like this can
        #call custom methods like self._loadCursorTextCheckBoxes etc if they
        #don't need all groupboxes that the superclass loads.
        _superclass._loadDisplayOptionsGroupBox(self, pmGroupBox)

        self._rubberbandLineGroupBox = PM_GroupBox(
            pmGroupBox,
            title = 'Rubber band line:')

        dnaLineChoices = ['Ribbons', 'Ladder']
        self.dnaRubberBandLineDisplayComboBox = \
            PM_ComboBox( self._rubberbandLineGroupBox ,
                         label         =  " Display as:",
                         choices       =  dnaLineChoices,
                         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 ,
            dnaDuplexEditCommand_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)
            ("Number of base pairs",
             dnaDuplexEditCommand_cursorTextCheckBox_numberOfBasePairs_prefs_key),

            ("Number of turns",
             dnaDuplexEditCommand_cursorTextCheckBox_numberOfTurns_prefs_key),

            ("Duplex length",
             dnaDuplexEditCommand_cursorTextCheckBox_length_prefs_key),

            ("Angle",
             dnaDuplexEditCommand_cursorTextCheckBox_angle_prefs_key) ]

        return params


    def _addToolTipText(self):
        """
        Tool Tip text for widgets in the DNA Property Manager.
        """
        pass


    def conformationComboBoxChanged( self, inIndex ):
        """
        Slot for the Conformation combobox. It is called whenever the
        Conformation choice is changed.

        @param inIndex: The new index.
        @type  inIndex: int
        """
        conformation  =  self.conformationComboBox.currentText()

        if conformation == "B-DNA":
            self.basesPerTurnDoubleSpinBox.setValue("10.0")

        elif conformation == "Z-DNA":
            self.basesPerTurnDoubleSpinBox.setValue("12.0")

        else:
            msg = redmsg("conformationComboBoxChanged(): \
                         Error - unknown DNA conformation. Index = "+ inIndex)
            env.history.message(msg)

        self.duplexLengthSpinBox.setSingleStep(getDuplexRise(conformation))

    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 basesPerTurnChanged( self, basesPerTurn ):
        """
        Slot for the B{Bases per turn} spinbox.
        """
        self.editCommand.basesPerTurn = basesPerTurn
        self._basesPerTurn = basesPerTurn
        return

    def duplexRiseChanged( self, rise ):
        """
        Slot for the B{Rise} spinbox.
        """
        self.editCommand.duplexRise = rise
        self._duplexRise = rise
        return

    def getParameters(self):
        """
        Return the parameters from this property manager
        to be used to create the DNA duplex.
        @return: A tuple containing the parameters
        @rtype: tuple
        @see: L{DnaDuplex_EditCommand._gatherParameters} where this is used
        """
        numberOfBases = self.numberOfBasePairsSpinBox.value()
        dnaForm  = str(self.conformationComboBox.currentText())
        basesPerTurn = self.basesPerTurnDoubleSpinBox.value()
        duplexRise = self.duplexRiseDoubleSpinBox.value()

        dnaModel = str(self.dnaModelComboBox.currentText())

        # First endpoint (origin) of DNA duplex
        x1 = self.x1SpinBox.value()
        y1 = self.y1SpinBox.value()
        z1 = self.z1SpinBox.value()

        # Second endpoint (direction vector/axis) of DNA duplex.
        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)

        return (numberOfBases,
                dnaForm,
                dnaModel,
                basesPerTurn,
                duplexRise,
                self.endPoint1,
                self.endPoint2)

    def _addWhatsThisText(self):
        """
        What's This text for widgets in this Property Manager.
        """
        whatsThis_DnaDuplexPropertyManager(self)
Beispiel #39
0
class OrderDna_PropertyManager( PM_Dialog, DebugMenuMixin ):
    """
    The OrderDna_PropertyManager class provides a Property Manager 
    for the B{Order Dna} command on the flyout toolbar in the 
    Build > Dna mode. 

    @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         =  "Order Dna"
    pmName        =  title
    iconPath      =  "ui/actions/Command Toolbar/Order_DNA.png"
    
    def __init__( self, parentCommand ):
        """
        Constructor for the property manager.
        """

        self.parentMode = parentCommand
        self.w = self.parentMode.w
        self.win = self.parentMode.w
        self.pw = self.parentMode.pw        
        self.o = self.win.glpane
        self.assy = self.win.assy
                    
        PM_Dialog.__init__(self, self.pmName, self.iconPath, self.title)
        
        DebugMenuMixin._init1( self )

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_WHATS_THIS_BUTTON)
        
        self.update_includeStrands() # Updates the message box.
        """
        if self.getNumberOfBases():
            msg = "Click on <b>View DNA Order File...</b> to preview a "\
                "DNA order for all DNA strands in the current model."
        else:
            msg = "<font color=red>There is no DNA in the current model."
        self.updateMessage(msg)
        """
        
    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.viewDnaOrderFileButton,
                        SIGNAL("clicked()"), 
                        self.viewDnaOrderFile)
        
        change_connect( self.includeStrandsComboBox,
                      SIGNAL("activated(int)"),
                      self.update_includeStrands )
        
    def ok_btn_clicked(self):
        """
        Slot for the OK button
        """      
        self.win.toolsDone()
        
    def show(self):
        """
        Shows the Property Manager. Overrides PM_Dialog.show.
        """
        PM_Dialog.show(self)
        self.connect_or_disconnect_signals(isConnect = True)

    def close(self):
        """
        Closes the Property Manager. Overrides PM_Dialog.close.
        """
        self.connect_or_disconnect_signals(False)
        PM_Dialog.close(self)
    
    def _addGroupBoxes( self ):
        """
        Add the Property Manager group boxes.
        """        
        self._pmGroupBox1 = PM_GroupBox( self, title = "Options" )
        self._loadGroupBox1( self._pmGroupBox1 )
    
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """
        
        includeStrandsChoices = ["All strands in model",
                                 "Selected strands only"]
        
        self.includeStrandsComboBox  = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Include strands:", 
                         choices       =  includeStrandsChoices,
                         setAsDefault  =  True)
        
        self.numberOfBasesLineEdit  = \
            PM_LineEdit( pmGroupBox,
                         label  =  "Number of bases:",
                         text   = str(self.getNumberOfBases()))
        self.numberOfBasesLineEdit.setEnabled(False)
        
        self.viewDnaOrderFileButton = \
            PM_PushButton( pmGroupBox,
                           label     = "",
                           text      = "View DNA Order File...",
                           spanWidth = True)
    
    def _addWhatsThisText( self ):
        """
        What's This text for widgets in the DNA Property Manager.  
        """
        pass
                
    def _addToolTipText(self):
        """
        Tool Tip text for widgets in the DNA Property Manager.  
        """
        pass
    
    # Ask Bruce where this should live (i.e. class Part?) --Mark
    def getAllDnaStrands(self, selectedOnly = False):
        """
        Returns a list of all the DNA strands in the current part, or only
        the selected strands if I{selectedOnly} is True.
        
        @param selectedOnly: If True, return only the selected DNA strands.
        @type  selectedOnly: bool
        """
        
        dnaStrandList = []
         
        def func(node):
            if isinstance(node, DnaStrand):
                if selectedOnly:
                    if node.picked:
                        dnaStrandList.append(node)
                else:
                    dnaStrandList.append(node)
                    
        self.win.assy.part.topnode.apply2all(func)
        
        return dnaStrandList
    
    def getNumberOfBases(self, selectedOnly = False):
        """
        Returns the number of bases count for all the DNA strands in the 
        current part, or only the selected strand if I{selectedOnly} is True.
        
        @param selectedOnly: If True, return only the selected DNA strands.
        @type  selectedOnly: bool
        """
        dnaSequenceString = ''
        selectedOnly = self.includeStrandsComboBox.currentIndex()
        strandList = self.getAllDnaStrands(selectedOnly)
        
        for strand in strandList:
            strandSequenceString = str(strand.getStrandSequence())
            dnaSequenceString += strandSequenceString
            
        return len(dnaSequenceString)
        
    def getDnaSequence(self, format = 'CSV'):
        """
        Return the complete Dna sequence information string (i.e. all strand 
        sequences) in the specified format. 
        
        @return: The Dna sequence string
        @rtype: string
        
        """
        if format == 'CSV': #comma separated values.
            separator = ','
            
        dnaSequenceString = ''
        selectedOnly = self.includeStrandsComboBox.currentIndex()
        strandList = self.getAllDnaStrands(selectedOnly)
        
        for strand in strandList:
            dnaSequenceString = dnaSequenceString + strand.name + separator
            strandSequenceString = str(strand.getStrandSequence())
            if strandSequenceString: 
                strandSequenceString = strandSequenceString.upper()
                dnaSequenceString = dnaSequenceString + strandSequenceString
                
            dnaSequenceString = dnaSequenceString + "\n"
            
        return dnaSequenceString
        
    def viewDnaOrderFile(self, openFileInEditor = True):
        """
        Opens a text editor and loads a temporary text file containing all the 
        DNA strand names and their sequences in the current DNA object. It will
        look something like this: 

        Strand1,ATCAGCTACGCATCGCT
        Strand2,TAGTCGATGCGTAGCGA
        ...
        Strandn, ...

        The user can then save the file to a permanent location using the 
        text editor the file is loaded (and displayed) in.

        @see: Ui_DnaFlyout.orderDnaCommand
        @see: writeDnaOrderFile()
        @TODO: assy.getAllDnaObjects(). 
        """
        dnaSequence = self.getDnaSequence(format = 'CSV')

        if dnaSequence: 
            tmpdir = find_or_make_Nanorex_subdir('temp')
            fileBaseName = 'DnaOrder'
            temporaryFile = os.path.join(tmpdir, "%s.csv" % fileBaseName)            
            writeDnaOrderFile(temporaryFile, 
                              self.assy,
                              dnaSequence)      

            if openFileInEditor:
                open_file_in_editor(temporaryFile)

    def update_includeStrands(self, ignoreVal = 0):
        """
        Slot method for "Include (strands)" combobox.
        """
        
        idx = self.includeStrandsComboBox.currentIndex()
        
        includeType = ["model", "selection"]
        
        _numberOfBases = self.getNumberOfBases()
        self.numberOfBasesLineEdit.setText(str(_numberOfBases))
        
        if _numberOfBases > 0:
            self.viewDnaOrderFileButton.setEnabled(True)
            msg = "Click on <b>View DNA Order File...</b> to preview a " \
                "DNA order for all DNA strands in the current %s." \
                % includeType[idx]
        else:
            self.viewDnaOrderFileButton.setEnabled(False)
            msg = "<font color=red>" \
                "There are no DNA strands in the current %s." \
                % includeType[idx]
                
        self.updateMessage(msg)
            
class EditNanotube_PropertyManager( DnaOrCnt_PropertyManager ):
    """
    The NanotubeSegmenta_PropertyManager class provides a Property Manager
    for the EditNanotube_EditCommand.

    @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         =  "Nanotube Properties"
    pmName        =  title
    iconPath      =  "ui/actions/Command Toolbar/BuildNanotube/EditNanotube.png"

    def __init__( self,  command ):
        """
        Constructor for the Cnt Segment Properties property manager.
        """

        #For model changed signal
        self.previousSelectionParams = None

        #see self.connect_or_disconnect_signals for comment about this flag
        self.isAlreadyConnected = False
        self.isAlreadyDisconnected = False

        # Initialized here. Their values will be set in
        # _update_widgets_in_PM_before_show()
        self.endPoint1 = V(0, 0, 0)
        self.endPoint2 = V(0, 0, 0)

        _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.nameLineEdit,
                       SIGNAL("editingFinished()"),
                       self._nameChanged)

        change_connect(self.showCursorTextCheckBox,
                       SIGNAL('stateChanged(int)'),
                       self._update_state_of_cursorTextGroupBox)
        return

    def show(self):
        """
        Show this property manager. Overrides EditCommand_PM.show()
        This method also retrives the name information from the
        command's structure for its name line edit field.
        @see: EditNanotube_EditCommand.getStructureName()
        @see: self.close()
        """
        _superclass.show(self)
        #if self.command is not None:
            #name = self.command.getStructureName()
            #if name is not None:
                #self.nameLineEdit.setText(name)

    def close(self):
        """
        Close this property manager.
        Also sets the name of the self.command's structure to the one
        displayed in the line edit field.
        @see self.show()
        @see: EditNanotube_EditCommand.setStructureName
        """
        if self.command is not None:
            name = str(self.nameLineEdit.text())
            self.command.setStructureName(name)
        _superclass.close(self)

    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 ,
            editNanotubeEditCommand_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",
                   editNanotubeEditCommand_cursorTextCheckBox_length_prefs_key)
                 ]

        return params

    def setParameters(self, params):
        """
        This is called when entering "Nanotube Segment Properties
        (i.e. "Edit properties...") to retrieve and set parameters of the
        nanotube segment that might be modified during this command and
        are needed to regenerate the nanotube segment.

        @param params: The parameters of the nanotube segment.
                       These parameters are retreived via
                       L{NanotubeSegment.getProps()}, called from
                       L{EditNanotube_EditCommand.editStructure()}.

                       Parameters:
                       - n, m (chirality)
                       - type (i.e. carbon or boron nitride)
                       - endings (none, hydrogen, nitrogen)
                       - endpoints (endPoint1, endPoint2)
        @type params: list (n, m), type, endings, (endPoint1, endPoint2)

        @note: Any widgets in the property manager that display these
        parameters should be updated here.

        @see: L{NanotubeSegment.getProps()}

        TODO:
        - Make this a EditCommand_PM API method?
        - See also the routines GraphicsMode.setParams or object.setProps
        ..better to name them all in one style?
        """
        (self.n, self.m), self.type, self.endings,\
            (self.endPoint1, self.endPoint2) = params

        # This is needed to update the endpoints since the Nanotube segment
        # may have been moved (i.e. translated or rotated). In that case,
        # the endpoints are not updated, so we recompute them here.
        nanotubeChunk = self.command.struct.members[0]
        self.endPoint1, self.endPoint2, radius = \
            self.command.struct.nanotube.computeEndPointsFromChunk(nanotubeChunk)

        if 0:
            print "\n--------------"
            print "setParameters():"
            print "Struct=", self.command.struct
            print "N, M:", self.n, self.m
            print "type:", self.type
            print "endings:", self.endings
            print "pt1, pt2:", self.endPoint1, self.endPoint2

    def getParameters(self):
        """
        Get the parameters that the edit command will use to determine if
        any have changed. If any have, then the nanotube will be modified.
        """
        if 0:
            print "\n--------------"
            print "getParameters():"
            print "Struct=", self.command.struct
            print "N, M:", self.n, self.m
            print "type:", self.type
            print "endings:", self.endings
            print "pt1, pt2:", self.endPoint1, self.endPoint2

        return (self.n, self.m,
                self.type,
                self.endings,
                self.endPoint1, self.endPoint2)

    def _update_widgets_in_PM_before_show(self):
        """
        This is called only when user is editing an existing structure.
        Its different than self.update_widgets_in_pm_before_show. (that method
        is called just before showing the property manager)
        @see: EditNanotube_EditCommand.editStructure()

        """
        if self.command and self.command.hasValidStructure():

            self.nanotube = self.command.struct.nanotube

            self.n, self.m = self.nanotube.getChirality()
            self.type = self.nanotube.getType()
            self.endings = self.nanotube.getEndings()
            self.endPoint1, self.endPoint2 = self.nanotube.getEndPoints()
            pass

        # Note that _update_widgets_in_PM_before_show() is called in
        # self.show, before you connect the signals. So, for the
        # 'first show' we will need to manually set the value of any
        # widgets that need updated. But later, when a different
        # NanotubeSegment is clicked, (while still in
        # EditNanotube_EditCommand, the propMgr will already be connected
        # so any calls in that case is redundant.
        self.updateNameField()
        self.updateLength()
        self.updateNanotubeDiameter()
        self.updateChirality()

        return

    def _update_UI_do_updates(self):
        """
        Overrides superclass method.

        @see: Command_PropertyManager._update_UI_do_updates()
        """
        self._update_widgets_in_PM_before_show()

        if self.command.struct:
            msg = "Editing structure <b>%s</b>." % \
                self.command.getStructureName()
        else:
            msg = "Select a nanotube to edit."
        self.updateMessage(msg)
        return

    def _addGroupBoxes( self ):
        """
        Add the Property Manager group boxes.
        """

        self._pmGroupBox1 = PM_GroupBox( self, title = "Parameters" )
        self._loadGroupBox1( self._pmGroupBox1 )

        self._displayOptionsGroupBox = PM_GroupBox( self,
                                                    title = "Display Options" )
        self._loadDisplayOptionsGroupBox( self._displayOptionsGroupBox )

    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                         label         =  "Name:",
                         text          =  "",
                         setAsDefault  =  False)

        # Nanotube Length
        self.ntLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.ntLengthLineEdit.setDisabled(True)

        # Nanotube Radius
        self.ntDiameterLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Nanotube Diameter: ",
                         setAsDefault  =  False)

        self.ntDiameterLineEdit.setDisabled(True)

        # Nanotube chirality. These are disabled (read-only) for now. --Mark
        self.chiralityNSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label        = "Chirality (n) :",
                        minimum      =  2,
                        maximum      =  100,
                        setAsDefault = True )
        self.chiralityNSpinBox.setDisabled(True)

        self.chiralityMSpinBox = \
            PM_SpinBox( pmGroupBox,
                        label        = "Chirality (m) :",
                        minimum      =  0,
                        maximum      =  100,
                        setAsDefault = True )
        self.chiralityMSpinBox.setDisabled(True)

    def _addWhatsThisText(self):
        """
        Add what's this text.
        """
        pass

    def _addToolTipText(self):
        """
        Add Tooltip text
        """
        pass

    def _nameChanged(self):
        """
        Slot for "Name" field.

        @TODO: Include a validator for the name field.
        """

        _name = str(self.nameLineEdit.text())

        if not _name: # Minimal test. Need to implement a validator.
            self.updateNameField()
            return

        self.command.setStructureName(_name)
        msg = "Editing structure <b>%s</b>." % _name
        self.updateMessage(msg)

        return

    def updateNameField(self):
        """
        Update the name field showing the name of the currently selected protein.
        clear the combobox list.
        """

        if self.command.hasValidStructure():
            self.nameLineEdit.setEnabled(True)
            self.nameLineEdit.setText(self.command.getStructureName())
        else:
            self.nameLineEdit.setDisabled(True)
            self.nameLineEdit.setText("")
        return

    def updateLength( self ):
        """
        Update the nanotube Length lineEdit widget.
        """
        if self.command.hasValidStructure():
            _nanotubeLength = vlen(self.endPoint1 - self.endPoint2)
            _lengthText = "%-7.4f Angstroms" % (_nanotubeLength)
        else:
            _lengthText = ""
        self.ntLengthLineEdit.setText(_lengthText)
        return

    def updateNanotubeDiameter(self):
        """
        Update the nanotube Diameter lineEdit widget.
        """
        if self.command.hasValidStructure():
            _diameterText = "%-7.4f Angstroms" %  (self.nanotube.getDiameter())
        else:
            _diameterText = ""
        self.ntDiameterLineEdit.setText(_diameterText)
        return

    def updateChirality( self ):
        """
        Update the nanotube chirality spinboxes (read-only).
        """
        if self.command.hasValidStructure():
            n, m = self.nanotube.getChirality()
        else:
            n = 0
            m = 0
        self.chiralityNSpinBox.setValue(n)
        self.chiralityMSpinBox.setValue(m)
        return

    pass # End of EditNanotube_PropertyManager class
class Ui_BuildAtomsPropertyManager(Command_PropertyManager):
    """
    The Ui_BuildAtomsPropertyManager class defines UI elements for the Property
    Manager of the B{Build Atoms mode}.

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

    # The title that appears in the Property Manager header
    title = "Build Atoms"
    # The name of this Property Manager. This will be set to
    # the name of the PM_Dialog object via setObjectName().
    pmName = title
    # The relative path to the PNG file that appears in the header
    iconPath = "ui/actions/Tools/Build Structures/BuildAtoms.png"

    def __init__(self, command):
        """
        Constructor for the B{Build Atoms} property manager class that defines
        its UI.

        @param command: The parent mode where this Property Manager is used
        @type  command: L{depositMode}
        """

        self.previewGroupBox = None
        self.regularElementChooser = None
        self.PAM5Chooser = None
        self.PAM3Chooser = None
        self.elementChooser = None
        self.advancedOptionsGroupBox = None
        self.bondToolsGroupBox = None

        self.selectionFilterCheckBox = None
        self.filterlistLE = None
        self.selectedAtomInfoLabel = None

        #Initialize the following to None. (subclasses may not define this)
        #Make sure you initialize it before adding groupboxes!
        self.selectedAtomPosGroupBox = None
        self.showSelectedAtomInfoCheckBox = None

        _superclass.__init__(self, command)

        self.showTopRowButtons(PM_DONE_BUTTON | PM_WHATS_THIS_BUTTON)
        msg = ''
        self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault=False)

    def _addGroupBoxes(self):
        """
        Add various group boxes to the Build Atoms Property manager.
        """
        self._addPreviewGroupBox()
        self._addAtomChooserGroupBox()
        self._addBondToolsGroupBox()

        #@@@TODO HIDE the bonds tool groupbox initially as the
        #by default, the atoms tool is active when BuildAtoms command is
        #finist invoked.
        self.bondToolsGroupBox.hide()

        self._addSelectionOptionsGroupBox()
        self._addAdvancedOptionsGroupBox()

    def _addPreviewGroupBox(self):
        """
        Adde the preview groupbox that shows the element selected in the
        element chooser.
        """
        self.previewGroupBox = PM_PreviewGroupBox( self, glpane = self.o )

    def _addAtomChooserGroupBox(self):
        """
        Add the Atom Chooser groupbox. This groupbox displays one of the
        following three groupboxes depending on the choice selected in the
        combobox:
          a) Periodic Table Elements L{self.regularElementChooser}
          b) PAM5 Atoms  L{self.PAM5Chooser}
          c) PAM3 Atoms  L{self.PAM3Chooser}
        @see: L{self.__updateAtomChooserGroupBoxes}
        """
        self.atomChooserGroupBox = \
            PM_GroupBox(self, title = "Atom Chooser")
        self._loadAtomChooserGroupBox(self.atomChooserGroupBox)

        self._updateAtomChooserGroupBoxes(currentIndex = 0)

    def _addElementChooserGroupBox(self, inPmGroupBox):
        """
        Add the 'Element Chooser' groupbox. (present within the
        Atom Chooser Groupbox)
        """
        if not self.previewGroupBox:
            return

        elementViewer = self.previewGroupBox.elementViewer
        self.regularElementChooser = \
            PM_ElementChooser( inPmGroupBox,
                               parentPropMgr = self,
                               elementViewer = elementViewer)


    def _add_PAM5_AtomChooserGroupBox(self, inPmGroupBox):
        """
        Add the 'PAM5 Atom Chooser' groupbox (present within the
        Atom Chooser Groupbox)
        """
        if not self.previewGroupBox:
            return

        elementViewer = self.previewGroupBox.elementViewer
        self.PAM5Chooser = \
            PM_PAM5_AtomChooser( inPmGroupBox,
                                 parentPropMgr = self,
                                 elementViewer = elementViewer)

    def _add_PAM3_AtomChooserGroupBox(self, inPmGroupBox):
        """
        Add the 'PAM3 Atom Chooser' groupbox (present within the
        Atom Chooser Groupbox)
        """
        if not self.previewGroupBox:
            return

        elementViewer = self.previewGroupBox.elementViewer
        self.PAM3Chooser = \
            PM_PAM3_AtomChooser( inPmGroupBox,
                                 parentPropMgr = self,
                                 elementViewer = elementViewer)

    def _hideAllAtomChooserGroupBoxes(self):
        """
        Hides all Atom Chooser group boxes.
        """
        if self.regularElementChooser:
            self.regularElementChooser.hide()
        if self.PAM5Chooser:
            self.PAM5Chooser.hide()
        if self.PAM3Chooser:
            self.PAM3Chooser.hide()

    def _addBondToolsGroupBox(self):
        """
        Add the 'Bond Tools' groupbox.
        """
        self.bondToolsGroupBox = \
            PM_GroupBox( self, title = "Bond Tools")

        self._loadBondToolsGroupBox(self.bondToolsGroupBox)

    def _addSelectionOptionsGroupBox(self):
        """
        Add 'Selection Options' groupbox
        """
        self.selectionOptionsGroupBox = \
            PM_GroupBox( self, title = "Selection Options" )

        self._loadSelectionOptionsGroupBox(self.selectionOptionsGroupBox)

    def _loadAtomChooserGroupBox(self, inPmGroupBox):
        """
        Load the widgets inside the Atom Chooser groupbox.
        @param inPmGroupBox: The Atom Chooser box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        atomChooserChoices = [ "Periodic Table Elements",
                             "PAM5 Atoms",
                             "PAM3 Atoms" ]

        self.atomChooserComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = '',
                         choices      = atomChooserChoices,
                         index        = 0,
                         setAsDefault = False,
                         spanWidth    = True )

        #Following fixes bug 2550
        self.atomChooserComboBox.setFocusPolicy(Qt.NoFocus)

        self._addElementChooserGroupBox(inPmGroupBox)
        self._add_PAM5_AtomChooserGroupBox(inPmGroupBox)
        self._add_PAM3_AtomChooserGroupBox(inPmGroupBox)

    def _loadSelectionOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Selection Options group box.
        @param inPmGroupBox: The Selection Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.selectionFilterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text  = "Enable atom selection filter",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )
        self.selectionFilterCheckBox.setDefaultValue(False)

        self.filterlistLE = PM_LineEdit( inPmGroupBox,
                                         label        = "",
                                         text         = "",
                                         setAsDefault = False,
                                         spanWidth    = True )
        self.filterlistLE.setReadOnly(True)

        if self.selectionFilterCheckBox.isChecked():
            self.filterlistLE.setEnabled(True)
        else:
            self.filterlistLE.setEnabled(False)

        self.showSelectedAtomInfoCheckBox = \
            PM_CheckBox(
                inPmGroupBox,
                text  = "Show Selected Atom Info",
                widgetColumn = 0,
                state        = Qt.Unchecked)

        self.selectedAtomPosGroupBox = \
            PM_GroupBox( inPmGroupBox, title = "")
        self._loadSelectedAtomPosGroupBox(self.selectedAtomPosGroupBox)

        self.toggle_selectedAtomPosGroupBox(show = 0)
        self.enable_or_disable_selectedAtomPosGroupBox( bool_enable = False)

        self.reshapeSelectionCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Dragging reshapes selection',
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )

        connect_checkbox_with_boolean_pref( self.reshapeSelectionCheckBox,
                                            reshapeAtomsSelection_prefs_key )

        env.prefs[reshapeAtomsSelection_prefs_key] = False

        self.waterCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = "Z depth filter (water surface)",
                         widgetColumn = 0,
                         state        = Qt.Unchecked  )

    def _loadSelectedAtomPosGroupBox(self, inPmGroupBox):
        """
        Load the selected Atoms position groupbox It is a sub-gropbox of
        L{self.selectionOptionsGroupBox)
        @param inPmGroupBox: 'The Selected Atom Position Groupbox'
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.selectedAtomLineEdit = PM_LineEdit( inPmGroupBox,
                                         label        = "Selected Atom:",
                                         text         = "",
                                         setAsDefault = False,
                                         spanWidth    = False )

        self.selectedAtomLineEdit.setReadOnly(True)
        self.selectedAtomLineEdit.setEnabled(False)

        self.coordinateSpinboxes = PM_CoordinateSpinBoxes(inPmGroupBox)

        # User input to specify x-coordinate
        self.xCoordOfSelectedAtom  =  self.coordinateSpinboxes.xSpinBox
        # User input to specify y-coordinate
        self.yCoordOfSelectedAtom  =  self.coordinateSpinboxes.ySpinBox
        # User input to specify z-coordinate
        self.zCoordOfSelectedAtom  =  self.coordinateSpinboxes.zSpinBox

    def _addAdvancedOptionsGroupBox(self):
        """
        Add 'Advanced Options' groupbox
        """
        self.advancedOptionsGroupBox = \
            PM_GroupBox( self, title = "Advanced Options" )

        self._loadAdvancedOptionsGroupBox(self.advancedOptionsGroupBox)

    def _loadAdvancedOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Advanced Options group box.
        @param inPmGroupBox: The Advanced Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.autoBondCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Auto bond',
                         widgetColumn = 0,
                         state        = Qt.Checked  )

        self.highlightingCheckBox = \
            PM_CheckBox( inPmGroupBox,
                         text         = "Hover highlighting",
                         widgetColumn = 0,
                         state        = Qt.Checked )

    def _loadBondToolsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Bond Tools group box.
        @param inPmGroupBox: The Bond Tools box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        BOND_TOOL_BUTTONS = \
                          [ ( "QToolButton", 0,  "SINGLE",    "", "", None, 0),
                            ( "QToolButton", 1,  "DOUBLE",    "", "", None, 1),
                            ( "QToolButton", 2,  "TRIPLE",    "", "", None, 2),
                            ( "QToolButton", 3,  "AROMATIC",  "", "", None, 3),
                            ( "QToolButton", 4,  "GRAPHITIC", "", "", None, 4),
                            ( "QToolButton", 5,  "CUTBONDS",  "", "", None, 5)
                          ]


        self.bondToolButtonRow = \
            PM_ToolButtonRow(
                inPmGroupBox,
                title        = "",
                buttonList   = BOND_TOOL_BUTTONS,
                checkedId    = None,
                setAsDefault = True )

    def _addWhatsThisText(self):
        """
        "What's This" text for widgets in this Property Manager.
        """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_BuildAtomsPropertyManager
        whatsThis_BuildAtomsPropertyManager(self)

    def _addToolTipText(self):
        """
        Tool Tip text for widgets in this Property Manager.
        """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_BuildAtomsPropertyManager
        ToolTip_BuildAtomsPropertyManager(self)

    def toggle_selectedAtomPosGroupBox(self, show = 0):
        """
        Show or hide L{self.selectedAtomPosGroupBox} depending on the state of
        the checkbox (L{self.showSelectedAtomInfoCheckBox})
        @param show: Flag that shows or hides the groupbox (can have values
                     0 or 1
        @type  show: int
        """
        if show:
            self.selectedAtomPosGroupBox.show()
        else:
            self.selectedAtomPosGroupBox.hide()

    def enable_or_disable_selectedAtomPosGroupBox(self, bool_enable = False):
        """
        Enable or disable Selected AtomPosGroupBox present within
        'selection options' and also the checkbox that shows or hide this
        groupbox. These two widgets are enabled when only a single atom is
        selected from the 3D workspace.
        @param bool_enable: Flag that enables or disables widgets
        @type  bool_enable: boolean
        """
        if self.showSelectedAtomInfoCheckBox:
            self.showSelectedAtomInfoCheckBox.setEnabled(bool_enable)
        if self.selectedAtomPosGroupBox:
            self.selectedAtomPosGroupBox.setEnabled(bool_enable)

    def _updateAtomChooserGroupBoxes(self, currentIndex):
        """
        Updates the Atom Chooser Groupbox. It displays one of the
        following three groupboxes depending on the choice selected in the
        combobox:
          a) Periodic Table Elements L{self.regularElementChooser}
          b) PAM5 Atoms  L{self.PAM5Chooser}
          c) PAM3 Atoms  L{self.PAM3Chooser}
        It also sets self.elementChooser to the current active Atom chooser
        and updates the display accordingly in the Preview groupbox.
        """
        self._hideAllAtomChooserGroupBoxes()

        if currentIndex is 0:
            self.elementChooser = self.regularElementChooser
            self.regularElementChooser.show()
        if currentIndex is 1:
            self.elementChooser = self.PAM5Chooser
            self.PAM5Chooser.show()
        if currentIndex is 2:
            self.elementChooser = self.PAM3Chooser
            self.PAM3Chooser.show()

        if self.elementChooser:
            self.elementChooser.updateElementViewer()

        self.updateMessage()


    def updateMessage(self):
        """
        Update the Message groupbox with informative message.
        Subclasses should override this.
        """
        pass
    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 _loadFreeDragTranslateGroupBox(self, inPmGroupBox):
     """
     Load widgets in the Free Drag Translate group box, which is present 
     within the Translate groupbox.
     @param inPmGroupBox: The Free Drag Translate group box in the Translate 
                          group box. 
     @type  inPmGroupBox: L{PM_GroupBox}
     """
     # Button list to create a toolbutton row.
     # Format: 
     # - buttonId, 
     # - buttonText , 
     # - iconPath
     # - tooltip
     # - shortcut
     # - column
     
      
     BUTTON_LIST = [ 
         ( "QToolButton", 1,  "MOVEDEFAULT", 
           "ui/actions/Properties Manager/Move_Free.png", "", "F", 0),
         ( "QToolButton", 2,  "TRANSX", 
           "ui/actions/Properties Manager/TranslateX.png", "", "X", 1),
         ( "QToolButton", 3,  "TRANSY",  
           "ui/actions/Properties Manager/TranslateY.png", "", "Y", 2),
         ( "QToolButton", 4,  "TRANSZ",  
           "ui/actions/Properties Manager/TranslateZ.png", "", "Z", 3),
         ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",  
           "ui/actions/Properties Manager/translate+rotate-A.png", "", \
           "A", 4)
                     
         ]
         
     self.freeDragTranslateButtonGroup = \
         PM_ToolButtonRow( inPmGroupBox, 
                            title        = "",
                            buttonList   = BUTTON_LIST,
                            checkedId    = 1,
                            setAsDefault = True,
                            )
     self.transFreeButton =self.freeDragTranslateButtonGroup.getButtonById(1)
     self.transXButton = self.freeDragTranslateButtonGroup.getButtonById(2)
     self.transYButton = self.freeDragTranslateButtonGroup.getButtonById(3)
     self.transZButton = self.freeDragTranslateButtonGroup.getButtonById(4)
     self.transAlongAxisButton = \
         self.freeDragTranslateButtonGroup.getButtonById(5)
     
     self.moveFromToButton = PM_ToolButton(
                 inPmGroupBox, 
                 text = "Translate from/to",
                 iconPath  = "ui/actions/Properties Manager"\
                 "/Translate_Components.png",
                 spanWidth = True
                 
                 )
     self.moveFromToButton.setCheckable(True)
     self.moveFromToButton.setAutoRaise(True)
     self.moveFromToButton.setToolButtonStyle(
         Qt.ToolButtonTextBesideIcon)
     
     
     self.startCoordLineEdit = PM_LineEdit( 
         inPmGroupBox, 
         label        = "ui/actions/Properties Manager"\
                 "/Move_Start_Point.png",
         text         = "Define 'from' and 'to' points",
         setAsDefault = False,
         )
     self.startCoordLineEdit.setReadOnly(True)
     self.startCoordLineEdit.setEnabled(False)
class DnaSegment_PropertyManager( DnaOrCnt_PropertyManager):
    """
    The DnaSegmenta_PropertyManager class provides a Property Manager 
    for the DnaSegment_EditCommand. 

    @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         =  "DnaSegment Properties"
    pmName        =  title
    iconPath      =  "ui/actions/Tools/Build Structures/DNA.png"

    def __init__( self, win, editCommand ):
        """
        Constructor for the Build DNA property manager.
        """

        #For model changed signal
        #@see: self.model_changed() and self._current_model_changed_params 
        #for example use
        self._previous_model_changed_params = None


        #see self.connect_or_disconnect_signals for comment about this flag
        self.isAlreadyConnected = False
        self.isAlreadyDisconnected = False

        self.endPoint1 = V(0, 0, 0)
        self.endPoint2 = V(0, 0, 0)

        self._numberOfBases = 0 
        self._conformation = 'B-DNA'
        self.duplexRise = 3.18
        self.basesPerTurn = 10
        self.dnaModel = 'PAM3'

        _superclass.__init__( self,  win, editCommand)


        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_CANCEL_BUTTON | \
                                PM_PREVIEW_BUTTON | \
                                PM_WHATS_THIS_BUTTON)

        msg = "Use resize handles to resize the segment. Drag any axis or sugar"\
            " atom for translation or rotation about axis respectively. Dragging"\
            " any bond will freely move the whole segment."
        self.updateMessage(msg)   

    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 
            
        
        _superclass.connect_or_disconnect_signals(self, isConnect)


        change_connect( self.numberOfBasePairsSpinBox,
                        SIGNAL("valueChanged(int)"),
                        self.numberOfBasesChanged )

        change_connect( self.basesPerTurnDoubleSpinBox,
                        SIGNAL("valueChanged(double)"),
                        self.basesPerTurnChanged )

        change_connect( self.duplexRiseDoubleSpinBox,
                        SIGNAL("valueChanged(double)"),
                        self.duplexRiseChanged )

        change_connect(self.showCursorTextCheckBox, 
                       SIGNAL('stateChanged(int)'), 
                       self._update_state_of_cursorTextGroupBox)


    def model_changed(self): 
        """
        @see: DnaSegment_EditCommand.model_changed()
        @see: DnaSegment_EditCommand.hasResizableStructure()
        @see: self._current_model_changed_params()
        """
        currentParams = self._current_model_changed_params()
        #Optimization. Return from the model_changed method if the 
        #params are the same. 
        if same_vals(currentParams, self._previous_model_changed_params):
            return 

        isStructResizable, why_not = currentParams
        #update the self._previous_model_changed_params with this new param set.
        self._previous_model_changed_params = currentParams

        if not isStructResizable:
            #disable all widgets
            if self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(False)
                msg = redmsg("DnaSegment is not resizable. Reason: %s"%(why_not))
                self.updateMessage(msg)
        else:
            if not self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(True)
                msg = "Use resize handles to resize the segment. Drag any axis or sugar"\
                    " atom for translation or rotation about axis respectively. Dragging"\
                    " any bond will freely move the whole segment."
                self.updateMessage(msg)


    def _current_model_changed_params(self):
        """
        Returns a tuple containing the parameters that will be compared
        against the previously stored parameters. This provides a quick test
        to determine whether to do more things in self.model_changed()
        @see: self.model_changed() which calls this
        @see: self._previous_model_changed_params attr. 
        """
        params = None

        if self.editCommand:   
            isStructResizable, why_not = self.editCommand.hasResizableStructure()
            params = (isStructResizable, why_not)

        return params 


    def show(self):
        """
        Show this property manager. Overrides EditCommand_PM.show()
        This method also retrives the name information from the 
        editCommand's structure for its name line edit field. 
        @see: DnaSegment_EditCommand.getStructureName()
        @see: self.close()
        """
        _superclass.show(self)
        if self.editCommand is not None:
            name = self.editCommand.getStructureName()
            if name is not None:
                self.nameLineEdit.setText(name)

    def close(self):
        """
        Close this property manager. 
        Also sets the name of the self.editCommand's structure to the one 
        displayed in the line edit field.
        @see self.show()
        @see: DnaSegment_EditCommand.setStructureName
        """
        if self.editCommand is not None:
            name = str(self.nameLineEdit.text())
            self.editCommand.setStructureName(name)
        _superclass.close(self)

    def setParameters(self, params):
        """
        This is usually called when you are editing an existing structure. 
        Some property manager ui elements then display the information 
        obtained from the object being edited. 
        TODO:
        - Make this a EditCommand_PM API method? 
        - See also the routines GraphicsMode.setParams or object.setProps
        ..better to name them all in one style?  
        """
        numberOfBasePairs, \
                         dnaForm, \
                         dnaModel,\
                         basesPerTurn, \
                         duplexRise, \
                         endPoint1, \
                         endPoint2 , \
                         color = params 

        if numberOfBasePairs is not None:
            self.numberOfBasePairsSpinBox.setValue(numberOfBasePairs)
        if dnaForm is not None:
            self._conformation = dnaForm
        if dnaModel is not None:
            self.dnaModel = dnaModel
        if duplexRise is not None:
            self.duplexRiseDoubleSpinBox.setValue(duplexRise)
        if basesPerTurn is not None:
            self.basesPerTurnDoubleSpinBox.setValue(basesPerTurn)
        if endPoint1 is not None:
            self.endPoint1 = endPoint1
        if endPoint2 is not None:
            self.endPoint2 = endPoint2
        if color is not None:
            self._colorChooser.setColor(color)



    def getParameters(self):
        """
        """
        #See bug 2802 for details about the parameter 
        #'number_of_basePairs_from_struct'. Basically it is used to check
        #if the structure got modified (e.g. because of undo) 
        #The numberOfBases parameter obtained from the propMgr is given as a 
        #separate parameter for the reasons mentioned in bug 2802
        #-- Ninad 2008-04-12
        number_of_basePairs_from_struct = None
        if self.editCommand.hasValidStructure():
            number_of_basePairs_from_struct = self.editCommand.struct.getNumberOfBasePairs()
        numberOfBases = self.numberOfBasePairsSpinBox.value()
        dnaForm  = self._conformation
        dnaModel = self.dnaModel
        basesPerTurn = self.basesPerTurn
        duplexRise = self.duplexRise
        color = self._colorChooser.getColor()

        return (
            number_of_basePairs_from_struct,
            numberOfBases, 
            dnaForm,
            dnaModel,
            basesPerTurn,
            duplexRise,
            self.endPoint1, 
            self.endPoint2, 
            color
        )

    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

    def basesPerTurnChanged( self, basesPerTurn ):
        """
        Slot for the B{Bases per turn} spinbox.
        """
        self.basesPerTurn = basesPerTurn


    def duplexRiseChanged( self, rise ):
        """
        Slot for the B{Rise} spinbox.
        """
        self.duplexRise = rise


    def _addGroupBoxes( self ):
        """
        Add the DNA Property Manager group boxes.
        """        

        self._pmGroupBox1 = PM_GroupBox( self, title = "Parameters" )
        self._loadGroupBox1( self._pmGroupBox1 )

        self._displayOptionsGroupBox = PM_GroupBox( self, 
                                                    title = "Display Options" )
        self._loadDisplayOptionsGroupBox( self._displayOptionsGroupBox )


    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """

        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                                         label         =  "Segment name:",
                                         text          =  "",
                                         setAsDefault  =  False)

        # Strand Length (i.e. the number of bases)
        self.numberOfBasePairsSpinBox = \
            PM_SpinBox( pmGroupBox, 
                        label         =  "Base pairs:", 
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )

        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  self.basesPerTurn,
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )

        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  self.duplexRise,
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )


        # Duplex Length
        self.duplexLengthLineEdit  =  \
            PM_LineEdit( pmGroupBox,
                         label         =  "Duplex length: ",
                         text          =  "0.0 Angstroms",
                         setAsDefault  =  False)

        self.duplexLengthLineEdit.setDisabled(True)  
        
        
    def _loadDisplayOptionsGroupBox(self, pmGroupBox):
        """
        Overrides superclass method. 
        Also loads the color chooser widget. 
        """
        self._loadColorChooser(pmGroupBox)
        _superclass._loadDisplayOptionsGroupBox(self, pmGroupBox)
        


    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 , 
            dnaSegmentEditCommand_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)
                  ("Number of base pairs", 
                   dnaSegmentEditCommand_cursorTextCheckBox_numberOfBasePairs_prefs_key),

                   ("Duplex length",
                    dnaSegmentEditCommand_cursorTextCheckBox_length_prefs_key),

                    ("Number of basepairs to be changed",
                     dnaSegmentEditCommand_cursorTextCheckBox_changedBasePairs_prefs_key) 
                 ]

        return params


    def _addWhatsThisText(self):
        """
        Add what's this text. 
        """
        pass

    def _addToolTipText(self):
        """
        Add Tooltip text
        """
        pass
 def _loadFreeDragRotateGroupBox(self, inPmGroupBox):
     """
     Load widgets in the Free Drag Rotate group box, which is 
     present within the Rotate groupbox.
     @param inPmGroupBox: The Free Drag Rotate group box in the Rotate 
                          group box.
     @type  inPmGroupBox: L{PM_GroupBox}
     """
     # Button list to create a toolbutton row.
     # Format: 
     # - buttonId, 
     # - buttonText , 
     # - iconPath
     # - tooltip
     # - shortcut
     # - column
     
     BUTTON_LIST = [ 
         ( "QToolButton", 1,  "ROTATEDEFAULT", 
           "ui/actions/Properties Manager/Rotate_Free.png", "", "F", 0 ),
         
         ( "QToolButton", 2,  "ROTATEX", 
           "ui/actions/Properties Manager/RotateX.png", "", "X", 1 ),
         
         ( "QToolButton", 3,  "ROTATEY",  
           "ui/actions/Properties Manager/RotateY.png", "", "Y", 2 ),
         
         ( "QToolButton", 4,  "ROTATEZ",  
           "ui/actions/Properties Manager/RotateZ.png", "", "Z", 3 ),
         
         ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",  
           "ui/actions/Properties Manager/translate+rotate-A.png", "", \
           "A", 4 )
                     
         ]
         
     self.freeDragRotateButtonGroup = \
         PM_ToolButtonRow( inPmGroupBox, 
                            title        = "",
                            buttonList   = BUTTON_LIST,
                            spanWidth = True,
                            checkedId    = 1,
                            setAsDefault = True,
                         )
             
     self.rotateFreeButton = self.freeDragRotateButtonGroup.getButtonById(1)
     self.rotateXButton    = self.freeDragRotateButtonGroup.getButtonById(2)
     self.rotateYButton    = self.freeDragRotateButtonGroup.getButtonById(3)
     self.rotateZButton    = self.freeDragRotateButtonGroup.getButtonById(4)
     self.rotAlongAxisButton = \
         self.freeDragRotateButtonGroup.getButtonById(5)
     
     inPmGroupBox.setStyleSheet(
         self.freeDragRotateButtonGroup._getStyleSheet())
             
     X_ROW_LABELS = [("QLabel", "Delta Theta X:", 0),
                     ("QLabel", "", 1),
                     ("QLabel", "0.00", 2),
                     ("QLabel", "Degrees", 3)]
     
     Y_ROW_LABELS = [("QLabel", "Delta Theta Y:", 0),
                     ("QLabel", "", 1),
                     ("QLabel", "0.00", 2),
                     ("QLabel", "Degrees", 3)]
     
     Z_ROW_LABELS = [("QLabel", "Delta Theta Z:", 0),
                     ("QLabel", "", 1),
                     ("QLabel", "0.00", 2),
                     ("QLabel", "Degrees", 3)]
     
     self.rotateXLabelRow = PM_LabelRow( inPmGroupBox,
                                         title = "",
                                         labelList = X_ROW_LABELS )  
     self.deltaThetaX_lbl = self.rotateXLabelRow.labels[2]
                                        
     self.rotateYLabelRow = PM_LabelRow( inPmGroupBox,
                                         title = "",
                                         labelList = Y_ROW_LABELS )
     self.deltaThetaY_lbl = self.rotateYLabelRow.labels[2]
                                       
     self.rotateZLabelRow = PM_LabelRow( inPmGroupBox,
                                         title = "",
                                         labelList = Z_ROW_LABELS )  
     self.deltaThetaZ_lbl = self.rotateZLabelRow.labels[2]    
     
     self.rotateAboutPointButton = PM_ToolButton(
                 inPmGroupBox, 
                 text = "Rotate selection about a point",
                 iconPath  = "ui/actions/Properties Manager"\
                 "/Rotate_Components.png",
                 spanWidth = True                    
                 )
     self.rotateAboutPointButton.setCheckable(True)
     self.rotateAboutPointButton.setAutoRaise(True)
     self.rotateAboutPointButton.setToolButtonStyle(
         Qt.ToolButtonTextBesideIcon)
     
     
     self.rotateStartCoordLineEdit = PM_LineEdit( 
         inPmGroupBox, 
         label        = "ui/actions/Properties Manager"\
                 "/Move_Start_Point.png",
         text         = "Define 3 points",
         setAsDefault = False,
         )
     self.rotateStartCoordLineEdit.setReadOnly(True)
     self.rotateStartCoordLineEdit.setEnabled(False)
class DnaStrand_PropertyManager( DnaOrCnt_PropertyManager):
    """
    The DnaStrand_PropertyManager class provides a Property Manager 
    for the DnaStrand_EditCommand.

    @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         =  "DnaStrand Properties"
    pmName        =  title
    iconPath      =  "ui/actions/Properties Manager/Strand.png"

    def __init__( self, win, editCommand ):
        """
        Constructor for the Build DNA property manager.
        """
        
        #For model changed signal
        self.previousSelectionParams = None
        
        #see self.connect_or_disconnect_signals for comment about this flag
        self.isAlreadyConnected = False
        self.isAlreadyDisconnected = False
        
        self.sequenceEditor = None      
        
        self._numberOfBases = 0 
        self._conformation = 'B-DNA'
        self.duplexRise = 3.18
        self.basesPerTurn = 10
        self.dnaModel = 'PAM3'
        
        
        _superclass.__init__( self, 
                                    win,
                                    editCommand)


        
        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_WHATS_THIS_BUTTON)
        
        self._loadSequenceEditor()
        
        msg = "Use resize handles to resize the strand. Use sequence editor"\
                   "to assign a new sequence or the current one to a file."
        self.updateMessage(msg)
        
               
    
    def _addGroupBoxes( self ):
        """
        Add the DNA Property Manager group boxes.
        """        
                
        self._pmGroupBox1 = PM_GroupBox( self, title = "Parameters" )
        self._loadGroupBox1( self._pmGroupBox1 )
        self._displayOptionsGroupBox = PM_GroupBox( self, 
                                                    title = "Display Options" )
        self._loadDisplayOptionsGroupBox( self._displayOptionsGroupBox )
    
    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box 4.
        """
        
        self.nameLineEdit = PM_LineEdit( pmGroupBox,
                         label         =  "Strand name:",
                         text          =  "",
                         setAsDefault  =  False)
        
        self.numberOfBasesSpinBox = \
            PM_SpinBox( pmGroupBox, 
                        label         =  "Number of bases:", 
                        value         =  self._numberOfBases,
                        setAsDefault  =  False,
                        minimum       =  2,
                        maximum       =  10000 )
        
        self.basesPerTurnDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Bases per turn:",
                              value         =  self.basesPerTurn,
                              setAsDefault  =  True,
                              minimum       =  8.0,
                              maximum       =  20.0,
                              decimals      =  2,
                              singleStep    =  0.1 )
        
        self.duplexRiseDoubleSpinBox  =  \
            PM_DoubleSpinBox( pmGroupBox,
                              label         =  "Rise:",
                              value         =  self.duplexRise,
                              setAsDefault  =  True,
                              minimum       =  2.0,
                              maximum       =  4.0,
                              decimals      =  3,
                              singleStep    =  0.01 )
        
        self.disableStructHighlightingCheckbox = \
            PM_CheckBox( pmGroupBox,
                         text         = "Don't highlight while editing DNA",
                         widgetColumn  = 0,
                         state        = Qt.Unchecked,
                         setAsDefault = True,
                         spanWidth = True
                         )
        
        #As of 2008-03-31, the properties such as number of bases will be 
        #editable only by using the resize handles. post FNANO we will support 
        #the 
        self.numberOfBasesSpinBox.setEnabled(False)
        self.basesPerTurnDoubleSpinBox.setEnabled(False)
        self.duplexRiseDoubleSpinBox.setEnabled(False)
        
    
            
    def _loadSequenceEditor(self):
        """
        Temporary code  that shows the Sequence editor ..a doc widget docked
        at the bottom of the mainwindow. The implementation is going to change
        before 'rattleSnake' product release.
        As of 2007-11-20: This feature (sequence editor) is waiting 
        for the ongoing dna model work to complete.
        """
        self.sequenceEditor = self.win.createDnaSequenceEditorIfNeeded() 
        self.sequenceEditor.hide()
        
        
    def _loadDisplayOptionsGroupBox(self, pmGroupBox):
        """
        Overrides superclass method. 
        Also loads the color chooser widget. 
        """
        self._loadColorChooser(pmGroupBox)
        _superclass._loadDisplayOptionsGroupBox(self, pmGroupBox)
        
         
    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 , 
            dnaStrandEditCommand_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)
                  ("Number of bases", 
                   dnaStrandEditCommand_cursorTextCheckBox_numberOfBases_prefs_key),

                  ("Number of bases to be changed",
                   dnaStrandEditCommand_cursorTextCheckBox_changedBases_prefs_key) 
                 ]

        return params
    
        
    def getParameters(self):
        numberOfBases = self.numberOfBasesSpinBox.value()
        dnaForm  = self._conformation
        dnaModel = self.dnaModel
        basesPerTurn = self.basesPerTurn
        duplexRise = self.duplexRise
        color = self._colorChooser.getColor()
              
        return (numberOfBases, 
                dnaForm,
                dnaModel,
                basesPerTurn,
                duplexRise, 
                color
                )
    
    def setParameters(self, params):
        """
        This is usually called when you are editing an existing structure. 
        Some property manager ui elements then display the information 
        obtained from the object being edited. 
        TODO:
        - Make this a EditCommand_PM API method? 
        - See also the routines GraphicsMode.setParams or object.setProps
        ..better to name them all in one style?  
        """
        #Set the duplex rise and bases per turn spinbox values. 
        
        numberOfBases, \
                     dnaForm, \
                     dnaModel, \
                     basesPerTurn, \
                     duplexRise, \
                     color  = params 
        
        if numberOfBases is not None:
            self.numberOfBasesSpinBox.setValue(numberOfBases)
        if dnaForm is not None:
            self._conformation = dnaForm
        if dnaModel is not None:
            self.dnaModel = dnaModel
        if duplexRise is not None:
            self.duplexRiseDoubleSpinBox.setValue(duplexRise)
        if basesPerTurn is not None:
            self.basesPerTurnDoubleSpinBox.setValue(basesPerTurn)    
        if color is not None:
            self._colorChooser.setColor(color)
    
    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
        """
        #TODO: This is a temporary fix for a bug. When you invoke a temporary
        # mode Entering such a temporary mode keeps the signals of 
        #PM from the previous mode connected (
        #but while exiting that temporary mode and reentering the 
        #previous mode, it atucally reconnects the signal! This gives rise to 
        #lots  of bugs. This needs more general fix in Temporary mode API. 
        # -- Ninad 2008-01-09 (similar comment exists in MovePropertyManager.py
                
        if isConnect and self.isAlreadyConnected:
            if debug_flags.atom_debug:
                print_compact_stack("warning: attempt to connect widgets"\
                                    "in this PM that are already connected." )
            return 
        
        if not isConnect and self.isAlreadyDisconnected:
            if debug_flags.atom_debug:
                print_compact_stack("warning: attempt to disconnect widgets"\
                                    "in this PM that are already disconnected.")
            return
        
        self.isAlreadyConnected = isConnect
        self.isAlreadyDisconnected = not isConnect
        
        if isConnect:
            change_connect = self.win.connect     
        else:
            change_connect = self.win.disconnect 
          
                
        if self.sequenceEditor:
            self.sequenceEditor.connect_or_disconnect_signals(isConnect)
            
        _superclass.connect_or_disconnect_signals(self, isConnect)
            
        
        change_connect(self.disableStructHighlightingCheckbox, 
                       SIGNAL('stateChanged(int)'), 
                       self.change_struct_highlightPolicy)
        
        change_connect(self.showCursorTextCheckBox, 
                       SIGNAL('stateChanged(int)'), 
                       self._update_state_of_cursorTextGroupBox)
        
    def model_changed(self): 
        """
        @see: DnaStrand_EditCommand.model_changed()
        @see: DnaStrand_EditCommand.hasResizableStructure()
        """
        isStructResizable, why_not = self.editCommand.hasResizableStructure()
        if not isStructResizable:
            #disable all widgets
            if self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(False)
                msg1 = ("Viewing properties of %s <br>") %(self.editCommand.struct.name) 
                msg2 = redmsg("DnaStrand is not resizable. Reason: %s"%(why_not))                    
                self.updateMessage(msg1 + msg2)
        else:
            if not self._pmGroupBox1.isEnabled():
                self._pmGroupBox1.setEnabled(True)
                msg1 = ("Viewing properties of %s <br>") %(self.editCommand.struct.name) 
                msg2 = "Use resize handles to resize the strand. Use sequence editor"\
                    "to assign a new sequence or the current one to a file."
                self.updateMessage(msg1 + msg2)
        
    def show(self):
        """
        Show this PM 
        As of 2007-11-20, it also shows the Sequence Editor widget and hides 
        the history widget. This implementation may change in the near future
        This method also retrives the name information from the 
        editCommand's structure for its name line edit field. 
        @see: DnaStrand_EditCommand.getStructureName()
        @see: self.close()
        """
        _superclass.show(self) 
        self._showSequenceEditor()
    
        if self.editCommand is not None:
            name = self.editCommand.getStructureName()
            if name is not None:
                self.nameLineEdit.setText(name)     
           
    def close(self):
        """
        Close this property manager. 
        Also sets the name of the self.editCommand's structure to the one 
        displayed in the line edit field.
        @see self.show()
        @see: DnaSegment_EditCommand.setStructureName
        """
        if self.editCommand is not None:
            name = str(self.nameLineEdit.text())
            self.editCommand.setStructureName(name)
            
        if self.sequenceEditor:
            self.sequenceEditor.close()
                            
        _superclass.close(self)
            
    def _showSequenceEditor(self):
        if self.sequenceEditor:
            if not self.sequenceEditor.isVisible():
                #Show the sequence editor
                #ATTENTION: the sequence editor also closes (temporarily) the
                #reports dockwidget (if visible) Its state is later restored when
                #the sequuence Editor is closed. 
                self.sequenceEditor.show()     
                                              
            self.updateSequence()

        
    def updateSequence(self):
        """
        Update the sequence string in the sequence editor
        @see: DnaSequenceEditor.setSequence()
        @see DnaSequenceEditor._determine_complementSequence()
        @see: DnaSequenceEditor.setComplementSequence()
        @see: DnaStrand.getStrandSequenceAndItsComplement()
        """
        #Read in the strand sequence of the selected strand and 
        #show it in the text edit in the sequence editor.
        ##strand = self.strandListWidget.getPickedItem()
        
        if not self.editCommand.hasValidStructure():
            return
        
        strand = self.editCommand.struct
        
        titleString = 'Sequence Editor for ' + strand.name
                           
        self.sequenceEditor.setWindowTitle(titleString)
        sequenceString, complementSequenceString = strand.getStrandSequenceAndItsComplement()
        if sequenceString:
            sequenceString = QString(sequenceString) 
            sequenceString = sequenceString.toUpper()
            #Set the initial sequence (read in from the file)
            self.sequenceEditor.setSequence(sequenceString)
            
            #Set the initial complement sequence for DnaSequence editor. 
            #do this independently because 'complementSequenceString' may have
            #some characters (such as * ) that denote a missing base on the 
            #complementary strand. this information is used by the sequence
            #editor. See DnaSequenceEditor._determine_complementSequence() 
            #for more details. See also bug 2787
            self.sequenceEditor.setComplementSequence(complementSequenceString)

            
    def change_struct_highlightPolicy(self,checkedState = False):
        """
        Change the 'highlight policy' of the structure being edited 
        (i.e. self.editCommand.struct) . 
        @param checkedState: The checked state of the checkbox that says 
                             'Don't highlight while editing DNA'. So, it 
                             its True, the structure being edited won't get
                             highlighted. 
        @see: DnaStrand.setHighlightPolicy for more comments        
        """        
        if self.editCommand and self.editCommand.hasValidStructure():
            highlight = not checkedState
            self.editCommand.struct.setHighlightPolicy(highlight = highlight)

    def _addWhatsThisText(self):
        """
        Add what's this text. 
        Abstract method.
        """
        pass
class Ui_MovePropertyManager(Command_PropertyManager):
    
       
    # The title that appears in the Property Manager header        
    title = "Move"
    # The name of this Property Manager. This will be set to
    # the name of the PM_Dialog object via setObjectName().
    pmName = title
    # The relative path to the PNG file that appears in the header
    iconPath = "ui/actions/Properties Manager/Translate_Components.png"
    
    # The title(s) that appear in the property manager header.
    # (these are changed depending on the active group box) 
    translateTitle = "Translate"
    rotateTitle = "Rotate"

    # The full path to PNG file(s) that appears in the header.
    translateIconPath = "ui/actions/Properties Manager/Translate_Components.png"
    rotateIconPath = "ui/actions/Properties Manager/Rotate_Components.png"
    
    def __init__(self, command):  
        _superclass.__init__(self, command)       
        self.showTopRowButtons(PM_DONE_BUTTON | PM_WHATS_THIS_BUTTON)
    
    def _addGroupBoxes(self):
        """
        Add groupboxes to the Property Manager dialog. 
        """
        
        self.translateGroupBox = PM_GroupBox( self, 
                                              title = "Translate",
                                              connectTitleButton = False)
        self.translateGroupBox.titleButton.setShortcut('T')
        self._loadTranslateGroupBox(self.translateGroupBox)
        
        self.rotateGroupBox = PM_GroupBox( self, 
                                           title = "Rotate",
                                           connectTitleButton = False)
        self.rotateGroupBox.titleButton.setShortcut('R')
        self._loadRotateGroupBox(self.rotateGroupBox)
        
        self.translateGroupBox.collapse()
        self.rotateGroupBox.collapse()
            
    # == Begin Translate Group Box =====================
    
    def _loadTranslateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Translate group box.
        @param inPmGroupBox: The Translate group box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        
        translateChoices = [ "Free Drag", 
                             "By Delta XYZ", 
                             "To XYZ Position" ]
        
        self.translateComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = '', 
                         choices      = translateChoices, 
                         index        = 0, 
                         setAsDefault = False,
                         spanWidth    = True )
        
        self.freeDragTranslateGroupBox = PM_GroupBox( inPmGroupBox )
        self._loadFreeDragTranslateGroupBox(self.freeDragTranslateGroupBox)
                
        self.byDeltaGroupBox = PM_GroupBox( inPmGroupBox )
        self._loadByDeltaGroupBox(self.byDeltaGroupBox)
        
        self.toPositionGroupBox = PM_GroupBox( inPmGroupBox )
        self._loadToPositionGroupBox(self.toPositionGroupBox)
        
        self.updateTranslateGroupBoxes(0)
    
    def _loadFreeDragTranslateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Free Drag Translate group box, which is present 
        within the Translate groupbox.
        @param inPmGroupBox: The Free Drag Translate group box in the Translate 
                             group box. 
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format: 
        # - buttonId, 
        # - buttonText , 
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        
         
        BUTTON_LIST = [ 
            ( "QToolButton", 1,  "MOVEDEFAULT", 
              "ui/actions/Properties Manager/Move_Free.png", "", "F", 0),
            ( "QToolButton", 2,  "TRANSX", 
              "ui/actions/Properties Manager/TranslateX.png", "", "X", 1),
            ( "QToolButton", 3,  "TRANSY",  
              "ui/actions/Properties Manager/TranslateY.png", "", "Y", 2),
            ( "QToolButton", 4,  "TRANSZ",  
              "ui/actions/Properties Manager/TranslateZ.png", "", "Z", 3),
            ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",  
              "ui/actions/Properties Manager/translate+rotate-A.png", "", \
              "A", 4)
                        
            ]
            
        self.freeDragTranslateButtonGroup = \
            PM_ToolButtonRow( inPmGroupBox, 
                               title        = "",
                               buttonList   = BUTTON_LIST,
                               checkedId    = 1,
                               setAsDefault = True,
                               )
        self.transFreeButton =self.freeDragTranslateButtonGroup.getButtonById(1)
        self.transXButton = self.freeDragTranslateButtonGroup.getButtonById(2)
        self.transYButton = self.freeDragTranslateButtonGroup.getButtonById(3)
        self.transZButton = self.freeDragTranslateButtonGroup.getButtonById(4)
        self.transAlongAxisButton = \
            self.freeDragTranslateButtonGroup.getButtonById(5)
        
        self.moveFromToButton = PM_ToolButton(
                    inPmGroupBox, 
                    text = "Translate from/to",
                    iconPath  = "ui/actions/Properties Manager"\
                    "/Translate_Components.png",
                    spanWidth = True
                    
                    )
        self.moveFromToButton.setCheckable(True)
        self.moveFromToButton.setAutoRaise(True)
        self.moveFromToButton.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        
        
        self.startCoordLineEdit = PM_LineEdit( 
            inPmGroupBox, 
            label        = "ui/actions/Properties Manager"\
                    "/Move_Start_Point.png",
            text         = "Define 'from' and 'to' points",
            setAsDefault = False,
            )
        self.startCoordLineEdit.setReadOnly(True)
        self.startCoordLineEdit.setEnabled(False)
        
    def _loadByDeltaGroupBox(self, inPmGroupBox):
        """
        Load widgets in the translate By Delta group box, which is present 
        within the Translate groupbox.
        @param inPmGroupBox: The Translate By Delta group box in the translate 
                             group box. 
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.moveDeltaXSpinBox = \
            PM_DoubleSpinBox( 
                inPmGroupBox, 
                label        = "ui/actions/Properties Manager/Delta_X.png",
                value        = 0.0, 
                setAsDefault = True,
                minimum      = -100.0, 
                maximum      =  100.0, 
                singleStep   = 1.0, 
                decimals     = 3, 
                suffix       = ' Angstroms',
                spanWidth    = False )
        
        self.moveDeltaYSpinBox = \
            PM_DoubleSpinBox( 
                inPmGroupBox, 
                label        = "ui/actions/Properties Manager/Delta_Y.png",
                value        = 0.0, 
                setAsDefault = True,
                minimum      = -100.0, 
                maximum      =  100.0, 
                singleStep   = 1.0, 
                decimals     = 3, 
                suffix       = ' Angstroms',
                spanWidth    = False )
        
        self.moveDeltaZSpinBox = \
            PM_DoubleSpinBox( 
                inPmGroupBox, 
                label        = "ui/actions/Properties Manager/Delta_Z.png",
                value        = 0.0, 
                setAsDefault = True,
                minimum      = -100.0, 
                maximum      =  100.0, 
                singleStep   = 1.0, 
                decimals     = 3, 
                suffix       = ' Angstroms',
                spanWidth    = False )
        
        DELTA_BUTTONS = [
                        ("QToolButton",1,  "Delta Plus", 
                         "ui/actions/Properties Manager/Move_Delta_Plus.png", 
                         "", "+", 0 ),
            
                        ( "QToolButton", 2,  "Delta Minus",  
                          "ui/actions/Properties Manager/Move_Delta_Minus.png", 
                          "", "-", 1 )
                        ]
        
        self.translateDeltaButtonRow = \
            PM_ToolButtonRow( inPmGroupBox, 
                              title        = "",
                              buttonList   = DELTA_BUTTONS,
                              label        = 'Translate:',
                              isAutoRaise  =  True,
                              isCheckable  =  False                           
                            )
        self.transDeltaPlusButton = \
            self.translateDeltaButtonRow.getButtonById(1)
        self.transDeltaMinusButton = \
            self.translateDeltaButtonRow.getButtonById(2)
        
            
    def _loadToPositionGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Translate To a given Position group box, which is 
        present within the Translate groupbox.
        @param inPmGroupBox: Translate To Position group box in the Translate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        
        self.toPositionspinboxes = PM_CoordinateSpinBoxes(inPmGroupBox)
        
        self.moveXSpinBox = self.toPositionspinboxes.xSpinBox
        self.moveYSpinBox = self.toPositionspinboxes.ySpinBox
        self.moveZSpinBox = self.toPositionspinboxes.zSpinBox
        
        
        self.moveAbsoluteButton = \
            PM_PushButton( inPmGroupBox,
                           label     = "",
                           text      = "Move Selection",
                           spanWidth = True )
            
    # == Begin Rotate Group Box =====================
    def _loadRotateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Rotate group box, 
        @param inPmGroupBox: The Rotate GroupBox in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        
        rotateChoices = [ "Free Drag", "By Specified Angle"]
        
        self.rotateComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = '', 
                         choices      = rotateChoices, 
                         index        = 0, 
                         setAsDefault = False,
                         spanWidth    = True )
        
        self.rotateAsUnitCB = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Rotate as a unit' ,
                         widgetColumn = 0,
                         state        = Qt.Checked )
        
               
        self.freeDragRotateGroupBox = PM_GroupBox( inPmGroupBox )
        self._loadFreeDragRotateGroupBox(self.freeDragRotateGroupBox)
        
        self.bySpecifiedAngleGroupBox = PM_GroupBox( inPmGroupBox )
        self._loadBySpecifiedAngleGroupBox(self.bySpecifiedAngleGroupBox)
        
        self.updateRotateGroupBoxes(0)    
        
    def _loadFreeDragRotateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Free Drag Rotate group box, which is 
        present within the Rotate groupbox.
        @param inPmGroupBox: The Free Drag Rotate group box in the Rotate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format: 
        # - buttonId, 
        # - buttonText , 
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        
        BUTTON_LIST = [ 
            ( "QToolButton", 1,  "ROTATEDEFAULT", 
              "ui/actions/Properties Manager/Rotate_Free.png", "", "F", 0 ),
            
            ( "QToolButton", 2,  "ROTATEX", 
              "ui/actions/Properties Manager/RotateX.png", "", "X", 1 ),
            
            ( "QToolButton", 3,  "ROTATEY",  
              "ui/actions/Properties Manager/RotateY.png", "", "Y", 2 ),
            
            ( "QToolButton", 4,  "ROTATEZ",  
              "ui/actions/Properties Manager/RotateZ.png", "", "Z", 3 ),
            
            ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",  
              "ui/actions/Properties Manager/translate+rotate-A.png", "", \
              "A", 4 )
                        
            ]
            
        self.freeDragRotateButtonGroup = \
            PM_ToolButtonRow( inPmGroupBox, 
                               title        = "",
                               buttonList   = BUTTON_LIST,
                               spanWidth = True,
                               checkedId    = 1,
                               setAsDefault = True,
                            )
                
        self.rotateFreeButton = self.freeDragRotateButtonGroup.getButtonById(1)
        self.rotateXButton    = self.freeDragRotateButtonGroup.getButtonById(2)
        self.rotateYButton    = self.freeDragRotateButtonGroup.getButtonById(3)
        self.rotateZButton    = self.freeDragRotateButtonGroup.getButtonById(4)
        self.rotAlongAxisButton = \
            self.freeDragRotateButtonGroup.getButtonById(5)
        
        inPmGroupBox.setStyleSheet(
            self.freeDragRotateButtonGroup._getStyleSheet())
                
        X_ROW_LABELS = [("QLabel", "Delta Theta X:", 0),
                        ("QLabel", "", 1),
                        ("QLabel", "0.00", 2),
                        ("QLabel", "Degrees", 3)]
        
        Y_ROW_LABELS = [("QLabel", "Delta Theta Y:", 0),
                        ("QLabel", "", 1),
                        ("QLabel", "0.00", 2),
                        ("QLabel", "Degrees", 3)]
        
        Z_ROW_LABELS = [("QLabel", "Delta Theta Z:", 0),
                        ("QLabel", "", 1),
                        ("QLabel", "0.00", 2),
                        ("QLabel", "Degrees", 3)]
        
        self.rotateXLabelRow = PM_LabelRow( inPmGroupBox,
                                            title = "",
                                            labelList = X_ROW_LABELS )  
        self.deltaThetaX_lbl = self.rotateXLabelRow.labels[2]
                                           
        self.rotateYLabelRow = PM_LabelRow( inPmGroupBox,
                                            title = "",
                                            labelList = Y_ROW_LABELS )
        self.deltaThetaY_lbl = self.rotateYLabelRow.labels[2]
                                          
        self.rotateZLabelRow = PM_LabelRow( inPmGroupBox,
                                            title = "",
                                            labelList = Z_ROW_LABELS )  
        self.deltaThetaZ_lbl = self.rotateZLabelRow.labels[2]    
        
        self.rotateAboutPointButton = PM_ToolButton(
                    inPmGroupBox, 
                    text = "Rotate selection about a point",
                    iconPath  = "ui/actions/Properties Manager"\
                    "/Rotate_Components.png",
                    spanWidth = True                    
                    )
        self.rotateAboutPointButton.setCheckable(True)
        self.rotateAboutPointButton.setAutoRaise(True)
        self.rotateAboutPointButton.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        
        
        self.rotateStartCoordLineEdit = PM_LineEdit( 
            inPmGroupBox, 
            label        = "ui/actions/Properties Manager"\
                    "/Move_Start_Point.png",
            text         = "Define 3 points",
            setAsDefault = False,
            )
        self.rotateStartCoordLineEdit.setReadOnly(True)
        self.rotateStartCoordLineEdit.setEnabled(False)
            
            
    def _loadBySpecifiedAngleGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Rotate By Specified Angle group box, which is 
        present within the Rotate groupbox.
        @param inPmGroupBox: Rotate By Specified Angle group box in the Rotate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format: 
        # - buttonId, 
        # - buttonText , 
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        
        BUTTON_LIST = [ 
            ( "QToolButton", 1,  "ROTATEX", 
              "ui/actions/Properties Manager/RotateX.png", 
              "Rotate about X axis", "X", 0 ),
            
            ( "QToolButton", 2,  "ROTATEY",  
              "ui/actions/Properties Manager/RotateY.png", 
              "Rotate about Y axis", "Y", 1 ),
            
            ( "QToolButton", 3,  "ROTATEZ",  
              "ui/actions/Properties Manager/RotateZ.png", 
              "Rotate about Z axis","Z", 2 ),
            ]
        
        
        
        self.rotateAroundAxisButtonRow = \
            PM_ToolButtonRow( inPmGroupBox, 
                              title        = "",
                              buttonList   = BUTTON_LIST,
                              alignment    = 'Right',
                              label        = 'Rotate Around:'
                            )
        self.rotXaxisButton = \
            self.rotateAroundAxisButtonRow.getButtonById(1)
        
        self.rotYaxisButton = \
            self.rotateAroundAxisButtonRow.getButtonById(2)
        
        self.rotZaxisButton = \
            self.rotateAroundAxisButtonRow.getButtonById(3)
        
        

        self.rotateThetaSpinBox = \
            PM_DoubleSpinBox(inPmGroupBox,
                             label        = "Rotate By:",
                             value        = 0.0, 
                             setAsDefault = True,
                             minimum      = 0, 
                             maximum      = 360.0,
                             singleStep   = 1.0, 
                             decimals     = 2, 
                             suffix       = ' Degrees')
        
        
        THETA_BUTTONS = [ 
            ( "QToolButton", 1,  "Theta Plus", 
              "ui/actions/Properties Manager/Move_Theta_Plus.png", "", "+", 0 ),
            
            ( "QToolButton", 2,  "Theta Minus",  
              "ui/actions/Properties Manager/Move_Theta_Minus.png", "", "-", 1 )
            ]
        
        self.rotateThetaButtonRow = \
            PM_ToolButtonRow( inPmGroupBox, 
                              title        = "",
                              buttonList   = THETA_BUTTONS,
                              label        = 'Direction:',
                              isAutoRaise  =  True,
                              isCheckable  =  False                           
                            )
        self.rotateThetaPlusButton =  self.rotateThetaButtonRow.getButtonById(1)
        self.rotateThetaMinusButton = self.rotateThetaButtonRow.getButtonById(2)
    
    # == End Rotate Group Box =====================
        
    # == Slots for Translate group box
    def _hideAllTranslateGroupBoxes(self):
        """
        Hides all Translate group boxes.
        """
        self.toPositionGroupBox.hide()
        self.byDeltaGroupBox.hide()
        self.freeDragTranslateGroupBox.hide()
            
    def updateTranslateGroupBoxes(self, id):
        """
        Update the translate group boxes displayed based on the translate
        option selected.
        @param id: Integer value corresponding to the combobox item in the 
                   Translate group box. 
        @type  id: int
        """
        self._hideAllTranslateGroupBoxes()
        
        if id is 0:
            self.freeDragTranslateGroupBox.show()
                               
        if id is 1:
            self.byDeltaGroupBox.show()                    
        if id is 2:
            self.toPositionGroupBox.show()
           
        self.updateMessage()
    
    def changeMoveOption(self, button):
        """
        Subclasses should reimplement this method.
        
        @param button: QToolButton that decides the type of translate operation 
        to be set.
        @type  button: QToolButton 
                       L{http://doc.trolltech.com/4.2/qtoolbutton.html}
        @see: B{MovePropertyManager.changeMoveOption} which overrides this
              method
        """
        pass
    
    # == Slots for Rotate group box
    def updateRotateGroupBoxes(self, id):
        """
        Update the translate group boxes displayed based on the translate
        option selected.
        @param id: Integer value corresponding to the combobox item in the 
                   Rotate group box. 
        @type  id: int
        """
        if id is 0:
            self.bySpecifiedAngleGroupBox.hide()
            self.freeDragRotateGroupBox.show()   
        if id is 1:
            self.freeDragRotateGroupBox.hide()
            self.bySpecifiedAngleGroupBox.show()
            
        self.updateMessage()
            
    def changeRotateOption(self, button):
        """
        Subclasses should reimplement this method. 
        
        @param button: QToolButton that decides the type of rotate operation 
        to be set.
        @type  button: QToolButton 
                       L{http://doc.trolltech.com/4.2/qtoolbutton.html}
        @see: B{MovePropertyManage.changeRotateOption} which overrides this 
             method
        """
        pass
    
    def _addWhatsThisText(self):
        """
        What's This text for some of the widgets in this Property Manager.  

        """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_MovePropertyManager
        whatsThis_MovePropertyManager(self)
        
    def _addToolTipText(self):
        """
        Tool Tip text for widgets in this Property Manager.  
        """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_MovePropertyManager
        ToolTip_MovePropertyManager(self)
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/Tools/Build Structures/InsertNanotube.png"

    def __init__(self, win, editCommand):
        """
        Constructor for the Nanotube property manager.
        """
        self.endPoint1 = None
        self.endPoint2 = None

        self.nanotube = Nanotube()  # A 5x5 CNT.

        _superclass.__init__(self, win, editCommand)

        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 ok_btn_clicked(self):
        """
        Slot for the OK button
        """
        if self.editCommand:
            self.editCommand.preview_or_finalize_structure(previewing=False)
            ##env.history.message(self.editCommand.logMessage)
        self.win.toolsDone()

    def cancel_btn_clicked(self):
        """
        Slot for the Cancel button.
        """
        if self.editCommand:
            self.editCommand.cancelStructure()
        self.win.toolsCancel()

    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 getFlyoutActionList(self):
        """
        Returns custom actionlist that will be used in a specific mode
        or editing a feature etc Example: while in movie mode,
        the _createFlyoutToolBar method calls this.
        """
        #'allActionsList' returns all actions in the flyout toolbar
        #including the subcontrolArea actions
        allActionsList = []

        #Action List for  subcontrol Area buttons.
        #In this mode there is really no subcontrol area.
        #We will treat subcontrol area same as 'command area'
        #(subcontrol area buttons will have an empty list as their command area
        #list). We will set  the Comamnd Area palette background color to the
        #subcontrol area.

        subControlAreaActionList = []

        self.exitEditCommandAction.setChecked(True)
        subControlAreaActionList.append(self.exitEditCommandAction)

        separator = QAction(self.w)
        separator.setSeparator(True)
        subControlAreaActionList.append(separator)

        allActionsList.extend(subControlAreaActionList)

        #Empty actionlist for the 'Command Area'
        commandActionLists = []

        #Append empty 'lists' in 'commandActionLists equal to the
        #number of actions in subControlArea
        for i in range(len(subControlAreaActionList)):
            lst = []
            commandActionLists.append(lst)

        params = (subControlAreaActionList, commandActionLists, allActionsList)

        return params

    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))
        print "Bond Length =", self.nanotube.getBondLength()
        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
        """
        _n, _m = self.nanotube.setChirality(self.chiralityNSpinBox.value(),
                                            self.chiralityMSpinBox.value())

        #self.n, self.m = self.nanotube.getChirality()

        self.connect_or_disconnect_signals(isConnect=False)
        self.chiralityNSpinBox.setValue(_n)
        self.chiralityMSpinBox.setValue(_m)
        self.connect_or_disconnect_signals(isConnect=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
    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options,
        Find and replace widgets etc.
        """

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")

        self.saveSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png")

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        # Hide load and save buttons until they are implemented. -Mark 2008-12-20.
        self.loadSequenceButton.hide()
        self.saveSequenceButton.hide()

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)

        self.replaceLineEdit = \
            PM_LineEdit( self,
                         label        = "  Replace:",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        # Hide Replace widgets until we add support for transmuting residues.
        # Mark 2008-12-19
        #self.replaceLabel.hide()
        self.replacePushButton.hide()
        self.replaceLineEdit.hide()

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        #Widgets to include in the widget row.
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "                                     Find:", 4),
                      ('PM_LineEdit',   self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8),
                      #('PM_Label',      self.replaceLabel, 9),
                      ('PM_TextEdit',   self.replaceLineEdit, 9),
                      ('PM_PushButton', self.replacePushButton, 10),
                      ('PM_Label', self.warningSign, 11),
                      ('PM_Label', self.phraseNotFoundLabel, 12),
                      ('QSpacerItem', 5, 5, 13) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )
        return
    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Open.png")

        self.saveSequenceButton = PM_ToolButton(
            self,
            iconPath="ui/actions/Properties Manager/Save_Strand_Sequence.png")

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        # Only supporting 5' to 3' direction until bug 2956 is fixed.
        # Mark 2008-12-19
        editDirectionChoices = ["5' to 3'"]  # , "3' to 5'"]
        self.baseDirectionChoiceComboBox = \
            PM_ComboBox( self,
                         choices = editDirectionChoices,
                         index     = 0,
                         spanWidth = False )

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self,
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self, iconPath="ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text="Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done --

        # When the widget type starts with the word 'PM_' , the
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned
        # labels (and if they were recreated as mentioned above),
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2]
        #Cleanup in PM_widgetGrid could be to check if the widget starts with
        #'Q'  instead of 'PM_'

        #Widgets to include in the widget row.
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Sequence direction:", 2),
                      ('PM_ComboBox', self.baseDirectionChoiceComboBox, 3),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8),
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10),
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14)]

        widgetRow = PM_WidgetRow(self,
                                 title='',
                                 widgetList=widgetList,
                                 label="",
                                 spanWidth=True)
        return
class Ui_CookiePropertyManager(PM_Dialog):
    """
    The Ui_CookiePropertyManager class defines UI elements for the Property 
    Manager of the B{Crystal mode}.
    
    @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> - the title that appears in the property manager header.
    title = "Build Crystal"
    # <iconPath> - full path to PNG file that appears in the header.
    # The name of this Property Manager. This will be set to
    # the name of the PM_Dialog object via setObjectName().
    pmName = title
    iconPath = "ui/actions/Tools/Build Structures/Build Crystal.png"

    def __init__(self, parentMode):
        """
        Constructor for the B{Crystal} property manager class that defines 
        its UI.
        
        @param parentMode: The parent mode where this Property Manager is used
        @type  parentMode: L{cookieMode}  
        """
        self.parentMode = parentMode
        self.w = self.parentMode.w
        self.win = self.parentMode.w
        self.pw = self.parentMode.pw
        self.o = self.parentMode.o

        PM_Dialog.__init__(self, self.pmName, self.iconPath, self.title)

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_CANCEL_BUTTON | \
                                PM_WHATS_THIS_BUTTON)

        msg = ''
        self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault=False)

    def _addGroupBoxes(self):
        """
        Add various group boxes to the Property manager. 
        """
        self._addCrystalSpecsGroupbox()
        self._addLayerPropertiesGroupBox()
        self._addDisplayOptionsGroupBox()
        self._addAdvancedOptionsGroupBox()

    def _addCrystalSpecsGroupbox(self):
        """
        Add 'Crystal groupbox' to the PM
        """
        self.crystalSpecsGroupBox = \
            PM_GroupBox(self, title = "Crystal Specifications")
        self._loadCrystalSpecsGroupBox(self.crystalSpecsGroupBox)

    def _addLayerPropertiesGroupBox(self):
        """
        Add 'Layer Properties' groupbox to the PM
        """
        self.layerPropertiesGroupBox = \
            PM_GroupBox(self, title = "Layer Properties")
        self._loadLayerPropertiesGroupBox(self.layerPropertiesGroupBox)

    def _addAdvancedOptionsGroupBox(self):
        """
        Add 'Advanced Options' groupbox
        """
        self.advancedOptionsGroupBox = \
            PM_GroupBox( self, title = "Advanced Options" )
        self._loadAdvancedOptionsGroupBox(self.advancedOptionsGroupBox)

    def _addDisplayOptionsGroupBox(self):
        """
        Add 'Display Options' groupbox
        """
        self.displayOptionsGroupBox = PM_GroupBox(self,
                                                  title='Display Options')
        self._loadDisplayOptionsGroupBox(self.displayOptionsGroupBox)

    def _loadCrystalSpecsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Crystal Specifications group box.
        @param inPmGroupBox: The Crystal Specifications groupbox in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """
        latticeChoices = ["Diamond", "Lonsdaleite"]

        self.latticeCBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = 'Lattice:',
                         labelColumn  = 0,
                         choices      = latticeChoices,
                         index        = 0,
                         setAsDefault = True,
                         spanWidth    = False )

        # Button list to create a toolbutton row.
        # Format:
        # - buttonType,
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column
        BUTTON_LIST = [("QToolButton", 0, "Surface 100",
                        "ui/actions/Properties Manager/Surface100.png",
                        "Surface 100", "", 0),
                       ("QToolButton", 1, "Surface 110",
                        "ui/actions/Properties Manager/Surface110.png",
                        "Surface 110", "", 1),
                       ("QToolButton", 2, "Surface 111",
                        "ui/actions/Properties Manager/Surface111.png",
                        "Surface 110", "", 2)]
        self.gridOrientationButtonRow = \
            PM_ToolButtonRow(inPmGroupBox,
                               title        = "",
                               label        = "Orientation:",
                               buttonList   = BUTTON_LIST,
                               checkedId    = 0,
                               setAsDefault = True,
                               spanWidth   = False
                               )

        self.orientButtonGroup = self.gridOrientationButtonRow.buttonGroup
        self.surface100_btn = self.gridOrientationButtonRow.getButtonById(0)
        self.surface110_btn = self.gridOrientationButtonRow.getButtonById(1)
        self.surface111_btn = self.gridOrientationButtonRow.getButtonById(2)

        self.rotateGridByAngleSpinBox = \
            PM_SpinBox( inPmGroupBox,
                        label         =  "Rotate by: ",
                        labelColumn   =  0,
                        value         =  45,
                        minimum       =  0,
                        maximum       =  360,
                        singleStep    =  5,
                        suffix        = " degrees")

        GRID_ANGLE_BUTTONS = [
            ("QToolButton", 0, "Anticlockwise",
             "ui/actions/Properties Manager/rotate_minus.png", "", "+", 0),
            ("QToolButton", 1, "Clockwise",
             "ui/actions/Properties Manager/rotate_plus.png", "", "-", 1)
        ]

        self.gridRotateButtonRow = \
            PM_ToolButtonRow( inPmGroupBox,
                              title        = "",
                              buttonList   = GRID_ANGLE_BUTTONS,
                              label        = 'Rotate grid:',
                              isAutoRaise  =  False,
                              isCheckable  =  False
                            )
        self.rotGridAntiClockwiseButton = \
            self.gridRotateButtonRow.getButtonById(0)
        self.rotGridClockwiseButton = \
            self.gridRotateButtonRow.getButtonById(1)

    def _loadLayerPropertiesGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Layer Properties group box.
        @param inPmGroupBox: The Layer Properties groupbox in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        self.currentLayerComboBox = \
            PM_ComboBox( inPmGroupBox,
                         index     = 0,
                         spanWidth = True
                        )

        self.addLayerButton = PM_PushButton(inPmGroupBox)
        self.addLayerButton.setIcon(
            geticon('ui/actions/Properties Manager/addlayer.png'))
        self.addLayerButton.setFixedSize(QSize(26, 26))
        self.addLayerButton.setIconSize(QSize(22, 22))

        # A widget list to create a widget row.
        # Format:
        # - Widget type,
        # - widget object,
        # - column

        firstRowWidgetList = [('PM_ComboBox', self.currentLayerComboBox, 1),
                              ('PM_PushButton', self.addLayerButton, 2)]

        widgetRow = PM_WidgetRow(
            inPmGroupBox,
            title='',
            widgetList=firstRowWidgetList,
            label="Layer:",
            labelColumn=0,
        )

        self.layerCellsSpinBox = \
             PM_SpinBox( inPmGroupBox,
                        label         =  "Lattice cells:",
                        labelColumn   =  0,
                        value         =  2,
                        minimum       =  1,
                        maximum       =  25
                      )

        self.layerThicknessLineEdit = PM_LineEdit(inPmGroupBox,
                                                  label="Thickness:",
                                                  text="",
                                                  setAsDefault=False,
                                                  spanWidth=False)

        #self.layerThicknessLineEdit.setReadOnly(True)
        self.layerThicknessLineEdit.setDisabled(True)
        tooltip = "Thickness of layer in Angstroms"
        self.layerThicknessLineEdit.setToolTip(tooltip)

    def _loadAdvancedOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Advanced Options group box.
        @param inPmGroupBox: The Advanced Options box in the PM
        @type  inPmGroupBox: L{PM_GroupBox} 
        """
        self.snapGridCheckBox = \
            PM_CheckBox(inPmGroupBox,
                        text = "Snap to grid",
                        state = Qt.Checked
                        )
        tooltip = "Snap selection point to a nearest cell grid point."
        self.snapGridCheckBox.setToolTip(tooltip)

        self.freeViewCheckBox = \
            PM_CheckBox(inPmGroupBox,
                        text = "Enable free view",
                        state = Qt.Unchecked
                    )

    def _loadDisplayOptionsGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Display Options groupbox.
        @param inPmGroupBox: The Display Options groupbox 
        @type  inPmGroupBox: L{PM_GroupBox} 
        """

        displayChoices = ['Tubes', 'Spheres']

        self.dispModeComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = 'Display style:',
                         choices      = displayChoices,
                         index        = 0,
                         setAsDefault = False,
                         spanWidth    = False )

        self.gridLineCheckBox = PM_CheckBox(inPmGroupBox,
                                            text="Show grid lines",
                                            widgetColumn=0,
                                            state=Qt.Checked)

        self.fullModelCheckBox = PM_CheckBox(inPmGroupBox,
                                             text="Show model",
                                             widgetColumn=0,
                                             state=Qt.Unchecked)

    def _addWhatsThisText(self):
        """
        What's This text for widgets in this Property Manager.  

        @note: Many PM widgets are still missing their "What's This" text.
        """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_CookiePropertyManager
        whatsThis_CookiePropertyManager(self)

    def _addToolTipText(self):
        """
        What's Tool Tip text for widgets in this Property Manager.  
        """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_CookiePropertyManager
        ToolTip_CookiePropertyManager(self)
    def _loadFreeDragTranslateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Free Drag Translate group box, which is present 
        within the Translate groupbox.
        @param inPmGroupBox: The Free Drag Translate group box in the Translate 
                             group box. 
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column


        BUTTON_LIST = [
            ( "QToolButton", 1,  "MOVEDEFAULT",
              "ui/actions/Properties Manager/Move_Free.png", "", "F", 0),
            ( "QToolButton", 2,  "TRANSX",
              "ui/actions/Properties Manager/TranslateX.png", "", "X", 1),
            ( "QToolButton", 3,  "TRANSY",
              "ui/actions/Properties Manager/TranslateY.png", "", "Y", 2),
            ( "QToolButton", 4,  "TRANSZ",
              "ui/actions/Properties Manager/TranslateZ.png", "", "Z", 3),
            ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",
              "ui/actions/Properties Manager/translate+rotate-A.png", "", \
              "A", 4)

            ]

        self.freeDragTranslateButtonGroup = \
            PM_ToolButtonRow( inPmGroupBox,
                               title        = "",
                               buttonList   = BUTTON_LIST,
                               checkedId    = 1,
                               setAsDefault = True,
                               )
        self.transFreeButton = self.freeDragTranslateButtonGroup.getButtonById(
            1)
        self.transXButton = self.freeDragTranslateButtonGroup.getButtonById(2)
        self.transYButton = self.freeDragTranslateButtonGroup.getButtonById(3)
        self.transZButton = self.freeDragTranslateButtonGroup.getButtonById(4)
        self.transAlongAxisButton = \
            self.freeDragTranslateButtonGroup.getButtonById(5)

        self.moveFromToButton = PM_ToolButton(
                    inPmGroupBox,
                    text = "Translate from/to",
                    iconPath  = "ui/actions/Properties Manager"\
                    "/Translate_Components.png",
                    spanWidth = True

                    )
        self.moveFromToButton.setCheckable(True)
        self.moveFromToButton.setAutoRaise(True)
        self.moveFromToButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)


        self.startCoordLineEdit = PM_LineEdit(
            inPmGroupBox,
            label        = "ui/actions/Properties Manager"\
                    "/Move_Start_Point.png",
            text         = "Define 'from' and 'to' points",
            setAsDefault = False,
            )
        self.startCoordLineEdit.setReadOnly(True)
        self.startCoordLineEdit.setEnabled(False)
    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")  

        self.saveSequenceButton = PM_ToolButton(
            self, 
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png") 

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        editDirectionChoices = ["5' to 3'", "3' to 5'"]
        self.baseDirectionChoiceComboBox = \
            PM_ComboBox( self,
                         choices = editDirectionChoices,
                         index     = 0, 
                         spanWidth = False )   

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done -- 

        # When the widget type starts with the word 'PM_' , the 
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned 
        # labels (and if they were recreated as mentioned above), 
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2] 
        #Cleanup in PM_widgetGrid could be to check if the widget starts with 
        #'Q'  instead of 'PM_' 


        #Widgets to include in the widget row. 
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Sequence direction:", 2),
                      ('PM_ComboBox',  self.baseDirectionChoiceComboBox , 3),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8), 
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10), 
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )
class Ui_MovePropertyManager(Command_PropertyManager):

    # The title that appears in the Property Manager header
    title = "Move"
    # The name of this Property Manager. This will be set to
    # the name of the PM_Dialog object via setObjectName().
    pmName = title
    # The relative path to the PNG file that appears in the header
    iconPath = "ui/actions/Properties Manager/Translate_Components.png"

    # The title(s) that appear in the property manager header.
    # (these are changed depending on the active group box)
    translateTitle = "Translate"
    rotateTitle = "Rotate"

    # The full path to PNG file(s) that appears in the header.
    translateIconPath = "ui/actions/Properties Manager/Translate_Components.png"
    rotateIconPath = "ui/actions/Properties Manager/Rotate_Components.png"

    def __init__(self, command):
        _superclass.__init__(self, command)
        self.showTopRowButtons(PM_DONE_BUTTON | PM_WHATS_THIS_BUTTON)

    def _addGroupBoxes(self):
        """
        Add groupboxes to the Property Manager dialog. 
        """

        self.translateGroupBox = PM_GroupBox(self,
                                             title="Translate",
                                             connectTitleButton=False)
        self.translateGroupBox.titleButton.setShortcut('T')
        self._loadTranslateGroupBox(self.translateGroupBox)

        self.rotateGroupBox = PM_GroupBox(self,
                                          title="Rotate",
                                          connectTitleButton=False)
        self.rotateGroupBox.titleButton.setShortcut('R')
        self._loadRotateGroupBox(self.rotateGroupBox)

        self.translateGroupBox.collapse()
        self.rotateGroupBox.collapse()

    # == Begin Translate Group Box =====================

    def _loadTranslateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Translate group box.
        @param inPmGroupBox: The Translate group box in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        translateChoices = ["Free Drag", "By Delta XYZ", "To XYZ Position"]

        self.translateComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = '',
                         choices      = translateChoices,
                         index        = 0,
                         setAsDefault = False,
                         spanWidth    = True )

        self.freeDragTranslateGroupBox = PM_GroupBox(inPmGroupBox)
        self._loadFreeDragTranslateGroupBox(self.freeDragTranslateGroupBox)

        self.byDeltaGroupBox = PM_GroupBox(inPmGroupBox)
        self._loadByDeltaGroupBox(self.byDeltaGroupBox)

        self.toPositionGroupBox = PM_GroupBox(inPmGroupBox)
        self._loadToPositionGroupBox(self.toPositionGroupBox)

        self.updateTranslateGroupBoxes(0)

    def _loadFreeDragTranslateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Free Drag Translate group box, which is present 
        within the Translate groupbox.
        @param inPmGroupBox: The Free Drag Translate group box in the Translate 
                             group box. 
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column


        BUTTON_LIST = [
            ( "QToolButton", 1,  "MOVEDEFAULT",
              "ui/actions/Properties Manager/Move_Free.png", "", "F", 0),
            ( "QToolButton", 2,  "TRANSX",
              "ui/actions/Properties Manager/TranslateX.png", "", "X", 1),
            ( "QToolButton", 3,  "TRANSY",
              "ui/actions/Properties Manager/TranslateY.png", "", "Y", 2),
            ( "QToolButton", 4,  "TRANSZ",
              "ui/actions/Properties Manager/TranslateZ.png", "", "Z", 3),
            ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",
              "ui/actions/Properties Manager/translate+rotate-A.png", "", \
              "A", 4)

            ]

        self.freeDragTranslateButtonGroup = \
            PM_ToolButtonRow( inPmGroupBox,
                               title        = "",
                               buttonList   = BUTTON_LIST,
                               checkedId    = 1,
                               setAsDefault = True,
                               )
        self.transFreeButton = self.freeDragTranslateButtonGroup.getButtonById(
            1)
        self.transXButton = self.freeDragTranslateButtonGroup.getButtonById(2)
        self.transYButton = self.freeDragTranslateButtonGroup.getButtonById(3)
        self.transZButton = self.freeDragTranslateButtonGroup.getButtonById(4)
        self.transAlongAxisButton = \
            self.freeDragTranslateButtonGroup.getButtonById(5)

        self.moveFromToButton = PM_ToolButton(
                    inPmGroupBox,
                    text = "Translate from/to",
                    iconPath  = "ui/actions/Properties Manager"\
                    "/Translate_Components.png",
                    spanWidth = True

                    )
        self.moveFromToButton.setCheckable(True)
        self.moveFromToButton.setAutoRaise(True)
        self.moveFromToButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)


        self.startCoordLineEdit = PM_LineEdit(
            inPmGroupBox,
            label        = "ui/actions/Properties Manager"\
                    "/Move_Start_Point.png",
            text         = "Define 'from' and 'to' points",
            setAsDefault = False,
            )
        self.startCoordLineEdit.setReadOnly(True)
        self.startCoordLineEdit.setEnabled(False)

    def _loadByDeltaGroupBox(self, inPmGroupBox):
        """
        Load widgets in the translate By Delta group box, which is present 
        within the Translate groupbox.
        @param inPmGroupBox: The Translate By Delta group box in the translate 
                             group box. 
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.moveDeltaXSpinBox = \
            PM_DoubleSpinBox(
                inPmGroupBox,
                label        = "ui/actions/Properties Manager/Delta_X.png",
                value        = 0.0,
                setAsDefault = True,
                minimum      = -100.0,
                maximum      =  100.0,
                singleStep   = 1.0,
                decimals     = 3,
                suffix       = ' Angstroms',
                spanWidth    = False )

        self.moveDeltaYSpinBox = \
            PM_DoubleSpinBox(
                inPmGroupBox,
                label        = "ui/actions/Properties Manager/Delta_Y.png",
                value        = 0.0,
                setAsDefault = True,
                minimum      = -100.0,
                maximum      =  100.0,
                singleStep   = 1.0,
                decimals     = 3,
                suffix       = ' Angstroms',
                spanWidth    = False )

        self.moveDeltaZSpinBox = \
            PM_DoubleSpinBox(
                inPmGroupBox,
                label        = "ui/actions/Properties Manager/Delta_Z.png",
                value        = 0.0,
                setAsDefault = True,
                minimum      = -100.0,
                maximum      =  100.0,
                singleStep   = 1.0,
                decimals     = 3,
                suffix       = ' Angstroms',
                spanWidth    = False )

        DELTA_BUTTONS = [
            ("QToolButton", 1, "Delta Plus",
             "ui/actions/Properties Manager/Move_Delta_Plus.png", "", "+", 0),
            ("QToolButton", 2, "Delta Minus",
             "ui/actions/Properties Manager/Move_Delta_Minus.png", "", "-", 1)
        ]

        self.translateDeltaButtonRow = \
            PM_ToolButtonRow( inPmGroupBox,
                              title        = "",
                              buttonList   = DELTA_BUTTONS,
                              label        = 'Translate:',
                              isAutoRaise  =  True,
                              isCheckable  =  False
                            )
        self.transDeltaPlusButton = \
            self.translateDeltaButtonRow.getButtonById(1)
        self.transDeltaMinusButton = \
            self.translateDeltaButtonRow.getButtonById(2)

    def _loadToPositionGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Translate To a given Position group box, which is 
        present within the Translate groupbox.
        @param inPmGroupBox: Translate To Position group box in the Translate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        self.toPositionspinboxes = PM_CoordinateSpinBoxes(inPmGroupBox)

        self.moveXSpinBox = self.toPositionspinboxes.xSpinBox
        self.moveYSpinBox = self.toPositionspinboxes.ySpinBox
        self.moveZSpinBox = self.toPositionspinboxes.zSpinBox


        self.moveAbsoluteButton = \
            PM_PushButton( inPmGroupBox,
                           label     = "",
                           text      = "Move Selection",
                           spanWidth = True )

    # == Begin Rotate Group Box =====================
    def _loadRotateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Rotate group box, 
        @param inPmGroupBox: The Rotate GroupBox in the PM
        @type  inPmGroupBox: L{PM_GroupBox}
        """

        rotateChoices = ["Free Drag", "By Specified Angle"]

        self.rotateComboBox = \
            PM_ComboBox( inPmGroupBox,
                         label        = '',
                         choices      = rotateChoices,
                         index        = 0,
                         setAsDefault = False,
                         spanWidth    = True )

        self.rotateAsUnitCB = \
            PM_CheckBox( inPmGroupBox,
                         text         = 'Rotate as a unit' ,
                         widgetColumn = 0,
                         state        = Qt.Checked )

        self.freeDragRotateGroupBox = PM_GroupBox(inPmGroupBox)
        self._loadFreeDragRotateGroupBox(self.freeDragRotateGroupBox)

        self.bySpecifiedAngleGroupBox = PM_GroupBox(inPmGroupBox)
        self._loadBySpecifiedAngleGroupBox(self.bySpecifiedAngleGroupBox)

        self.updateRotateGroupBoxes(0)

    def _loadFreeDragRotateGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Free Drag Rotate group box, which is 
        present within the Rotate groupbox.
        @param inPmGroupBox: The Free Drag Rotate group box in the Rotate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column

        BUTTON_LIST = [
            ( "QToolButton", 1,  "ROTATEDEFAULT",
              "ui/actions/Properties Manager/Rotate_Free.png", "", "F", 0 ),

            ( "QToolButton", 2,  "ROTATEX",
              "ui/actions/Properties Manager/RotateX.png", "", "X", 1 ),

            ( "QToolButton", 3,  "ROTATEY",
              "ui/actions/Properties Manager/RotateY.png", "", "Y", 2 ),

            ( "QToolButton", 4,  "ROTATEZ",
              "ui/actions/Properties Manager/RotateZ.png", "", "Z", 3 ),

            ( "QToolButton", 5,  "ROT_TRANS_ALONG_AXIS",
              "ui/actions/Properties Manager/translate+rotate-A.png", "", \
              "A", 4 )

            ]

        self.freeDragRotateButtonGroup = \
            PM_ToolButtonRow( inPmGroupBox,
                               title        = "",
                               buttonList   = BUTTON_LIST,
                               spanWidth = True,
                               checkedId    = 1,
                               setAsDefault = True,
                            )

        self.rotateFreeButton = self.freeDragRotateButtonGroup.getButtonById(1)
        self.rotateXButton = self.freeDragRotateButtonGroup.getButtonById(2)
        self.rotateYButton = self.freeDragRotateButtonGroup.getButtonById(3)
        self.rotateZButton = self.freeDragRotateButtonGroup.getButtonById(4)
        self.rotAlongAxisButton = \
            self.freeDragRotateButtonGroup.getButtonById(5)

        inPmGroupBox.setStyleSheet(
            self.freeDragRotateButtonGroup._getStyleSheet())

        X_ROW_LABELS = [("QLabel", "Delta Theta X:", 0), ("QLabel", "", 1),
                        ("QLabel", "0.00", 2), ("QLabel", "Degrees", 3)]

        Y_ROW_LABELS = [("QLabel", "Delta Theta Y:", 0), ("QLabel", "", 1),
                        ("QLabel", "0.00", 2), ("QLabel", "Degrees", 3)]

        Z_ROW_LABELS = [("QLabel", "Delta Theta Z:", 0), ("QLabel", "", 1),
                        ("QLabel", "0.00", 2), ("QLabel", "Degrees", 3)]

        self.rotateXLabelRow = PM_LabelRow(inPmGroupBox,
                                           title="",
                                           labelList=X_ROW_LABELS)
        self.deltaThetaX_lbl = self.rotateXLabelRow.labels[2]

        self.rotateYLabelRow = PM_LabelRow(inPmGroupBox,
                                           title="",
                                           labelList=Y_ROW_LABELS)
        self.deltaThetaY_lbl = self.rotateYLabelRow.labels[2]

        self.rotateZLabelRow = PM_LabelRow(inPmGroupBox,
                                           title="",
                                           labelList=Z_ROW_LABELS)
        self.deltaThetaZ_lbl = self.rotateZLabelRow.labels[2]

        self.rotateAboutPointButton = PM_ToolButton(
                    inPmGroupBox,
                    text = "Rotate selection about a point",
                    iconPath  = "ui/actions/Properties Manager"\
                    "/Rotate_Components.png",
                    spanWidth = True
                    )
        self.rotateAboutPointButton.setCheckable(True)
        self.rotateAboutPointButton.setAutoRaise(True)
        self.rotateAboutPointButton.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)


        self.rotateStartCoordLineEdit = PM_LineEdit(
            inPmGroupBox,
            label        = "ui/actions/Properties Manager"\
                    "/Move_Start_Point.png",
            text         = "Define 3 points",
            setAsDefault = False,
            )
        self.rotateStartCoordLineEdit.setReadOnly(True)
        self.rotateStartCoordLineEdit.setEnabled(False)

    def _loadBySpecifiedAngleGroupBox(self, inPmGroupBox):
        """
        Load widgets in the Rotate By Specified Angle group box, which is 
        present within the Rotate groupbox.
        @param inPmGroupBox: Rotate By Specified Angle group box in the Rotate 
                             group box.
        @type  inPmGroupBox: L{PM_GroupBox}
        """
        # Button list to create a toolbutton row.
        # Format:
        # - buttonId,
        # - buttonText ,
        # - iconPath
        # - tooltip
        # - shortcut
        # - column

        BUTTON_LIST = [
            ("QToolButton", 1, "ROTATEX",
             "ui/actions/Properties Manager/RotateX.png",
             "Rotate about X axis", "X", 0),
            ("QToolButton", 2, "ROTATEY",
             "ui/actions/Properties Manager/RotateY.png",
             "Rotate about Y axis", "Y", 1),
            ("QToolButton", 3, "ROTATEZ",
             "ui/actions/Properties Manager/RotateZ.png",
             "Rotate about Z axis", "Z", 2),
        ]



        self.rotateAroundAxisButtonRow = \
            PM_ToolButtonRow( inPmGroupBox,
                              title        = "",
                              buttonList   = BUTTON_LIST,
                              alignment    = 'Right',
                              label        = 'Rotate Around:'
                            )
        self.rotXaxisButton = \
            self.rotateAroundAxisButtonRow.getButtonById(1)

        self.rotYaxisButton = \
            self.rotateAroundAxisButtonRow.getButtonById(2)

        self.rotZaxisButton = \
            self.rotateAroundAxisButtonRow.getButtonById(3)



        self.rotateThetaSpinBox = \
            PM_DoubleSpinBox(inPmGroupBox,
                             label        = "Rotate By:",
                             value        = 0.0,
                             setAsDefault = True,
                             minimum      = 0,
                             maximum      = 360.0,
                             singleStep   = 1.0,
                             decimals     = 2,
                             suffix       = ' Degrees')

        THETA_BUTTONS = [
            ("QToolButton", 1, "Theta Plus",
             "ui/actions/Properties Manager/Move_Theta_Plus.png", "", "+", 0),
            ("QToolButton", 2, "Theta Minus",
             "ui/actions/Properties Manager/Move_Theta_Minus.png", "", "-", 1)
        ]

        self.rotateThetaButtonRow = \
            PM_ToolButtonRow( inPmGroupBox,
                              title        = "",
                              buttonList   = THETA_BUTTONS,
                              label        = 'Direction:',
                              isAutoRaise  =  True,
                              isCheckable  =  False
                            )
        self.rotateThetaPlusButton = self.rotateThetaButtonRow.getButtonById(1)
        self.rotateThetaMinusButton = self.rotateThetaButtonRow.getButtonById(
            2)

    # == End Rotate Group Box =====================

    # == Slots for Translate group box
    def _hideAllTranslateGroupBoxes(self):
        """
        Hides all Translate group boxes.
        """
        self.toPositionGroupBox.hide()
        self.byDeltaGroupBox.hide()
        self.freeDragTranslateGroupBox.hide()

    def updateTranslateGroupBoxes(self, id):
        """
        Update the translate group boxes displayed based on the translate
        option selected.
        @param id: Integer value corresponding to the combobox item in the 
                   Translate group box. 
        @type  id: int
        """
        self._hideAllTranslateGroupBoxes()

        if id is 0:
            self.freeDragTranslateGroupBox.show()

        if id is 1:
            self.byDeltaGroupBox.show()
        if id is 2:
            self.toPositionGroupBox.show()

        self.updateMessage()

    def changeMoveOption(self, button):
        """
        Subclasses should reimplement this method.
        
        @param button: QToolButton that decides the type of translate operation 
        to be set.
        @type  button: QToolButton 
                       L{http://doc.trolltech.com/4.2/qtoolbutton.html}
        @see: B{MovePropertyManager.changeMoveOption} which overrides this
              method
        """
        pass

    # == Slots for Rotate group box
    def updateRotateGroupBoxes(self, id):
        """
        Update the translate group boxes displayed based on the translate
        option selected.
        @param id: Integer value corresponding to the combobox item in the 
                   Rotate group box. 
        @type  id: int
        """
        if id is 0:
            self.bySpecifiedAngleGroupBox.hide()
            self.freeDragRotateGroupBox.show()
        if id is 1:
            self.freeDragRotateGroupBox.hide()
            self.bySpecifiedAngleGroupBox.show()

        self.updateMessage()

    def changeRotateOption(self, button):
        """
        Subclasses should reimplement this method. 
        
        @param button: QToolButton that decides the type of rotate operation 
        to be set.
        @type  button: QToolButton 
                       L{http://doc.trolltech.com/4.2/qtoolbutton.html}
        @see: B{MovePropertyManage.changeRotateOption} which overrides this 
             method
        """
        pass

    def _addWhatsThisText(self):
        """
        What's This text for some of the widgets in this Property Manager.  

        """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_MovePropertyManager
        whatsThis_MovePropertyManager(self)

    def _addToolTipText(self):
        """
        Tool Tip text for widgets in this Property Manager.  
        """
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_MovePropertyManager
        ToolTip_MovePropertyManager(self)
class Ui_DnaSequenceEditor(PM_DockWidget):
    """
    The Ui_DnaSequenceEditor class defines UI elements for the Sequence Editor
    object. The sequence editor is usually visible while in DNA edit mode.
    It is a DockWidget that is doced at the bottom of the MainWindow
    """
    _title         =  "Sequence Editor"
    _groupBoxCount = 0
    _lastGroupBox = None

    def __init__(self, win):
        """
        Constructor for the Ui_DnaSequenceEditor 
        @param win: The parentWidget (MainWindow) for the sequence editor 
        """
        
        self.win = win
        # Should parentWidget for a docwidget always be win? 
        #Not necessary but most likely it will be the case.        
        parentWidget = win 
        
        _superclass.__init__(self, parentWidget, title = self._title)
        
        #A flag used to restore the state of the Reports dock widget 
        #(which can be accessed through View  >  Reports) see self.show() and
        #self.closeEvent() for more details. 
        self._reportsDockWidget_closed_in_show_method = False
        self.setFixedHeight(90)

    def show(self):
        """
        Shows the sequence editor. While doing this, it also closes the reports
        dock widget (if visible) the state of the reports dockwidget will be
        restored when the sequence editor is closed. 
        @see:self.closeEvent()
        """
        self._reportsDockWidget_closed_in_show_method = False
        #hide the history widget first
        #(It will be shown back during self.close)
        #The history widget is hidden or shown only when both 
        # 'View > Full Screen' and View > Semi Full Screen actions 
        # are *unchecked*
        #Thus show or close methods won't do anything to history widget
        # if either of the above mentioned actions is checked.
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self.win.reportsDockWidget.isVisible():
                self.win.reportsDockWidget.close()
                self._reportsDockWidget_closed_in_show_method = True

        _superclass.show(self)  
        
    def closeEvent(self, event):
        """
        Overrides close event. Makes sure that the visible state of the reports
        widgetis restored when the sequence editor is closed. 
        @see: self.show()
        """
        _superclass.closeEvent(self, event)
       
        if self.win.viewFullScreenAction.isChecked() or \
           self.win.viewSemiFullScreenAction.isChecked():
            pass
        else:
            if self._reportsDockWidget_closed_in_show_method:
                self.win.viewReportsAction.setChecked(True) 
                self._reportsDockWidget_closed_in_show_method = False

    def _loadWidgets(self):
        """
        Overrides PM.PM_DockWidget._loadWidgets. Loads the widget in this
        dockwidget.
        """
        self._loadMenuWidgets()
        self._loadTextEditWidget()


    def _loadMenuWidgets(self):
        """
        Load the various menu widgets (e.g. Open, save sequence options, 
        Find and replace widgets etc. 
        """
        #Note: Find and replace widgets might be moved to their own class.

        self.loadSequenceButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Open.png")  

        self.saveSequenceButton = PM_ToolButton(
            self, 
            iconPath = "ui/actions/Properties Manager/Save_Strand_Sequence.png") 

        self.loadSequenceButton.setAutoRaise(True)
        self.saveSequenceButton.setAutoRaise(True)

        editDirectionChoices = ["5' to 3'", "3' to 5'"]
        self.baseDirectionChoiceComboBox = \
            PM_ComboBox( self,
                         choices = editDirectionChoices,
                         index     = 0, 
                         spanWidth = False )   

        #Find and replace widgets --
        self.findLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.findLineEdit.setMaximumWidth(60)


        self.replaceLineEdit = \
            PM_LineEdit( self, 
                         label        = "",
                         spanWidth    = False)
        self.replaceLineEdit.setMaximumWidth(60)

        self.findOptionsToolButton = PM_ToolButton(self)
        self.findOptionsToolButton.setMaximumWidth(12)
        self.findOptionsToolButton.setAutoRaise(True)

        self.findOptionsToolButton.setPopupMode(QToolButton.MenuButtonPopup)

        self._setFindOptionsToolButtonMenu()

        self.findNextToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Next.png")
        self.findNextToolButton.setAutoRaise(True)

        self.findPreviousToolButton = PM_ToolButton(
            self,
            iconPath = "ui/actions/Properties Manager/Find_Previous.png")
        self.findPreviousToolButton.setAutoRaise(True)

        self.replacePushButton = PM_PushButton(self, text = "Replace")

        self.warningSign = QLabel(self)
        self.warningSign.setPixmap(
            getpixmap('ui/actions/Properties Manager/Warning.png'))
        self.warningSign.hide()

        self.phraseNotFoundLabel = QLabel(self)
        self.phraseNotFoundLabel.setText("Sequence Not Found")
        self.phraseNotFoundLabel.hide()

        # NOTE: Following needs cleanup in the PM_WidgetRow/ PM_WidgetGrid
        # but this explanation is sufficient  until thats done -- 

        # When the widget type starts with the word 'PM_' , the 
        # PM_WidgetRow treats it as a well defined widget and thus doesn't try
        # to create a QWidget object (or its subclasses)
        # This is the reason why qLabels such as self.warningSign and
        # self.phraseNotFoundLabel  are defined as PM_Labels and not 'QLabels'
        # If they were defined as 'QLabel'(s) then PM_WidgetRow would have
        # recreated the label. Since we want to show/hide the above mentioned 
        # labels (and if they were recreated as mentioned above), 
        # we would have needed to define  those something like this:
        # self.phraseNotFoundLabel = widgetRow._widgetList[-2] 
        #Cleanup in PM_widgetGrid could be to check if the widget starts with 
        #'Q'  instead of 'PM_' 


        #Widgets to include in the widget row. 
        widgetList = [('PM_ToolButton', self.loadSequenceButton, 0),
                      ('PM_ToolButton', self.saveSequenceButton, 1),
                      ('QLabel', "     Sequence direction:", 2),
                      ('PM_ComboBox',  self.baseDirectionChoiceComboBox , 3),
                      ('QLabel', "     Find:", 4),
                      ('PM_LineEdit', self.findLineEdit, 5),
                      ('PM_ToolButton', self.findOptionsToolButton, 6),
                      ('PM_ToolButton', self.findPreviousToolButton, 7),
                      ('PM_ToolButton', self.findNextToolButton, 8), 
                      ('QLabel', "     Replace:", 9),
                      ('PM_TextEdit', self.replaceLineEdit, 10), 
                      ('PM_PushButton', self.replacePushButton, 11),
                      ('PM_Label', self.warningSign, 12),
                      ('PM_Label', self.phraseNotFoundLabel, 13),
                      ('QSpacerItem', 5, 5, 14) ]

        widgetRow = PM_WidgetRow(self,
                                 title     = '',
                                 widgetList = widgetList,
                                 label = "",
                                 spanWidth = True )

    def _loadTextEditWidget(self):
        """
        Load the SequenceTexteditWidgets.         
        """        
        self.sequenceTextEdit = \
            PM_TextEdit( self, 
                         label = " Sequence: ", 
                         spanWidth = False,
                         permit_enter_keystroke = False)        
        self.sequenceTextEdit.setCursorWidth(2)
        self.sequenceTextEdit.setWordWrapMode( QTextOption.WrapAnywhere )
        self.sequenceTextEdit.setFixedHeight(20)

        #The StrandSequence 'Mate' it is a read only etxtedit that shows
        #the complementary strand sequence.         
        self.sequenceTextEdit_mate = \
            PM_TextEdit(self, 
                        label = "", 
                        spanWidth = False,
                        permit_enter_keystroke = False
                        )        
        palette = getPalette(None, 
                             QPalette.Base, 
                             sequenceEditStrandMateBaseColor)
        self.sequenceTextEdit_mate.setPalette(palette)        
        self.sequenceTextEdit_mate.setFixedHeight(20)
        self.sequenceTextEdit_mate.setReadOnly(True)
        self.sequenceTextEdit_mate.setWordWrapMode(QTextOption.WrapAnywhere)

        #Important to make sure that the horizontal and vertical scrollbars 
        #for these text edits are never displayed. 
        for textEdit in (self.sequenceTextEdit, self.sequenceTextEdit_mate):
            textEdit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            textEdit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)


    def _getFindLineEditStyleSheet(self):
        """
        Return the style sheet for the findLineEdit. This sets the following 
        properties only:
         - background-color

        This style is set whenever the searchStrig can't be found (sets
        a light red color background to the lineedit when this happens)   

        @return: The line edit style sheet.
        @rtype:  str

        """
        styleSheet = \
                   "QLineEdit {\
                   background-color: rgb(255, 102, 102)\
                   }"
        #Not used:
        #  background-color: rgb(217, 255, 216)\       

        return styleSheet

    def _setFindOptionsToolButtonMenu(self):
        """
        Sets the menu for the findOptionstoolbutton that appears a small 
        menu button next to the findLineEdit.
        """
        self.findOptionsMenu = QMenu(self.findOptionsToolButton)

        self.caseSensitiveFindAction = QAction(self.findOptionsToolButton)
        self.caseSensitiveFindAction.setText('Match Case')
        self.caseSensitiveFindAction.setCheckable(True)
        self.caseSensitiveFindAction.setChecked(False)

        self.findOptionsMenu.addAction(self.caseSensitiveFindAction)
        self.findOptionsMenu.addSeparator()

        self.findOptionsToolButton.setMenu(self.findOptionsMenu)

    def _addToolTipText(self):
        """
            What's Tool Tip text for widgets in this Property Manager.  
            """ 
        from ne1_ui.ToolTipText_for_PropertyManagers import ToolTip_SequenceEditor
        ToolTip_SequenceEditor(self)

    def _addWhatsThisText(self):
        """
            What's This text for widgets in this Property Manager.  

            """
        from ne1_ui.WhatsThisText_for_PropertyManagers import whatsThis_SequenceEditor
        whatsThis_SequenceEditor(self)
class OrderDna_PropertyManager(Command_PropertyManager):
    """
    The OrderDna_PropertyManager class provides a Property Manager 
    for the B{Order Dna} command on the flyout toolbar in the 
    Build > Dna mode. 

    @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 = "Order DNA"
    pmName = title
    iconPath = "ui/actions/Command Toolbar/BuildDna/OrderDna.png"

    def __init__(self, command):
        """
        Constructor for the property manager.
        """

        _superclass.__init__(self, command)

        self.assy = self.win.assy

        self.showTopRowButtons( PM_DONE_BUTTON | \
                                PM_WHATS_THIS_BUTTON)

        self.update_includeStrands()  # Updates the message box.
        return

    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.viewDnaOrderFileButton, SIGNAL("clicked()"),
                       self.viewDnaOrderFile)

        change_connect(self.includeStrandsComboBox, SIGNAL("activated(int)"),
                       self.update_includeStrands)
        return

    def _addGroupBoxes(self):
        """
        Add the Property Manager group boxes.
        """
        self._pmGroupBox1 = PM_GroupBox(self, title="Options")
        self._loadGroupBox1(self._pmGroupBox1)

    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """

        includeStrandsChoices = [
            "All strands in model", "Selected strands only"
        ]

        self.includeStrandsComboBox  = \
            PM_ComboBox( pmGroupBox,
                         label         =  "Include strands:",
                         choices       =  includeStrandsChoices,
                         setAsDefault  =  True)

        self.numberOfBasesLineEdit  = \
            PM_LineEdit( pmGroupBox,
                         label  =  "Total nucleotides:",
                         text   = str(self.getNumberOfBases()))
        self.numberOfBasesLineEdit.setEnabled(False)

        self.numberOfXBasesLineEdit  = \
            PM_LineEdit( pmGroupBox,
                         label  =  "Unassigned:",
                         text   = str(self.getNumberOfBases(unassignedOnly = True)))
        self.numberOfXBasesLineEdit.setEnabled(False)

        self.viewDnaOrderFileButton = \
            PM_PushButton( pmGroupBox,
                           label     = "",
                           text      = "View DNA Order File...",
                           spanWidth = True)
        return

    def _addWhatsThisText(self):
        """
        What's This text for widgets in this Property Manager.
        """
        whatsThis_OrderDna_PropertyManager(self)
        return

    def _addToolTipText(self):
        """
        Tool Tip text for widgets in the DNA Property Manager.  
        """
        pass

    # Ask Bruce where this should live (i.e. class Part?) --Mark
    def getAllDnaStrands(self, selectedOnly=False):
        """
        Returns a list of all the DNA strands in the current part, or only
        the selected strands if I{selectedOnly} is True.
        
        @param selectedOnly: If True, return only the selected DNA strands.
        @type  selectedOnly: bool
        """

        dnaStrandList = []

        def func(node):
            if isinstance(node, DnaStrand):
                if selectedOnly:
                    if node.picked:
                        dnaStrandList.append(node)
                else:
                    dnaStrandList.append(node)

        self.win.assy.part.topnode.apply2all(func)

        return dnaStrandList

    def getNumberOfBases(self, selectedOnly=False, unassignedOnly=False):
        """
        Returns the number of bases count for all the DNA strands in the 
        current part, or only the selected strand if I{selectedOnly} is True.
        
        @param selectedOnly: If True, return only the number of bases in the
                             selected DNA strands.
        @type  selectedOnly: bool
        
        @param unassignedOnly: If True, return only the number of unassigned
                               bases (i.e. base letters = X).
        @type  unassignedOnly: bool
        """
        dnaSequenceString = ''
        selectedOnly = self.includeStrandsComboBox.currentIndex()
        strandList = self.getAllDnaStrands(selectedOnly)

        for strand in strandList:
            strandSequenceString = str(strand.getStrandSequence())
            dnaSequenceString += strandSequenceString

        if unassignedOnly:
            return dnaSequenceString.count("X")

        return len(dnaSequenceString)

    def _update_UI_do_updates(self):
        """
        Overrides superclass method.
        """
        self.update_includeStrands()
        return

    def getDnaSequence(self, format='CSV'):
        """
        Return the complete Dna sequence information string (i.e. all strand 
        sequences) in the specified format. 
        
        @return: The Dna sequence string
        @rtype: string
        
        """
        if format == 'CSV':  #comma separated values.
            separator = ','

        dnaSequenceString = ''
        selectedOnly = self.includeStrandsComboBox.currentIndex()
        strandList = self.getAllDnaStrands(selectedOnly)

        for strand in strandList:
            dnaSequenceString = dnaSequenceString + strand.name + separator
            strandSequenceString = str(strand.getStrandSequence())
            if strandSequenceString:
                strandSequenceString = strandSequenceString.upper()
                strandLength = str(len(strandSequenceString)) + separator
                dnaSequenceString = dnaSequenceString + strandLength + strandSequenceString

            dnaSequenceString = dnaSequenceString + "\n"

        return dnaSequenceString

    def viewDnaOrderFile(self, openFileInEditor=True):
        """
        Writes a DNA Order file in comma-separated values (CSV) format 
        and opens it in a text editor.

        The user must save the file to a permanent location using the 
        text editor.

        @see: Ui_DnaFlyout.orderDnaCommand
        @see: writeDnaOrderFile()
        @TODO: assy.getAllDnaObjects(). 
        """
        dnaSequence = self.getDnaSequence(format='CSV')

        if dnaSequence:
            tmpdir = find_or_make_Nanorex_subdir('temp')
            fileBaseName = 'DnaOrder'
            temporaryFile = os.path.join(tmpdir, "%s.csv" % fileBaseName)
            writeDnaOrderFile(temporaryFile, self.assy,
                              self.getNumberOfBases(),
                              self.getNumberOfBases(unassignedOnly=True),
                              dnaSequence)

            if openFileInEditor:
                open_file_in_editor(temporaryFile)

        return

    def update_includeStrands(self, ignoreVal=0):
        """
        Slot method for "Include (strands)" combobox.
        """

        idx = self.includeStrandsComboBox.currentIndex()

        includeType = ["model", "selection"]

        _numberOfBases = self.getNumberOfBases()
        self.numberOfBasesLineEdit.setText(str(_numberOfBases) + " bases")

        _numberOfXBases = self.getNumberOfBases(unassignedOnly=True)
        self.numberOfXBasesLineEdit.setText(str(_numberOfXBases) + " bases")

        # Make the background color red if there are any unassigned bases.
        if _numberOfXBases:
            self.numberOfXBasesLineEdit.setStyleSheet(\
                "QLineEdit {"\
                "background-color: rgb(255, 0, 0)"\
                "}")
        else:
            self.numberOfXBasesLineEdit.setStyleSheet(\
                "QLineEdit {"\
                "background-color: rgb(255, 255, 255)"\
                "}")

        if _numberOfBases > 0:
            self.viewDnaOrderFileButton.setEnabled(True)
            msg = "Click on <b>View DNA Order File...</b> to preview a " \
                "DNA order for all DNA strands in the current %s." \
                % includeType[idx]
        else:
            self.viewDnaOrderFileButton.setEnabled(False)
            msg = "<font color=red>" \
                "There are no DNA strands in the current %s." \
                % includeType[idx]

        self.updateMessage(msg)
        return
class CompareProteins_PropertyManager(Command_PropertyManager):
    """
    The CompareProteins_PropertyManager class provides a Property Manager
    for the B{Compare Proteins} command on the Build Protein flyout toolbar.

    @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

    @ivar proteinChunk1: The first currently selected protein to be compared.
    @type proteinChunk1: protein chunk

    @ivar proteinChunk2: The second currently selected protein to be compared.
    @type proteinChunk2: protein chunk
    """

    title = "Compare Proteins"
    pmName = title
    iconPath = "ui/actions/Command Toolbar/BuildProtein/Compare.png"
    proteinChunk1 = None
    proteinChunk2 = None

    def __init__(self, command):
        """
        Constructor for the property manager.
        """
        self.threshold = 10.0

        _superclass.__init__(self, command)

        self.showTopRowButtons(PM_DONE_BUTTON | PM_WHATS_THIS_BUTTON)
        return

    def connect_or_disconnect_signals(self, isConnect=True):

        if isConnect:
            change_connect = self.win.connect
        else:
            change_connect = self.win.disconnect

        change_connect(self.comparePushButton, SIGNAL("clicked()"), self._compareProteins)

        change_connect(self.thresholdDoubleSpinBox, SIGNAL("valueChanged(double)"), self._thresholdChanged)

        change_connect(self.hidePushButton, SIGNAL("clicked()"), self._hideDifferences)
        return

    def close(self):
        """
        Closes the Property Manager. Overrides EditCommand_PM.close()
        """

        env.history.statusbar_msg("")
        self._resetAminoAcids()
        _superclass.close(self)

        # Restore the original global display style.
        self.o.setGlobalDisplayStyle(self.originalDisplayStyle)
        return

    def show(self):
        """
        Show the PM. Extends superclass method.

        @note: _update_UI_do_updates() gets called immediately after this and
               updates PM widgets with their correct values/settings.
        """
        _superclass.show(self)
        env.history.statusbar_msg("")

        # Force the Global Display Style to "Protein" since this is the only way
        # to see comparisons. The global display style will be restored when leaving
        # this command (via self.close()).
        self.originalDisplayStyle = self.o.displayMode
        self.o.setGlobalDisplayStyle(diPROTEIN)
        return

    def _addGroupBoxes(self):
        """
        Add the Property Manager group boxes.
        """
        self._pmGroupBox1 = PM_GroupBox(self, title="Compare")
        self._loadGroupBox1(self._pmGroupBox1)
        return

    def _addWhatsThisText(self):
        """
        What's This text for widgets in this Property Manager.
        """
        pass

    def _addToolTipText(self):
        """
        Tool Tip text for widgets in this Property Manager.
        """
        pass

    def _loadGroupBox1(self, pmGroupBox):
        """
        Load widgets in group box.
        """

        self.structure1LineEdit = PM_LineEdit(pmGroupBox, label="First structure:", setAsDefault=False)
        self.structure1LineEdit.setEnabled(False)

        self.structure2LineEdit = PM_LineEdit(pmGroupBox, label="Second structure:", setAsDefault=False)
        self.structure2LineEdit.setEnabled(False)

        self.thresholdDoubleSpinBox = PM_DoubleSpinBox(
            pmGroupBox,
            label="Threshold:",
            value=self.threshold,
            setAsDefault=True,
            minimum=0.0,
            maximum=360.0,
            decimals=1,
            singleStep=30.0,
            suffix=" deg",
            spanWidth=False,
        )

        self.comparePushButton = PM_PushButton(pmGroupBox, text="Compare", setAsDefault=True)

        self.hidePushButton = PM_PushButton(pmGroupBox, text="Hide differences", setAsDefault=True)
        return

    def _compareProteins(self):
        """
        Slot for Compare button.

        Compares two selected proteins of the same length.
        Amino acids that differ greater than the "threshold"
        value are displayed in two colors (red for the first protein
        and yellow for the second protein) and are only visible when
        the two proteins are displayed in the
        reduced display style.
        """
        from utilities.constants import red, orange, green, cyan

        if not self.proteinChunk1 or not self.proteinChunk2:
            return

        protein_1 = self.proteinChunk1.protein
        protein_2 = self.proteinChunk2.protein

        if protein_1 and protein_2:
            aa_list_1 = protein_1.get_amino_acids()
            aa_list_2 = protein_2.get_amino_acids()
            protein_1.collapse_all_rotamers()
            protein_2.collapse_all_rotamers()
            if len(aa_list_1) == len(aa_list_2):
                for aa1, aa2 in zip(aa_list_1, aa_list_2):
                    aa1.color = None
                    aa2.color = None
                    # aa1.collapse()
                    # aa2.collapse()
                    if aa1.get_one_letter_code() != aa2.get_one_letter_code():
                        aa1.set_color(red)
                        aa1.expand()
                        aa2.set_color(yellow)
                        aa2.expand()
                    else:
                        max = 0.0
                        for chi in range(0, 3):
                            angle1 = aa1.get_chi_angle(chi)
                            angle2 = aa2.get_chi_angle(chi)
                            if angle1 and angle2:
                                if angle1 < 0.0:
                                    angle1 += 360.0
                                if angle2 < 0.0:
                                    angle2 += 360.0
                                diff = abs(angle1 - angle2)
                                if diff > max:
                                    max = diff
                        if max >= self.threshold:
                            # This be a parameter.
                            aa1.set_color(green)
                            aa1.expand()
                            aa2.set_color(cyan)
                            aa2.expand()

                self.win.glpane.gl_update()
            else:
                msg = "The lengths of compared proteins are not equal."
                self.updateMessage(msg)
                env.history.redmsg(msg)
        return

    def _hideDifferences(self):
        """
        Slot for the "Hide differences" button.

        Hides amino acids that differ greater than the "threshold" value.

        @warning: Untested. Code looks suspicious.
        """
        if not self.proteinChunk1 or not self.proteinChunk2:
            return

        protein_1 = self.proteinChunk1.protein
        protein_2 = self.proteinChunk2.protein

        if protein_1 and protein_2:
            aa_list_1 = protein_1.get_amino_acids()
            aa_list_2 = protein_2.get_amino_acids()
            if len(aa_list_1) == len(aa_list_2):
                protein_1.collapse_all_rotamers()  # @@@
                protein_2.collapse_all_rotamers()  # @@@
                for aa1, aa2 in zip(aa_list_1, aa_list_2):
                    aa1.color = None
                    aa2.color = None
                    aa1.collapse()
                    aa2.collapse()
            self.win.glpane.gl_update()
        return

    def _thresholdChanged(self, value):
        """
        Slot for Threshold spinbox.
        """
        self.threshold = value
        self._compareProteins()
        return

    def _resetAminoAcids(self):
        """
        Resets the color and collapse all amino acids of all proteins.
        """

        proteinChunkList = getAllProteinChunksInPart(self.win.assy)

        for proteinChunk in proteinChunkList:
            proteinChunk.protein.collapse_all_rotamers()
            aa_list = proteinChunk.protein.get_amino_acids()
            for aa in aa_list:
                aa.color = None
                aa.collapse()

        self.win.glpane.gl_update()
        return

    def _update_UI_do_updates(self):
        """
        Overrides superclass method.

        @see: Command_PropertyManager._update_UI_do_updates()
        """

        self.proteinChunk1 = None
        self.proteinChunk2 = None
        self.comparePushButton.setEnabled(False)
        self.hidePushButton.setEnabled(False)

        selectedProteinList = self.win.assy.getSelectedProteinChunks()

        if len(selectedProteinList) == 0:
            self.structure1LineEdit.setText("")
            self.structure2LineEdit.setText("")
            msg = (
                "Select two structures of the same length in the graphics area, "
                "then click the <b>Compare</b> button to compare them."
            )

        elif len(selectedProteinList) == 1:
            self.proteinChunk1 = selectedProteinList[0]
            aa1_count = " (%d)" % self.proteinChunk1.protein.count_amino_acids()
            self.structure1LineEdit.setText(self.proteinChunk1.name + aa1_count)
            self.structure2LineEdit.setText("")
            msg = (
                "Select one more structure in the graphics area that is the same "
                "length as <b>" + self.proteinChunk1.name + "</b>. "
                "Then click the <b>Compare</b> button to compare them."
            )

        elif len(selectedProteinList) == 2:
            self.proteinChunk1 = selectedProteinList[0]
            aa1_count = " (%d)" % self.proteinChunk1.protein.count_amino_acids()
            self.structure1LineEdit.setText(self.proteinChunk1.name + aa1_count)

            self.proteinChunk2 = selectedProteinList[1]
            aa2_count = " (%d)" % self.proteinChunk2.protein.count_amino_acids()
            self.structure2LineEdit.setText(self.proteinChunk2.name + aa2_count)

            if aa1_count == aa2_count:
                self.comparePushButton.setEnabled(True)
                self.hidePushButton.setEnabled(True)
                msg = "Click the <b>Compare</b> button to compare the two selected structures."
            else:
                msg = "<b>%s</b> and <b>%s</b> are not the same length." % (
                    self.proteinChunk1.name,
                    self.proteinChunk2.name,
                )
                msg = redmsg(msg)

        else:
            self.structure1LineEdit.setText("")
            self.structure2LineEdit.setText("")
            msg = redmsg("Too many proteins selected.")

        self.updateMessage(msg)
        env.history.redmsg(msg)
        return