def logMessage(self, type = 'DEFAULT'): 
     """
     Updates the PM message groupbox message with the one specified below. 
     """
     msg = ''
     if type == 'ADD_SEGMENTS_ACTIVATED':
         msg = """To add new PAM3 DNA segments to the list, click on the segment's
          axis. Multiple segments can be added at once by doing a 
         rectangular lasso selection in the 3D workspace."""
     elif type == 'REMOVE_SEGMENTS_ACTIVATED':
         msg = """To remove segments from the list, click on the segment's 
         axis. Multiple segments can be removed at once by doing a 
         rectangular lasso selection in the 3D workspace."""
     elif type == 'LEFT_DRAG_STARTED':
         msg = """Transparent green spheres (if present) around the atoms indicate 
         potential crossover sites."""
     elif type == 'LEFT_DRAG_FINISHED':
         msg = self.propMgr.defaultLogMessage
     elif type == 'WARNING_LIMIT_EXCEEDED':
         msg = "Only a maximum of <b> %d </b> DNA segments can be searched for "\
             "crossover sites. Segments not added to the list"%self.itemLimitForSegmentListWidget()
         msg = orangemsg(msg)
     elif type == 'WARNING_PAM5_SEGMENT_FOUND':
         msg = """Warning: One or more of the PAM5 DNA segments have been 
         removed from the segment list.Make Crossovers command is only 
         availble for PAM3 model."""
         msg = orangemsg(msg)
     else:
         msg = self.propMgr.defaultLogMessage
     
     self.propMgr.updateMessage(msg)
    def Backup(self):
        """
        Undo any bonds made between chunks.
        """
        # This undoes only the last fused chunks.  Will work on supporting
        # multiple undos when we get a single undo working.   Mark 050326

        # Bust bonds between last pair/set of fused chunks.
        if self.bondable_pairs_atoms:
            for a1, a2 in self.bondable_pairs_atoms:
                b = a1.get_neighbor_bond(a2)
                if b: b.bust()


            if self.merged_chunks:
                nchunks_str = "%d" % (len(self.merged_chunks) + 1,)
                msg = "Fuse Chunks: Bonds broken between %s chunks." % (nchunks_str)
                env.history.message(msg)
                msg = "Warning: Cannot separate the original chunks. You can " \
                "do this yourself using <b>Modify > Separate</b>."
                env.history.message(orangemsg(msg))

                cnames = "Their names were: "
                # Here are the original names...
                for chunk in self.merged_chunks:
                    cnames += '[' + chunk.name + '] '
                env.history.message(cnames)

            self.o.gl_update()

        else:
            msg = "Fuse Chunks: No bonds have been made yet.  Undo ignored."
            env.history.message(redmsg(msg))
    def deposit_from_MMKit(self, atom_or_pos):
        """
        Deposit the clipboard item being previewed into the 3D workspace
        Calls L{self.deposit_from_Clipboard_page}
        @attention: This method needs renaming. L{depositMode} still uses it
        so simply overriden here. B{NEEDS CLEANUP}.
        @see: L{self.deposit_from_Clipboard_page}
        """

        if self.o.modkeys is None: # no Shift or Ctrl modifier key.
            self.o.assy.unpickall_in_GLPane()

        deposited_stuff, status = \
                       self.command.deposit_from_Clipboard_page(atom_or_pos)
        deposited_obj = 'Chunk'

        self.o.selatom = None

        if deposited_stuff:
            self.w.win_update()
            status = self.ensure_visible( deposited_stuff, status)
            env.history.message(status)
        else:
            env.history.message(orangemsg(status))

        return deposited_obj
Example #4
0
    def setViewZoomToSelection(self, fast = False): #Ninad 60903
        """
        Change the view so that only selected atoms, chunks and Jigs fit in the GLPane. 
        (i.e. Zoom to the selection) If <fast> is True, then snap to the view
        """
        #ninad060905: 
        #This considers only selected atoms, movable jigs and chunks while doing fit to window. 
        #Zoom to selection ignores other immovable jigs. (it clearly tells this in a history msg)
        # For future:  Should work when a non movable jig is selected
        #Bugs due to use of Bbox remain as in fit to window.

        bbox = self.assy.bbox_for_viewing_selection()

        if bbox is None:
            env.history.message( orangemsg(
                " Zoom To Selection: No visible atoms , chunks or movable jigs selected" \
                " [Acceptable Jigs: Motors, Grid Plane and ESP Image]" ))
            # KLUGE: the proper content of this message depends on the behavior
            # of bbox_for_viewing_selection, which should be extended to cover more
            # kinds of objects.
            return

        center, scale = self.center_and_scale_from_bbox( bbox, klugefactor = 0.85 )
            #ninad060905 experimenting with the scale factor
            # [which was renamed to klugefactor after this comment was written].
            # I see no change with various values!

        pov = V(-center[0], -center[1], -center[2])
        if fast:
            self.snapToView(self.quat, scale, pov, 1.0)
        else:
            self.animateToView(self.quat, scale, pov, 1.0)
        return
 def __init__(self, mapping, chunk):
     # immediately memoize some settings which need to be constant
     # during use, as a bug precaution. Also do whatever precomputes
     # are convenient.
     writemmp_mapping_memo.__init__(self, mapping)
     self.chunk = chunk
     self.ladder = chunk.ladder
     if not dna_updater_is_enabled():
         msg = "Warning: can't convert PAM model when dna updater is disabled; affects [N] chunk(s)"
         env.history.deferred_summary_message( orangemsg( msg))
     elif not self.ladder:
         # (might happen if dna updater is turned off at runtime -- not sure;
         #  note, doing that might have worse effects, like self.ladder existing
         #  but being out of date, causing traceback errors. #### FIX those sometime (elsewhere).)
         print "error: ladder not set during writemmp, can't convert pam model, in %r" % chunk
         msg = "Error: [N] chunk(s) don't have ladders set"
         env.history.deferred_summary_message( redmsg( msg))
     else:
         self.convert_pam_enabled = True
     if self.convert_pam_enabled:
         # Note: this only means conversion is possible -- we don't yet know
         # if it's requested by options on this mapping and chunk.
         # The ladder memo will decide that.
         self._ladder_memo = mapping.get_memo_for(self.ladder)
         self._save_as_pam = self._ladder_memo._f_save_as_what_PAM_model()
     return
Example #6
0
    def addLine(self, line):
        columns = line.split()

        if (len(columns) == 3 and
            columns[0] == 'Step' and
            columns[1] == 'Time' and
            columns[2] == 'Lambda'):

            self._resetColumns()
            self.state = 1
            return
        if (self.state == 1 and len(columns) == 3):
            self.step = columns[0]
            self.state = 2
            return
        if (self.state == 2 and len(columns) == 0):
            self.state = 3
            return
        if (self.state == 3 and len(columns) == 2 and columns[0] == 'Energies'):
            self.state = 4
            return
        if (self.state == 4):
            if (len(columns) > 0):
                self.state = 5
                self.column_headers = self._extractColumns(line.rstrip())
                return
            else:
                self._emitColumns()
                self.state = 0
                return
        if (self.state == 5):
            if (len(columns) == len(self.column_headers)):
                self._addColumns(self.column_headers, columns)
                self.state = 4
                return
            else:
                self.state = 0 # this never happens
                return

# Stepsize too small, or no change in energy.
# Converged to machine precision,
# but not to the requested precision Fmax < 0.006022
#
# Polak-Ribiere Conjugate Gradients did not converge to Fmax < 0.006022 in 100001 steps.

        if (line.find("converge") >= 0 and line.find("Fmax") >= 0):
            env.history.message("Energy (Bond, Strut, Nonbonded): (%f, %f, %f) zJ" %
                                (self.getBondEnergy(),
                                 self.getHarmonicEnergy(),
                                 self.getNonbondedEnergy()))
            env.history.message("Total Energy %f zJ" % self.getTotalEnergy())
            if (line.find("machine") >= 0 or line.find("did not") >= 0):
                env.history.message(orangemsg(quote_html(line.rstrip())))
            else:
                env.history.message(quote_html(line.rstrip()))
            return
Example #7
0
    def addLine(self, line):
        columns = line.split()

        if (len(columns) == 3 and
            columns[0] == 'Step' and
            columns[1] == 'Time' and
            columns[2] == 'Lambda'):

            self._resetColumns()
            self.state = 1
            return
        if (self.state == 1 and len(columns) == 3):
            self.step = columns[0]
            self.state = 2
            return
        if (self.state == 2 and len(columns) == 0):
            self.state = 3
            return
        if (self.state == 3 and len(columns) == 2 and columns[0] == 'Energies'):
            self.state = 4
            return
        if (self.state == 4):
            if (len(columns) > 0):
                self.state = 5
                self.column_headers = self._extractColumns(line.rstrip())
                return
            else:
                self._emitColumns()
                self.state = 0
                return
        if (self.state == 5):
            if (len(columns) == len(self.column_headers)):
                self._addColumns(self.column_headers, columns)
                self.state = 4
                return
            else:
                self.state = 0 # this never happens
                return

# Stepsize too small, or no change in energy.
# Converged to machine precision,
# but not to the requested precision Fmax < 0.006022
#
# Polak-Ribiere Conjugate Gradients did not converge to Fmax < 0.006022 in 100001 steps.

        if (line.find("converge") >= 0 and line.find("Fmax") >= 0):
            env.history.message("Energy (Bond, Strut, Nonbonded): (%f, %f, %f) zJ" %
                                (self.getBondEnergy(),
                                 self.getHarmonicEnergy(),
                                 self.getNonbondedEnergy()))
            env.history.message("Total Energy %f zJ" % self.getTotalEnergy())
            if (line.find("machine") >= 0 or line.find("did not") >= 0):
                env.history.message(orangemsg(quote_html(line.rstrip())))
            else:
                env.history.message(quote_html(line.rstrip()))
            return
    def _postProcess(self, baseList): # bruce 070414
        """
        Set bond direction on the backbone bonds.

        @param baseList: List of basepair chunks that make up the duplex.
        @type  baseList: list

        @note: baseList must contain at least two base-pair chunks.
        """
        # This implem depends on the specifics of how the end-representations
        # are terminated. If that's changed, it might stop working or it might
        # start giving wrong results. In the current representation,
        # baseList[0] (a chunk) is the starting end of the duplex. It has two
        # bonds whose directions we should set, which will determine the
        # directions of their strands: Se3 -> Ss3, and Sh3 <- Ss3.
        # Just find those bonds and set the strand directions.

        assert len(baseList) >= 2
        basepair1_atoms = baseList[0].atoms.values() # StartBasePair.MMP
        basepair2_atoms = baseList[1].atoms.values() # MiddleBasePair or EndBasePair.MMP
        Se3_list = filter( lambda atom: atom.element.symbol in ('Se3'), basepair1_atoms)
        Sh3_list = filter( lambda atom: atom.element.symbol in ('Sh3'), basepair1_atoms)
        # To set the direction of the Se3 -> Ss3 bond, we need the Ss3 atom
        # from the second base-pair (i.e. baseList[1]) that is connected to
        # the Se3 atom in the first base-pair.
        # Ss3_list will have only one Ss3 atom if the duplex is only two
        # base-pairs long. Otherwise, Ss3_list will have two Ss3 atoms.
        Ss3_list = filter( lambda atom: atom.element.symbol in ('Ss3'), basepair2_atoms)
        if len(Se3_list) == len(Sh3_list) == 1:
            for atom in Se3_list:
                assert len(atom.bonds) == 2
                # This is fragile since it is dependent on the bond order in the
                # PAM3 StartBasePair.MMP file. If the order gets changed in that
                # file, this will fail.
                # A more robust approach would be to check for the Se3 -> Ss3
                # bond, which is the one we want here. Mark 2007-09-27.
                #atom.bonds[1].set_bond_direction_from(atom, 1, propogate = True)
                # This implements the more robust stategy mentioned
                # above. I'd like Bruce to review it and confirm it is good.
                # Mark 2007-09-27.
                #atom.bonds[1].set_bond_direction_from(Ss3_list[0], -1, propogate = True)
                try: # Try the first Ss3 atom in Ss3_list.
                    atom.bonds[1].set_bond_direction_from(Ss3_list[0], -1, propogate = True)
                except: # That wasn't it, so it must be the second Ss3 atom.
                    atom.bonds[1].set_bond_direction_from(Ss3_list[1], -1, propogate = True)
            for atom in Sh3_list:
                assert len(atom.bonds) == 1
                atom.bonds[0].set_bond_direction_from(atom, -1, propogate = True)
        else:
            #bruce 070604 mitigate bug in above code when number of bases == 1
            # by not raising an exception when it fails.
            msg = "Warning: strand not terminated, bond direction not set \
            (too short)"
            env.history.message( orangemsg( msg))
        return
    def _postProcess(self, baseList): # bruce 070414
        """
        Set bond direction on the backbone bonds.
        
        @param baseList: List of basepair chunks that make up the duplex.
        @type  baseList: list
        
        @note: baseList must contain at least two base-pair chunks.
        """
        # This implem depends on the specifics of how the end-representations
        # are terminated. If that's changed, it might stop working or it might
        # start giving wrong results. In the current representation,
        # baseList[0] (a chunk) is the starting end of the duplex. It has two 
        # bonds whose directions we should set, which will determine the 
        # directions of their strands: Se3 -> Ss3, and Sh3 <- Ss3.
        # Just find those bonds and set the strand directions.

        assert len(baseList) >= 2
        basepair1_atoms = baseList[0].atoms.values() # StartBasePair.MMP
        basepair2_atoms = baseList[1].atoms.values() # MiddleBasePair or EndBasePair.MMP
        Se3_list = filter( lambda atom: atom.element.symbol in ('Se3'), basepair1_atoms)
        Sh3_list = filter( lambda atom: atom.element.symbol in ('Sh3'), basepair1_atoms)
        # To set the direction of the Se3 -> Ss3 bond, we need the Ss3 atom
        # from the second base-pair (i.e. baseList[1]) that is connected to
        # the Se3 atom in the first base-pair.
        # Ss3_list will have only one Ss3 atom if the duplex is only two 
        # base-pairs long. Otherwise, Ss3_list will have two Ss3 atoms.
        Ss3_list = filter( lambda atom: atom.element.symbol in ('Ss3'), basepair2_atoms)
        if len(Se3_list) == len(Sh3_list) == 1:
            for atom in Se3_list:
                assert len(atom.bonds) == 2
                # This is fragile since it is dependent on the bond order in the
                # PAM3 StartBasePair.MMP file. If the order gets changed in that
                # file, this will fail.
                # A more robust approach would be to check for the Se3 -> Ss3
                # bond, which is the one we want here. Mark 2007-09-27.
                #atom.bonds[1].set_bond_direction_from(atom, 1, propogate = True)
                # This implements the more robust stategy mentioned 
                # above. I'd like Bruce to review it and confirm it is good.
                # Mark 2007-09-27.
                #atom.bonds[1].set_bond_direction_from(Ss3_list[0], -1, propogate = True)
                try: # Try the first Ss3 atom in Ss3_list.
                    atom.bonds[1].set_bond_direction_from(Ss3_list[0], -1, propogate = True)
                except: # That wasn't it, so it must be the second Ss3 atom.
                    atom.bonds[1].set_bond_direction_from(Ss3_list[1], -1, propogate = True)
            for atom in Sh3_list:
                assert len(atom.bonds) == 1
                atom.bonds[0].set_bond_direction_from(atom, -1, propogate = True)
        else:
            #bruce 070604 mitigate bug in above code when number of bases == 1
            # by not raising an exception when it fails.
            msg = "Warning: strand not terminated, bond direction not set \
            (too short)"
            env.history.message( orangemsg( msg))
        return
Example #10
0
def simMoviePlayer(assy):
    """
    Plays a DPB movie file created by the simulator,
    either the current movie if any, or a previously saved
    dpb file with the same name as the current part, if one can be found.
    """
    from simulation.movie import find_saved_movie, Movie #bruce 050329 precaution (in case of similar bug to bug 499)
    win = assy.w
    if not assy.molecules: # No model, so no movie could be valid for current part.
        # bruce 050327 comment: even so, a movie file might be valid for some other Part...
        # not yet considered here. [050427 addendum: note that user can't yet autoload a new Part
        # just by opening a movie file, so there's no point in going into the mode -- it's only meant
        # for playing a movie for the *current contents of the current part*, for now.]
        env.history.message(redmsg("Movie Player: Need a model."))
        win.simMoviePlayerAction.setChecked(0) # toggle on the Movie Player icon ninad 061113
        return

    if assy.current_movie and assy.current_movie.might_be_playable():
        win.commandSequencer.userEnterCommand('MOVIE', always_update = True)
        return

    # no valid current movie, look for saved one with same name as assy
    ## env.history.message("Plot Tool: No simulation has been run yet.")
    if assy.filename:
        if assy.part is not assy.tree.part:
            msg = "Movie Player: Warning: Looking for saved movie for main part, not for displayed clipboard item."
            env.history.message(orangemsg(msg))
        errorcode, partdir = assy.find_or_make_part_files_directory()
        if not errorcode: # filename could be an MMP or PDB file.
            dir, fil = os.path.split(assy.filename)
            fil, ext = os.path.splitext(fil)
            mfile = os.path.join(partdir, fil + '.dpb')
        else:
            mfile = os.path.splitext(assy.filename)[0] + ".dpb"
        movie = find_saved_movie( assy, mfile)
            # checks existence -- should also check validity for current part or main part, but doesn't yet ###e
            # (neither did the pre-030527 code for this function, unless that's done in moviePlay, which it might be)
        if movie:
            # play this movie, and make it the current movie.
            assy.current_movie = movie
            #e should we switch to the part for which this movie was made? [might be done in moviePlay; if not:]
            # No current way to tell how to do that, and this might be done even if it's not valid
            # for any loaded Part. So let's not... tho we might presume (from filename choice we used)
            # it was valid for Main Part. Maybe print warning for clip item, and for not valid? #e
            env.history.message("Movie Player: %s previously saved movie for this part." % ("playing" or "loading"))
            win.commandSequencer.userEnterCommand('MOVIE', always_update = True)
            return
    # else if no assy.filename or no movie found from that:
    # bruce 050327 comment -- do what the old code did, except for the moviePlay
    # which seems wrong and tracebacks now.
    assy.current_movie = Movie(assy)
        # temporary kluge until bugs in movieMode for no assy.current_movie are fixed
    win.commandSequencer.userEnterCommand('MOVIE', always_update = True)
    return
Example #11
0
    def viewNormalTo(self): # 
        """
        Set view to the normal vector of the plane defined by 3 or more
        selected atoms or a jig's (Motor or RectGadget) axis.
        """
        cmd = greenmsg("Set View Normal To: ")

        chunks = self.assy.selmols
        jigs = self.assy.getSelectedJigs()
        atoms = self.assy.selatoms_list()

        #following fixes bug 1748 ninad 061003. 
        if len(chunks) > 0 and len(atoms) == 0:
            # Even though chunks have an axis, it is not necessarily the same
            # axis attr stored in the chunk.  Get the chunks atoms and let
            # compute_heuristic_axis() recompute them.
            for c in range(len(chunks)):
                atoms += chunks[c].atoms.values()
        elif len(jigs) == 1 and len(atoms) == 0:
            # Warning: RectGadgets have no atoms.  We handle this special case below.
            atoms = jigs[0].atoms 
        elif len(atoms) < 3:
            # There is a problem when allowing only 2 selected atoms. 
            # Changing requirement to 3 atoms fixes bug 1418. mark 060322
            msg = redmsg("Please select some atoms, jigs, and/or chunks, covering at least 3 atoms")
            print "ops_view.py len(atoms) = ", len(atoms)
            env.history.message(cmd + msg)
            return

        # This check is needed for jigs that have no atoms.  Currently, this 
        # is the case for RectGadgets (ESP Image and Grid Plane) only.
        if len(atoms):
            pos = A( map( lambda a: a.posn(), atoms ) )
            nears = [ self.glpane.out, self.glpane.up ]
            axis = compute_heuristic_axis( pos, 'normal', already_centered = False, nears = nears, dflt = None )
        else: # We have a jig with no atoms.
            axis = jigs[0].getaxis() # Get the jig's axis.
            # If axis is pointing into the screen, negate (reverse) axis.
            if dot(axis, self.glpane.lineOfSight) > 0:
                axis = -axis

        if not axis:
            msg = orangemsg( "Warning: Normal axis could not be determined. No change in view." )
            env.history.message(cmd + msg)
            return

        # Compute the destination quat (q2).
        q2 = Q(V(0,0,1), axis)
        q2 = q2.conj()

        self.glpane.rotateView(q2)

        info = 'View set to normal vector of the plane defined by the selected atoms.'
        env.history.message(cmd + info)
Example #12
0
def _force_download():
    # Don't check if the MD5 matches. Don't check if the XML file is
    # older than two days old. Just download it unconditionally.
    env.history.message(orangemsg("FOR DEBUG ONLY! _force_download() " + "does not respect user privacy preferences."))
    xmlfile = os.path.join(_sponsordir, "sponsors.xml")
    win = env.mainwindow()
    _download_xml_file(xmlfile)
    if not os.path.exists(xmlfile):
        raise Exception("_force_download failed")
    _load_sponsor_info(xmlfile, win)
    env.history.message(greenmsg("_force_download() is finished"))
Example #13
0
def simMoviePlayer(assy):
    """
    Plays a DPB movie file created by the simulator,
    either the current movie if any, or a previously saved
    dpb file with the same name as the current part, if one can be found.
    """
    from simulation.movie import find_saved_movie, Movie #bruce 050329 precaution (in case of similar bug to bug 499)
    win = assy.w
    if not assy.molecules: # No model, so no movie could be valid for current part.
        # bruce 050327 comment: even so, a movie file might be valid for some other Part...
        # not yet considered here. [050427 addendum: note that user can't yet autoload a new Part
        # just by opening a movie file, so there's no point in going into the mode -- it's only meant
        # for playing a movie for the *current contents of the current part*, for now.]
        env.history.message(redmsg("Movie Player: Need a model."))
        win.simMoviePlayerAction.setChecked(0) # toggle on the Movie Player icon ninad 061113
        return

    if assy.current_movie and assy.current_movie.might_be_playable():
        win.commandSequencer.userEnterCommand('MOVIE')
        return

    # no valid current movie, look for saved one with same name as assy
    ## env.history.message("Plot Tool: No simulation has been run yet.")
    if assy.filename:
        if assy.part is not assy.tree.part:
            msg = "Movie Player: Warning: Looking for saved movie for main part, not for displayed clipboard item."
            env.history.message(orangemsg(msg))
        errorcode, partdir = assy.find_or_make_part_files_directory()
        if not errorcode: # filename could be an MMP or PDB file.
            dir, fil = os.path.split(assy.filename)
            fil, ext = os.path.splitext(fil)
            mfile = os.path.join(partdir, fil + '.dpb')
        else:
            mfile = os.path.splitext(assy.filename)[0] + ".dpb"
        movie = find_saved_movie( assy, mfile)
            # checks existence -- should also check validity for current part or main part, but doesn't yet ###e
            # (neither did the pre-030527 code for this function, unless that's done in moviePlay, which it might be)
        if movie:
            # play this movie, and make it the current movie.
            assy.current_movie = movie
            #e should we switch to the part for which this movie was made? [might be done in moviePlay; if not:]
            # No current way to tell how to do that, and this might be done even if it's not valid
            # for any loaded Part. So let's not... tho we might presume (from filename choice we used)
            # it was valid for Main Part. Maybe print warning for clip item, and for not valid? #e
            env.history.message("Movie Player: %s previously saved movie for this part." % ("playing" or "loading"))
            win.commandSequencer.userEnterCommand('MOVIE')
            return
    # else if no assy.filename or no movie found from that:
    # bruce 050327 comment -- do what the old code did, except for the moviePlay
    # which seems wrong and tracebacks now.
    assy.current_movie = Movie(assy)
        # temporary kluge until bugs in movieMode for no assy.current_movie are fixed
    win.commandSequencer.userEnterCommand('MOVIE')
    return
Example #14
0
    def _postProcess(self, baseList):  # bruce 070414
        """
        Set bond direction on the backbone bonds.

        @param baseList: List of basepair chunks that make up the duplex.
        @type  baseList: list
        """
        # This implem depends on the specifics of how the end-representations
        # are terminated. If that's changed, it might stop working or it might
        # start giving wrong results. In the current representation,
        # baseList[0] (a chunk) has two bonds whose directions we must set,
        # which will determine the directions of their strands:
        #   Ss5 -> Sh5, and Ss5 <- Pe5.
        # Just find those bonds and set the strand directions (until such time
        # as they can be present to start with in the end1 mmp file).
        # (If we were instead passed all the atoms, we could be correct if we
        # just did this to the first Pe5 and Sh5 we saw, or to both of each if
        # setting the same direction twice is allowed.)
        atoms = baseList[0].atoms.values()
        Pe_list = filter(lambda atom: atom.element.symbol in ('Pe5'), atoms)
        Sh_list = filter(lambda atom: atom.element.symbol in ('Sh5'), atoms)

        if len(Pe_list) == len(Sh_list) == 1:
            for atom in Pe_list:
                assert len(atom.bonds) == 1
                atom.bonds[0].set_bond_direction_from(atom, 1, propogate=True)
            for atom in Sh_list:
                assert len(atom.bonds) == 1
                atom.bonds[0].set_bond_direction_from(atom, -1, propogate=True)
        else:
            #bruce 070604 mitigate bug in above code when number of bases == 1
            # by not raising an exception when it fails.
            msg = "Warning: strand not terminated, bond direction not set \
            (too short)"

            env.history.message(orangemsg(msg))

            # Note: It turns out this bug is caused by a bug in the rest of the
            # generator (which I didn't try to diagnose) -- for number of
            # bases == 1 it doesn't terminate the strands, so the above code
            # can't find the termination atoms (which is how it figures out
            # what to do without depending on intimate knowledge of the base
            # mmp file contents).

            # print "baseList = %r, its len = %r, atoms in [0] = %r" % \
            #       (baseList, len(baseList), atoms)
            ## baseList = [<molecule 'unknown' (11 atoms) at 0xb3d6f58>],
            ## its len = 1, atoms in [0] = [Ax1, X2, X3, Ss4, Pl5, X6, X7, Ss8, Pl9, X10, X11]

            # It would be a mistake to fix this here (by giving it that
            # intimate knowledge) -- instead we need to find and fix the bug
            # in the rest of generator when number of bases == 1.
        return
    def _postProcess(self, baseList): # bruce 070414
        """
        Set bond direction on the backbone bonds.

        @param baseList: List of basepair chunks that make up the duplex.
        @type  baseList: list
        """
        # This implem depends on the specifics of how the end-representations
        # are terminated. If that's changed, it might stop working or it might
        # start giving wrong results. In the current representation, 
        # baseList[0] (a chunk) has two bonds whose directions we must set,
        # which will determine the directions of their strands: 
        #   Ss5 -> Sh5, and Ss5 <- Pe5.
        # Just find those bonds and set the strand directions (until such time
        # as they can be present to start with in the end1 mmp file).
        # (If we were instead passed all the atoms, we could be correct if we 
        # just did this to the first Pe5 and Sh5 we saw, or to both of each if 
        # setting the same direction twice is allowed.)
        atoms = baseList[0].atoms.values()
        Pe_list = filter( lambda atom: atom.element.symbol in ('Pe5'), atoms)
        Sh_list = filter( lambda atom: atom.element.symbol in ('Sh5'), atoms)

        if len(Pe_list) == len(Sh_list) == 1:
            for atom in Pe_list:
                assert len(atom.bonds) == 1
                atom.bonds[0].set_bond_direction_from(atom, 1, propogate = True)
            for atom in Sh_list:
                assert len(atom.bonds) == 1
                atom.bonds[0].set_bond_direction_from(atom, -1, propogate = True)
        else:
            #bruce 070604 mitigate bug in above code when number of bases == 1
            # by not raising an exception when it fails.
            msg = "Warning: strand not terminated, bond direction not set \
                (too short)"
            env.history.message( orangemsg( msg))

            # Note: It turns out this bug is caused by a bug in the rest of the
            # generator (which I didn't try to diagnose) -- for number of 
            # bases == 1 it doesn't terminate the strands, so the above code
            # can't find the termination atoms (which is how it figures out
            # what to do without depending on intimate knowledge of the base 
            # mmp file contents).

            # print "baseList = %r, its len = %r, atoms in [0] = %r" % \
            #       (baseList, len(baseList), atoms)
            ## baseList = [<molecule 'unknown' (11 atoms) at 0xb3d6f58>],
            ## its len = 1, atoms in [0] = [Ax1, X2, X3, Ss4, Pl5, X6, X7, Ss8, Pl9, X10, X11]

            # It would be a mistake to fix this here (by giving it that
            # intimate knowledge) -- instead we need to find and fix the bug 
            # in the rest of generator when number of bases == 1.
        return
Example #16
0
 def will_copy_if_selected(self, sel, realCopy):
     """
     copy only if all my atoms are selected [overrides Jig.will_copy_if_selected]
     """
     # for measurement jigs, copy only if all atoms selected, wware 051107
     # [bruce 060329 adds: this doesn't prevent the copy if the jig is inside a Group, and that causes a bug]
     for atom in self.atoms:
         if not sel.picks_atom(atom):
             if realCopy:
                 msg = "Can't copy a measurement jig [%s] unless all its atoms are selected." % (self.name,)
                 env.history.message(orangemsg(msg))
             return False
     return True
Example #17
0
 def will_copy_if_selected(self, sel, realCopy):
     """
     copy only if all my atoms are selected [overrides Jig.will_copy_if_selected]
     """
     # for measurement jigs, copy only if all atoms selected, wware 051107
     # [bruce 060329 adds: this doesn't prevent the copy if the jig is inside a Group, and that causes a bug]
     for atom in self.atoms:
         if not sel.picks_atom(atom):
             if realCopy:
                 msg = "Can't copy a measurement jig [%s] unless all its atoms are selected." % (self.name,)
                 env.history.message(orangemsg(msg))
             return False
     return True
Example #18
0
    def viewQuteMol(self):
        """
        Slot for 'View > QuteMolX'. Opens the QuteMolX Property Manager.

        @note: The QuteMolX PM will not open if there are no atoms in the part.
        """    
        cmd = greenmsg("QuteMolX : ")

        if self.assy.molecules:
            self.qutemolPM.show()
        else:
            msg = orangemsg("No atoms in the current part.")
            env.history.message(cmd + msg)
Example #19
0
def _force_download():
    # Don't check if the MD5 matches. Don't check if the XML file is
    # older than two days old. Just download it unconditionally.
    env.history.message(
        orangemsg("FOR DEBUG ONLY! _force_download() " +
                  "does not respect user privacy preferences."))
    xmlfile = os.path.join(_sponsordir, 'sponsors.xml')
    win = env.mainwindow()
    _download_xml_file(xmlfile)
    if not os.path.exists(xmlfile):
        raise Exception('_force_download failed')
    _load_sponsor_info(xmlfile, win)
    env.history.message(greenmsg("_force_download() is finished"))
Example #20
0
    def viewNormalTo_NEW(self):
        """
        Set view to the normal vector of the plane defined by 3 or more
        selected atoms or a jig's (Motor or RectGadget) axis.
        """
        # This implementation has two serious problems:
        #   1. it selects a normal based on the atoms and not the axis of a jig (e.g. a moved rotary motor).
        #   2. doesn't consider selected jigs that have no atoms.
        # Bruce and I will discuss this and determine the best implem.
        # For A7, I've decide to use the original version. This version will be reinstated in A8
        # after fixing these problems. mark 060322.

        cmd = greenmsg("Set View Normal To: ")

        atoms = self.assy.getSelectedAtoms()

        if len(atoms) < 3:
            # There is a problem when allowing only 2 selected atoms.
            # Changing requirement to 3 atoms fixes bug 1418. mark 060322
            msg = redmsg(
                "Please select some atoms, jigs, and/or chunks, covering at least 3 atoms"
            )
            env.history.message(cmd + msg)
            return

        pos = A(map(lambda a: a.posn(),
                    atoms))  # build list of atom xyz positions.
        nears = [self.glpane.out, self.glpane.up]
        axis = compute_heuristic_axis(pos,
                                      'normal',
                                      already_centered=False,
                                      nears=nears,
                                      dflt=None)

        if not axis:
            msg = orangemsg(
                "Warning: Normal axis could not be determined. No change in view."
            )
            env.history.message(cmd + msg)
            return

        # Compute the destination quat (q2).
        q2 = Q(V(0, 0, 1), axis)
        q2 = q2.conj()

        self.glpane.rotateView(q2)

        info = 'View set to normal of the plane defined by the selection.'
        env.history.message(cmd + info)
    def _modifyStructure(self, params):
        """
        Modifies the structure (Rotary Motor) using the provided params.
        @param params: The parameters used as an input to modify the structure
                       (Rotary Motor created using this 
                       RotaryMotor_EditCommand) 
        @type  params: tuple
        """
        assert self.struct
        assert params 
        assert len(params) == 7             

        torque, initial_speed, final_speed, \
              dampers_state, enable_minimize_state, \
              color, atoms  = params

        numberOfAtoms = len(atoms)

        atomNumberRequirementMet, logMessage = \
                                self._checkMotorAtomLimits(numberOfAtoms)

        if not atomNumberRequirementMet:
            atoms = self.struct.atoms[:]
            logMessage = logMessage + " Motor will remain attached to the"\
                       " atoms listed in the 'Motor Atoms' list in this" \
                       " property manager"
            logMessage = orangemsg(logMessage)            
            self.propMgr.updateMessage(logMessage)
            assert len(atoms) > 0

        self.struct.cancelled = False
        self.struct.torque = torque
        self.struct.initial_speed = initial_speed
        self.struct.speed = final_speed
        self.struct.dampers_enabled = dampers_state
        self.struct.enable_minimize = enable_minimize_state
        self.struct.color = color
        #Not sure if it is safe to do self.struct.atoms = atoms
        #Instead using 'setShaft method -- ninad 2007-10-09
        self.struct.setShaft(atoms)


        self.struct.findCenterAndAxis(atoms, self.win.glpane)

        self.propMgr.updateAttachedAtomListWidget(atomList = atoms)

        self.win.win_update() # Update model tree
        self.win.assy.changed()     
    def _modifyStructure(self, params):
        """
        Modifies the structure (Rotary Motor) using the provided params.
        @param params: The parameters used as an input to modify the structure
                       (Rotary Motor created using this 
                       RotaryMotor_EditCommand) 
        @type  params: tuple
        """
        assert self.struct
        assert params 
        assert len(params) == 7             

        torque, initial_speed, final_speed, \
              dampers_state, enable_minimize_state, \
              color, atoms  = params

        numberOfAtoms = len(atoms)

        atomNumberRequirementMet, logMessage = \
                                self._checkMotorAtomLimits(numberOfAtoms)

        if not atomNumberRequirementMet:
            atoms = self.struct.atoms[:]
            logMessage = logMessage + " Motor will remain attached to the"\
                       " atoms listed in the 'Motor Atoms' list in this" \
                       " property manager"
            logMessage = orangemsg(logMessage)            
            self.propMgr.updateMessage(logMessage)
            assert len(atoms) > 0

        self.struct.cancelled = False
        self.struct.torque = torque
        self.struct.initial_speed = initial_speed
        self.struct.speed = final_speed
        self.struct.dampers_enabled = dampers_state
        self.struct.enable_minimize = enable_minimize_state
        self.struct.color = color
        #Not sure if it is safe to do self.struct.atoms = atoms
        #Instead using 'setShaft method -- ninad 2007-10-09
        self.struct.setShaft(atoms)


        self.struct.findCenterAndAxis(atoms, self.win.glpane)

        self.propMgr.updateAttachedAtomListWidget(atomList = atoms)

        self.win.win_update() # Update model tree
        self.win.assy.changed()     
def _clear_illegal_direction(bond):
    """
    [private helper for _fix_atom_or_return_error_info]

    bond has a direction but is not directional
    (since one of its atoms does not permit directional bonds,
     e.g. it might be an Ss-Ax bond).
    Report and immediately fix this error.

    @type bond: Bond
    """
    bond.clear_bond_direction()

    summary_format = "Warning: dna updater cleared [N] bond direction(s) on pseudoelements that don't permit one"
    env.history.deferred_summary_message( orangemsg(summary_format) )
    return
Example #24
0
def _clear_illegal_direction(bond):
    """
    [private helper for _fix_atom_or_return_error_info]

    bond has a direction but is not directional
    (since one of its atoms does not permit directional bonds,
     e.g. it might be an Ss-Ax bond).
    Report and immediately fix this error.

    @type bond: Bond
    """
    bond.clear_bond_direction()

    summary_format = "Warning: dna updater cleared [N] bond direction(s) on pseudoelements that don't permit one"
    env.history.deferred_summary_message(orangemsg(summary_format))
    return
def adjustSinglet(singlet, minimize=False):  # Mark 2007-10-21.
    """
    Adjusts I{singlet} using one of two methods based on I{minimize}:

    1. Hydrogenate the singlet, then transmute it back to a singlet
    (default). Singlet positions are much better after this, but
    they are not in their optimal location.

    2. Hydrogenate the singlet, then call the simulator via the
    L{LocalMinimize_Function} to adjust (minimize) the hydrogen atom, then
    tranmute the hydrogen back to a singlet. Singlet positions are best
    after using this method, but it has one major drawback -- it
    redraws while minimizing. This is a minor problem when breaking
    strands, but is intolerable in the DNA duplex generator (which adjusts
    open bond singlets in its postProcess method.

    @param singlet: A singlet.
    @type  singlet: L{Atom}

    @param minimize: If True, use the minimizer to adjust the singlet
                     (i.e. method #2).
    @type  minimize: bool

    @note: Real atoms are not adjusted.

    @see: L{Hydrogenate} for details about how we are using it to
          reposition singlets (via method 1 mentioned above).
    """
    if not singlet.is_singlet():
        return

    singlet.Hydrogenate()
    if minimize:
        msg = "ATTENTION: Using minimizer to adjust open bond singlets."
        env.history.message(orangemsg(msg))
        # Singlet is repositioned properly using minimize.
        # The problem is that this redraws while running. Don't want that!
        # Talk to Bruce and Eric M. about it. Mark 2007-10-21.
        LocalMinimize_function([singlet], nlayers=0)
    # Transmute() will not transmute singlets. Since <singlet> is a Hydrogen,
    # and not a singlet, this will work. -mark 2007-10-31 (Boo!)
    from model.elements import Singlet

    singlet.Transmute(Singlet)
    return
    def strandLengthChanged( self, inStrandLength ):
        """
        Slot for the Strand Length spin box, called whenever the value of the
        Strand Length spin box changes.

        @param inStrandLength: The number of bases in the strand sequence.
        @type  inStrandLength: int
        """

        theSequence   =  self.getPlainSequence()
        sequenceLen   =  len( theSequence )
        lengthChange  =  inStrandLength - self.getSequenceLength()

        # Preserve the cursor's position/selection
        cursor          =  self.sequenceTextEdit.textCursor()
        #cursorPosition  =  cursor.position()
        selectionStart  =  cursor.selectionStart()
        selectionEnd    =  cursor.selectionEnd()

        if inStrandLength < 0:
            return # Should never happen.

        if lengthChange < 0:
            # If length is less than the previous length,
            # simply truncate the current sequence.
            theSequence.chop( -lengthChange )

        elif lengthChange > 0:
            # If length has increased, add the correct number of base
            # letters to the current strand sequence.
            numNewBases  =  lengthChange

            # Get current base selected in combobox.
            chosenBase  =  'X' # Unassigned.

            basesToAdd  =  chosenBase * numNewBases
            theSequence.append( basesToAdd )

        else:
            env.history.message(
                orangemsg( "strandLengthChanged(): Length has not changed." ))

        self.setSequence( theSequence )

        return
Example #27
0
    def strandLengthChanged(self, inStrandLength):
        """
        Slot for the Strand Length spin box, called whenever the value of the 
        Strand Length spin box changes.
        
        @param inStrandLength: The number of bases in the strand sequence.
        @type  inStrandLength: int
        """

        theSequence = self.getPlainSequence()
        sequenceLen = len(theSequence)
        lengthChange = inStrandLength - self.getSequenceLength()

        # Preserve the cursor's position/selection
        cursor = self.sequenceTextEdit.textCursor()
        #cursorPosition  =  cursor.position()
        selectionStart = cursor.selectionStart()
        selectionEnd = cursor.selectionEnd()

        if inStrandLength < 0:
            return  # Should never happen.

        if lengthChange < 0:
            # If length is less than the previous length,
            # simply truncate the current sequence.
            theSequence.chop(-lengthChange)

        elif lengthChange > 0:
            # If length has increased, add the correct number of base
            # letters to the current strand sequence.
            numNewBases = lengthChange

            # Get current base selected in combobox.
            chosenBase = 'X'  # Unassigned.

            basesToAdd = chosenBase * numNewBases
            theSequence.append(basesToAdd)

        else:
            env.history.message(
                orangemsg("strandLengthChanged(): Length has not changed."))

        self.setSequence(theSequence)

        return
 def recompute_center_axis(self, glpane): #bruce 060120 replaced los arg with glpane re bug 1344
     # try to point in direction of prior axis, or along line of sight if no old axis (self.axis is V(0,0,0) then)
     nears = [self.axis, glpane.lineOfSight, glpane.down]
     pos = A( map( lambda a: a.posn(), self.atoms ) )
     self.center = sum(pos)/len(pos)
     relpos = pos - self.center
     from geometry.geometryUtilities import compute_heuristic_axis
     axis = compute_heuristic_axis( relpos, 'normal', already_centered = True, nears = nears, dflt = None )
     if not axis:
         #e warning? I think so... BTW we pass dflt = None to make the warning come out more often;
         # I don't know if we'd need to check for it here if we didn't.
         env.history.message( orangemsg( "Warning: motor axis chosen arbitrarily since atom arrangement doesn't suggest one." ))
             #k can this come out too often during movie-playing? No, because we don't recompute axis then at all.
         axis = glpane.lineOfSight
     self.axis = axis
     self.assy.changed()  #bruce 060116 fix unreported bug analogous to bug 1331
     self._initial_posns = None #bruce 050518; needed in RotaryMotor, harmless in others
     return
Example #29
0
 def cm_select_jigs_atoms(self):  #bruce 050504
     nodeset = self.topmost_selected_nodes()
     otherpart = {}  #bruce 050505 to fix bug 589
     did_these = {}
     nprior = len(self.assy.selatoms)
     for jig in nodeset:
         assert isinstance(jig, Jig)  # caller guarantees they are all jigs
         # If we didn't want to desel the jig, I'd have to say:
         # note: this does not deselect the jig (good); and permit_pick_atoms would deselect it (bad);
         # so to keep things straight (not sure this is actually needed except to avoid a debug message),
         # just set SELWHAT_ATOMS here; this is legal because no chunks are selected. Actually, bugs might occur
         # in which that's not true... I forget whether I fixed those recently or only analyzed them (due to delays
         # in update event posting vs processing)... but even if they can occur, it's not high-priority to fix them,
         # esp since selection rules might get revised soon.
         ## self.assy.set_selwhat(SELWHAT_ATOMS)
         # but (I forgot when I wrote that) we *do* desel the jig,
         # so instead I can just say:
         self.assy.part.permit_pick_atoms(
         )  # changes selwhat and deselects all chunks, jigs, and groups
         # [bruce 050519 8pm]
         for atm in jig.atoms:
             if atm.molecule.part == jig.part:
                 atm.pick()
                 did_these[atm.key] = atm
             else:
                 otherpart[atm.key] = atm
         ## jig.unpick() # not done by picking atoms [no longer needed since done by permit_pick_atoms]
     msg = fix_plurals("Selected %d atom(s)" %
                       len(did_these))  # might be 0, that's ok
     if nprior:  #bruce 050519
         #e msg should distinguish between atoms already selected and also selected again just now,
         # vs already and not now; for now, instead, we just try to be ambiguous about that
         msg += fix_plurals(" (%d atom(s) remain selected from before)" %
                            nprior)
     if otherpart:
         msg += fix_plurals(
             " (skipped %d atom(s) which were not in this Part)" %
             len(otherpart))
         msg = orangemsg(msg)  # the whole thing, I guess
     env.history.message(msg)
     self.win.win_update()
     # note: caller (which puts up context menu) does
     # self.win.update_select_mode(); we depend on that. [### still true??]
     return
Example #30
0
def adjustSinglet(singlet, minimize=False):  # Mark 2007-10-21.
    """
    Adjusts I{singlet} using one of two methods based on I{minimize}:

    1. Hydrogenate the singlet, then transmute it back to a singlet
    (default). Singlet positions are much better after this, but
    they are not in their optimal location.

    2. Hydrogenate the singlet, then call the simulator via the
    L{LocalMinimize_Function} to adjust (minimize) the hydrogen atom, then
    tranmute the hydrogen back to a singlet. Singlet positions are best
    after using this method, but it has one major drawback -- it
    redraws while minimizing. This is a minor problem when breaking
    strands, but is intolerable in the DNA duplex generator (which adjusts
    open bond singlets in its postProcess method.

    @param singlet: A singlet.
    @type  singlet: L{Atom}

    @param minimize: If True, use the minimizer to adjust the singlet
                     (i.e. method #2).
    @type  minimize: bool

    @note: Real atoms are not adjusted.

    @see: L{Hydrogenate} for details about how we are using it to
          reposition singlets (via method 1 mentioned above).
    """
    if not singlet.is_singlet():
        return

    singlet.Hydrogenate()
    if minimize:
        msg = "ATTENTION: Using minimizer to adjust open bond singlets."
        env.history.message(orangemsg(msg))
        # Singlet is repositioned properly using minimize.
        # The problem is that this redraws while running. Don't want that!
        # Talk to Bruce and Eric M. about it. Mark 2007-10-21.
        LocalMinimize_function([singlet], nlayers=0)
    # Transmute() will not transmute singlets. Since <singlet> is a Hydrogen,
    # and not a singlet, this will work. -mark 2007-10-31 (Boo!)
    from model.elements import Singlet
    singlet.Transmute(Singlet)
    return
    def _make_bonds_3(self):
        """
        Make bonds -- part 3.
        (Print history warning if self.singlet_found_with_multiple_bonds.)
        """
        if self.singlet_found_with_multiple_bonds:
            mbonds, singlets_not_bonded, bp = self.multibonds()

            self.total_bonds_made = len(self.bondable_pairs_atoms)

            if singlets_not_bonded == 1:
                msg = "%d bondpoint had more than one way to bond. It was not bonded." % (singlets_not_bonded,)
            else:
                msg = "%d bondpoints had more than one way to bond. They were not bonded." % (singlets_not_bonded,)
            env.history.message(orangemsg(msg))

        else:
            # All bondpoints had only one way to bond.
            self.total_bonds_made = len(self.bondable_pairs_atoms)
Example #32
0
def include_dir_ok(include_dir):
    """
    Is this include_dir acceptable (or maybe acceptable)? Return (0, "") or (errorcode, errortext).
    """
    if env.debug():
        print "debug: include_dir_ok(include_dir = %r)" % (include_dir,)
    if os.path.isdir(include_dir):
        # ok, but warn if transforms.inc is not inside it
        if not os.path.exists(os.path.join(include_dir, "transforms.inc")):
            msg = "Warning: transforms.inc not present in POV include directory [%s]; rendering might not work" % (include_dir,)
            env.history.message(orangemsg(msg))
        if env.debug():
            print "debug: include_dir_ok returns 0 (ok)"
        return 0, "" # ok
    else:
        if env.debug():
            print "debug: include_dir_ok returns 1 (Not found or not a directory)"
        return 1, "POV include directory: Not found or not a directory" #e pathname might be too long for a dialog
    pass
    def _modifyStructure(self, params):
        """
        Modifies the structure (Plane) using the provided params.
        @param params: The parameters used as an input to modify the structure
                       (Plane created using this Plane_EditCommand)
        @type  params: tuple
        """
        assert self.struct
        assert params
        assert len(params) == 5

        force, stiffness, enable_minimize_state, color, atoms = params

        numberOfAtoms = len(atoms)

        atomNumberRequirementMet, logMessage = self._checkMotorAtomLimits(numberOfAtoms)

        if not atomNumberRequirementMet:
            atoms = self.struct.atoms[:]
            logMessage = (
                logMessage + " Motor will remain attached to the"
                " atoms listed in the <b>Attached Atoms</b> list in"
                " this property manager"
            )
            logMessage = orangemsg(logMessage)
            self.propMgr.updateMessage(logMessage)
            assert len(atoms) > 0

        self.struct.cancelled = False
        self.struct.force = force
        self.struct.stiffness = stiffness
        self.struct.enable_minimize = enable_minimize_state
        self.struct.color = color
        # Not sure if it is safe to do self.struct.atoms = atoms
        # Instead using 'setShaft method -- ninad 2007-10-09
        self.struct.setShaft(atoms)

        self.struct.findCenterAndAxis(atoms, self.win.glpane)

        self.propMgr.updateAttachedAtomListWidget(atomList=atoms)

        self.win.win_update()  # Update model tree
        self.win.assy.changed()
def atom_limit_exceeded_and_confirmed(parent, natoms, limit = 200):
    """
    Displays a warning message if 'natoms' exceeds 'limit'.
    Returns False if the number of atoms does not exceed the limit or if the
    user confirms that the jigs should still be created even though the limit
    was exceeded.
    If parent is 0, the message box becomes an application-global modal dialog
    box.
    If parent is a widget, the message box becomes modal relative to parent.
    """
    if natoms < limit:
        return False # Atom limit not exceeded.

    wmsg = "Warning: Creating a jig with " + str(natoms) \
        + " atoms may degrade performance.\nDo you still want to add the jig?"

    dialog = QMessageBox("Warning", wmsg,
                    QMessageBox.Warning,
                    QMessageBox.Yes,
                    QMessageBox.No,
                    QMessageBox.NoButton,
                    parent)

    # We want to add a "Do not show this message again." checkbox to the dialog
    # like this:
    #     checkbox = QCheckBox("Do not show this message again.", dialog)
    # The line of code above works, but places the checkbox in the upperleft
    # corner of the dialog, obscuring important text.  I'll fix this later.
    # Mark 051122.

    ret = dialog.exec_()

    if ret != QMessageBox.Yes:
        return True

    # Print warning msg in history widget whenever the user adds new jigs with
    # more than 'limit' atoms.
    wmsg = "Warning: " + str(natoms) + " atoms selected.  A jig with more " \
        "than " + str(limit) + " atoms may degrade performance."
    env.history.message(orangemsg(wmsg))

    return False # from atom_limit_exceeded_and_confirmed
Example #35
0
def atom_limit_exceeded_and_confirmed(parent, natoms, limit = 200):
    """
    Displays a warning message if 'natoms' exceeds 'limit'.
    Returns False if the number of atoms does not exceed the limit or if the
    user confirms that the jigs should still be created even though the limit
    was exceeded.
    If parent is 0, the message box becomes an application-global modal dialog
    box. 
    If parent is a widget, the message box becomes modal relative to parent.
    """
    if natoms < limit:
        return False # Atom limit not exceeded.

    wmsg = "Warning: Creating a jig with " + str(natoms) \
        + " atoms may degrade performance.\nDo you still want to add the jig?"
    
    dialog = QMessageBox("Warning", wmsg, 
                    QMessageBox.Warning, 
                    QMessageBox.Yes, 
                    QMessageBox.No, 
                    QMessageBox.NoButton, 
                    parent)

    # We want to add a "Do not show this message again." checkbox to the dialog
    # like this:
    #     checkbox = QCheckBox("Do not show this message again.", dialog)
    # The line of code above works, but places the checkbox in the upperleft
    # corner of the dialog, obscuring important text.  I'll fix this later.
    # Mark 051122.

    ret = dialog.exec_()

    if ret != QMessageBox.Yes:
        return True
    
    # Print warning msg in history widget whenever the user adds new jigs with
    # more than 'limit' atoms.
    wmsg = "Warning: " + str(natoms) + " atoms selected.  A jig with more " \
        "than " + str(limit) + " atoms may degrade performance."
    env.history.message(orangemsg(wmsg))
        
    return False # from atom_limit_exceeded_and_confirmed
Example #36
0
def _unset_some_open_bond_direction(atom, direction):
    """
    Find an open bond on atom with the specified bond direction set
    (error if you can't), and unset its bond direction.
    """
    assert direction in (-1, 1)

    didit = False
    for bond in atom.bonds:
        if bond.is_open_bond() and \
           bond.bond_direction_from(atom) == direction: # bugfix 080201, care which direction is set, not just that some dir was set
            bond.clear_bond_direction()
            didit = True
            break
        continue
    assert didit

    summary_format = "Warning: dna updater unset bond direction on [N] open bond(s)"
    env.history.deferred_summary_message(orangemsg(summary_format))
    return
Example #37
0
def include_dir_ok(include_dir):
    """
    Is this include_dir acceptable (or maybe acceptable)? Return (0, "") or (errorcode, errortext).
    """
    if env.debug():
        print "debug: include_dir_ok(include_dir = %r)" % (include_dir, )
    if os.path.isdir(include_dir):
        # ok, but warn if transforms.inc is not inside it
        if not os.path.exists(os.path.join(include_dir, "transforms.inc")):
            msg = "Warning: transforms.inc not present in POV include directory [%s]; rendering might not work" % (
                include_dir, )
            env.history.message(orangemsg(msg))
        if env.debug():
            print "debug: include_dir_ok returns 0 (ok)"
        return 0, ""  # ok
    else:
        if env.debug():
            print "debug: include_dir_ok returns 1 (Not found or not a directory)"
        return 1, "POV include directory: Not found or not a directory"  #e pathname might be too long for a dialog
    pass
def _unset_some_open_bond_direction(atom, direction):
    """
    Find an open bond on atom with the specified bond direction set
    (error if you can't), and unset its bond direction.
    """
    assert direction in (-1, 1)

    didit = False
    for bond in atom.bonds:
        if bond.is_open_bond() and \
           bond.bond_direction_from(atom) == direction: # bugfix 080201, care which direction is set, not just that some dir was set
            bond.clear_bond_direction()
            didit = True
            break
        continue
    assert didit

    summary_format = "Warning: dna updater unset bond direction on [N] open bond(s)"
    env.history.deferred_summary_message( orangemsg(summary_format) )
    return
Example #39
0
def debug_make_BorrowerChunk_raw(do_addmol = True):
    win = env.mainwindow()
    atomset = win.assy.selatoms
    if not atomset:
        env.history.message(redmsg("Need selected atoms to make a BorrowerChunk (for debugging only)"))
    else:
        atomset = dict(atomset) # copy it, since we shouldn't really add singlets to assy.selatoms...
        for atom in atomset.values(): # not itervalues, we're changing it in the loop!
            # BTW Python is nicer about this than I expected:
            # exceptions.RuntimeError: dictionary changed size during iteration
            for bp in atom.singNeighbors(): # likely bugs if these are not added into the set!
                atomset[bp.key] = bp
            assy = atom.molecule.assy # these are all the same, and we do this at least once
        chunk = BorrowerChunk(assy, atomset)
        if do_addmol:
            win.assy.addmol(chunk)
        import __main__
        __main__._bc = chunk
        env.history.message(orangemsg("__main__._bc = %s (for debugging only)" % quote_html(safe_repr(chunk))))
    win.win_update() #k is this done by caller?
    return
def _set_some_open_bond_direction(atom, direction):
    """
    Find a directional open bond on atom with no bond direction set
    (error if you can't), and set its bond direction to the specified one.
    """
    assert direction in (-1, 1)

    didit = False
    for bond in atom.bonds:
        if bond.is_open_bond() and bond.is_directional() and not bond.bond_direction_from(atom):
            bond.set_bond_direction_from(atom, direction)
            didit = True
            break
        continue
    assert didit

    summary_format = "Warning: dna updater set bond direction on [N] open bond(s)"
    env.history.deferred_summary_message(orangemsg(summary_format))
    # todo: refactor so orangemsg is replaced with a warning option
    # review: should we say in how many cases we picked one of several open bonds arbitrarily?
    return
 def kill(self, require_confirmation=True):
     """
     Delete the POV-Ray Scene node and its associated .pov file if it exists.
     If <require_confirmation> is True, make the user confirm first [for deleting the file and the node both, as one op].
     [WARNING: user confirmation is not yet implemented.]
     Otherwise, delete the file without user confirmation.
     """
     if os.path.isfile(self.povrayscene_file):
         if 0:  # Don't require confirmation for A8. Mark 060701. [but see comment below about why this is a bad bug]
             # if require_confirmation:
             msg = "Please confirm that you want to delete " + self.name
             from widgets.widget_helpers import PleaseConfirmMsgBox
             confirmed = PleaseConfirmMsgBox(msg)
             if not confirmed:
                 return
         # warn the user that you are about to remove what might be an irreplaceable rendering of a prior version
         # of the main file, without asking, or even checking if other nodes in this assy still point to it
         # [this warning added by bruce 060711 for Mac A8, not present in Windows A8]
         env.history.message(
             orangemsg("Warning: deleting file [%s]" %
                       self.povrayscene_file))
         # do it
         os.remove(self.povrayscene_file)
         #bruce 060711 comment -- the above policy is a dangerous bug, since you can copy a node (not changing the filename)
         # and then delete one of the copies. This should not silently delete the file!
         # (Besides, even if you decide not to delete the file, .kill() should still delete the node.)
         #   This behavior is so dangerous that I'm tempted to fix it for Mac A8 even though it's too late
         # to fix it for Windows A8. Certainly it ought to be reported and releasenoted. But I think I will refrain
         # from the temptation to fix it for Mac A8, since doing it well is not entirely trivial, and any big bug-difference
         # in A8 on different platforms might cause confusion. But at least I will add a history message, so the user knows
         # right away if it caused a problem. And it needs to be fixed decently well for A8.1. ###@@@
         #   As for a better behavior, it would be good (and not too hard) to find out if other nodes
         # in the same assy point to the same file, and not even ask (just don't delete the file) if they do.
         # If not, ask, but always delete the node itself.
         #   But this is not trivial, since during a recursive kill of a Group, I'm not sure we can legally scan the tree.
         # (And if we did, it would then be quadratic time to delete a very large series of POV-Ray nodes.)
         # So we need a dictionary from filenames to lists or dicts of nodes that might refer to that filename.
         #   Of course there should also (for any filenode) be CM commands to delete or rename the file,
         # or (if other nodes also point to it) to copy it so this node owns a unique one.
     Node.kill(self)
Example #42
0
    def viewNormalTo_NEW(self):
        """
        Set view to the normal vector of the plane defined by 3 or more
        selected atoms or a jig's (Motor or RectGadget) axis.
        """
        # This implementation has two serious problems:
        #   1. it selects a normal based on the atoms and not the axis of a jig (e.g. a moved rotary motor).
        #   2. doesn't consider selected jigs that have no atoms.
        # Bruce and I will discuss this and determine the best implem.  
        # For A7, I've decide to use the original version. This version will be reinstated in A8
        # after fixing these problems. mark 060322.

        cmd = greenmsg("Set View Normal To: ")

        atoms = self.assy.getSelectedAtoms()

        if len(atoms) < 3:
            # There is a problem when allowing only 2 selected atoms.
            # Changing requirement to 3 atoms fixes bug 1418. mark 060322
            msg = redmsg("Please select some atoms, jigs, and/or chunks, covering at least 3 atoms")
            env.history.message(cmd + msg)
            return

        pos = A( map( lambda a: a.posn(), atoms ) ) # build list of atom xyz positions.
        nears = [ self.glpane.out, self.glpane.up ]
        axis = compute_heuristic_axis( pos, 'normal', already_centered = False, nears = nears, dflt = None )

        if not axis:
            msg = orangemsg( "Warning: Normal axis could not be determined. No change in view." )
            env.history.message(cmd + msg)
            return

        # Compute the destination quat (q2).
        q2 = Q(V(0,0,1), axis)
        q2 = q2.conj()

        self.glpane.rotateView(q2)

        info = 'View set to normal of the plane defined by the selection.'
        env.history.message(cmd + info)
Example #43
0
 def cm_select_jigs_atoms(self): #bruce 050504
     nodeset = self.topmost_selected_nodes()
     otherpart = {} #bruce 050505 to fix bug 589
     did_these = {}
     nprior = len(self.assy.selatoms)
     for jig in nodeset:
         assert isinstance( jig, Jig) # caller guarantees they are all jigs
         # If we didn't want to desel the jig, I'd have to say:
             # note: this does not deselect the jig (good); and permit_pick_atoms would deselect it (bad);
             # so to keep things straight (not sure this is actually needed except to avoid a debug message),
             # just set SELWHAT_ATOMS here; this is legal because no chunks are selected. Actually, bugs might occur
             # in which that's not true... I forget whether I fixed those recently or only analyzed them (due to delays
             # in update event posting vs processing)... but even if they can occur, it's not high-priority to fix them,
             # esp since selection rules might get revised soon.
             ## self.assy.set_selwhat(SELWHAT_ATOMS)
         # but (I forgot when I wrote that) we *do* desel the jig,
         # so instead I can just say:
         self.assy.part.permit_pick_atoms() # changes selwhat and deselects all chunks, jigs, and groups
         # [bruce 050519 8pm]
         for atm in jig.atoms:
             if atm.molecule.part == jig.part:
                 atm.pick()
                 did_these[atm.key] = atm
             else:
                 otherpart[atm.key] = atm
         ## jig.unpick() # not done by picking atoms [no longer needed since done by permit_pick_atoms]
     msg = fix_plurals("Selected %d atom(s)" % len(did_these)) # might be 0, that's ok
     if nprior: #bruce 050519
         #e msg should distinguish between atoms already selected and also selected again just now,
         # vs already and not now; for now, instead, we just try to be ambiguous about that
         msg += fix_plurals(" (%d atom(s) remain selected from before)" % nprior)
     if otherpart:
         msg += fix_plurals(" (skipped %d atom(s) which were not in this Part)" % len(otherpart))
         msg = orangemsg(msg) # the whole thing, I guess
     env.history.message(msg)
     self.win.win_update()
     # note: caller (which puts up context menu) does
     # self.win.update_select_mode(); we depend on that. [### still true??]
     return
Example #44
0
    def kill(self, require_confirmation=True):
        """
        Delete the POV-Ray Scene node and its associated .pov file if it exists.
        If <require_confirmation> is True, make the user confirm first [for deleting the file and the node both, as one op].
        [WARNING: user confirmation is not yet implemented.]
        Otherwise, delete the file without user confirmation.
        """
        if os.path.isfile(self.povrayscene_file):
            if 0:  # Don't require confirmation for A8. Mark 060701. [but see comment below about why this is a bad bug]
                # if require_confirmation:
                msg = "Please confirm that you want to delete " + self.name
                from widgets.widget_helpers import PleaseConfirmMsgBox

                confirmed = PleaseConfirmMsgBox(msg)
                if not confirmed:
                    return
            # warn the user that you are about to remove what might be an irreplaceable rendering of a prior version
            # of the main file, without asking, or even checking if other nodes in this assy still point to it
            # [this warning added by bruce 060711 for Mac A8, not present in Windows A8]
            env.history.message(orangemsg("Warning: deleting file [%s]" % self.povrayscene_file))
            # do it
            os.remove(self.povrayscene_file)
            # bruce 060711 comment -- the above policy is a dangerous bug, since you can copy a node (not changing the filename)
            # and then delete one of the copies. This should not silently delete the file!
            # (Besides, even if you decide not to delete the file, .kill() should still delete the node.)
            #   This behavior is so dangerous that I'm tempted to fix it for Mac A8 even though it's too late
            # to fix it for Windows A8. Certainly it ought to be reported and releasenoted. But I think I will refrain
            # from the temptation to fix it for Mac A8, since doing it well is not entirely trivial, and any big bug-difference
            # in A8 on different platforms might cause confusion. But at least I will add a history message, so the user knows
            # right away if it caused a problem. And it needs to be fixed decently well for A8.1. ###@@@
            #   As for a better behavior, it would be good (and not too hard) to find out if other nodes
            # in the same assy point to the same file, and not even ask (just don't delete the file) if they do.
            # If not, ask, but always delete the node itself.
            #   But this is not trivial, since during a recursive kill of a Group, I'm not sure we can legally scan the tree.
            # (And if we did, it would then be quadratic time to delete a very large series of POV-Ray nodes.)
            # So we need a dictionary from filenames to lists or dicts of nodes that might refer to that filename.
            #   Of course there should also (for any filenode) be CM commands to delete or rename the file,
            # or (if other nodes also point to it) to copy it so this node owns a unique one.
        Node.kill(self)
Example #45
0
    def deposit_from_MMKit(self, atom_or_pos):
        """
        Deposit the library part being previewed into the 3D workspace
        Calls L{self.deposit_from_Library_page}

        @param atom_or_pos: If user clicks on a bondpoint in 3D workspace,
                            this is that bondpoint. NE1 will try to bond the
                            part to this bondpoint, by Part's hotspot(if exists)
                            If user double clicks on empty space, this gives
                            the coordinates at that point. This data is then
                            used to deposit the item.
        @type atom_or_pos: Array (vector) of coordinates or L{Atom}

        @return: (deposited_stuff, status_msg_text) Object deposited in the 3 D
                workspace. (Deposits the selected  part as a 'Group'. The status
                message text tells whether the Part got deposited.
        @rtype: (L{Group} , str)

        @attention: This method needs renaming. L{BuildAtoms_Command} still uses it
        so simply overriden here. B{NEEDS CLEANUP}.
        @see: L{self.deposit_from_Library_page}

        """
        deposited_stuff, status = self.command.deposit_from_Library_page(
            atom_or_pos)
        deposited_obj = 'Part'
        if deposited_stuff and self.pickit():
            for d in deposited_stuff[:]:
                d.pickatoms()

        if deposited_stuff:
            self.w.win_update()
            status = self.ensure_visible(deposited_stuff, status)
            env.history.message(status)
        else:
            env.history.message(orangemsg(status))

        return deposited_obj
Example #46
0
def _set_some_open_bond_direction(atom, direction):
    """
    Find a directional open bond on atom with no bond direction set
    (error if you can't), and set its bond direction to the specified one.
    """
    assert direction in (-1, 1)

    didit = False
    for bond in atom.bonds:
        if bond.is_open_bond() and \
           bond.is_directional() and \
           not bond.bond_direction_from(atom):
            bond.set_bond_direction_from(atom, direction)
            didit = True
            break
        continue
    assert didit

    summary_format = "Warning: dna updater set bond direction on [N] open bond(s)"
    env.history.deferred_summary_message(orangemsg(summary_format))
    # todo: refactor so orangemsg is replaced with a warning option
    # review: should we say in how many cases we picked one of several open bonds arbitrarily?
    return
Example #47
0
    def deposit_from_MMKit(self, atom_or_pos):
        """
        Deposit the library part being previewed into the 3D workspace
        Calls L{self.deposit_from_Library_page}

        @param atom_or_pos: If user clicks on a bondpoint in 3D workspace,
                            this is that bondpoint. NE1 will try to bond the 
                            part to this bondpoint, by Part's hotspot(if exists)
                            If user double clicks on empty space, this gives 
                            the coordinates at that point. This data is then 
                            used to deposit the item.
        @type atom_or_pos: Array (vector) of coordinates or L{Atom}

        @return: (deposited_stuff, status_msg_text) Object deposited in the 3 D 
                workspace. (Deposits the selected  part as a 'Group'. The status
                message text tells whether the Part got deposited.
        @rtype: (L{Group} , str)

        @attention: This method needs renaming. L{depositMode} still uses it 
        so simply overriden here. B{NEEDS CLEANUP}.
        @see: L{self.deposit_from_Library_page} 

        """
        deposited_stuff, status = self.deposit_from_Library_page(atom_or_pos)
        deposited_obj = 'Part'
        if deposited_stuff and self.pickit():
            for d in deposited_stuff[:]:
                d.pickatoms() 

        if deposited_stuff:
            self.w.win_update()                
            status = self.ensure_visible( deposited_stuff, status) 
            env.history.message(status)
        else:
            env.history.message(orangemsg(status)) 

        return deposited_obj
Example #48
0
def _readpdb(assy, 
             filename, 
             isInsert = False, 
             showProgressDialog = False, 
             chainId = None):
    """
    Read a Protein DataBank-format file into a single new chunk, which is 
    returned unless there are no atoms in the file, in which case a warning
    is printed and None is returned. (The new chunk (if returned) is in assy,
    but is not yet added into any Group or Part in assy -- caller must do that.)
    Unless isInsert = True, set assy.filename to match the file we read,
    even if we return None.
    
    @param assy: The assembly.
    @type  assy: L{assembly}
    
    @param filename: The PDB filename to read.
    @type  filename: string
    
    @param isInsert: If True, the PDB file will be inserted into the current
                     assembly. If False (default), the PDB is opened as the 
                     assembly.
    @param isInsert: boolean
    
    @param showProgressDialog: if True, display a progress dialog while reading
                               a file.
    @type  showProgressDialog: boolean
    
    @return: A chunk containing the contents of the PDB file.
    @rtype:  L{Chunk}
    
    @see: U{B{PDB File Format}<http://www.wwpdb.org/documentation/format23/v2.3.html>}
    """
        
    fi = open(filename,"rU")
    lines = fi.readlines()
    fi.close()
    
    dir, nodename = os.path.split(filename)
    if not isInsert:
        assy.filename = filename
    ndix = {}
    mol = Chunk(assy, nodename)
    numconects = 0

    atomname_exceptions = {
        "HB":"H", #k these are all guesses -- I can't find this documented 
                  # anywhere [bruce 070410]
        ## "HE":"H", ### REVIEW: I'm not sure about this one -- 
                    ###          leaving it out means it's read as Helium,
        # but including it erroneously might prevent reading an actual Helium 
        # if that was intended.
        # Guess for now: include it for ATOM but not HETATM. (So it's 
        # specialcased below, rather than being included in this table.)
        # (Later: can't we use the case of the 'E' to distinguish it from He?)
        "HN":"H",
     }
    
    # Create and display a Progress dialog while reading the MMP file. 
    # One issue with this implem is that QProgressDialog always displays 
    # a "Cancel" button, which is not hooked up. I think this is OK for now,
    # but later we should either hook it up or create our own progress
    # dialog that doesn't include a "Cancel" button. --mark 2007-12-06
    if showProgressDialog:
        _progressValue = 0
        _progressFinishValue = len(lines)
        win = env.mainwindow()
        win.progressDialog.setLabelText("Reading file...")
        win.progressDialog.setRange(0, _progressFinishValue)
        _progressDialogDisplayed = False
        _timerStart = time.time()
    for card in lines:
        key = card[:6].lower().replace(" ", "")
        if key in ["atom", "hetatm"]:
            ## sym = capitalize(card[12:14].replace(" ", "").replace("_", "")) 
            # bruce 080508 revision (guess at a bugfix for reading NE1-saved
            # pdb files):
            # get a list of atomnames to try; use the first one we recognize.
            # Note that full atom name is in columns 13-16 i.e. card[12:16];
            # see http://www.wwpdb.org/documentation/format2.3-0108-us.pdf,
            # page 156. The old code only looked at two characters,
            # card[12:14] == columns 13-14, and discarded ' ' and '_',
            # and capitalized (the first character only). The code as I revised
            # it on 070410 also discarded digits, and handled HB, HE, HN
            # (guesses) using the atomname_exceptions dict.
            name4 = card[12:16].replace(" ", "").replace("_", "")
            name3 = card[12:15].replace(" ", "").replace("_", "")
            name2 = card[12:14].replace(" ", "").replace("_", "")
            def nodigits(name):
                for bad in "0123456789":
                    name = name.replace(bad, "")
                return name
            atomnames_to_try = [
                name4, # as seems best according to documentation
                name3,
                name2, # like old code
                nodigits(name4),
                nodigits(name3),
                nodigits(name2) # like code as revised on 070410
            ]
            foundit = False
            for atomname in atomnames_to_try:
                atomname = atomname_exceptions.get(atomname, atomname)
                if atomname == "HE" and key == "atom":
                    atomname = "H" # see comment in atomname_exceptions
                sym = capitalize(atomname) # turns either 'he' or 'HE' into 'He'
                try:
                    PeriodicTable.getElement(sym)
                except:
                    # note: this typically fails with AssertionError 
                    # (not e.g. KeyError) [bruce 050322]
                    continue
                else:
                    foundit = True
                    break
                pass
            if not foundit:
                msg = "Warning: Pdb file: will use Carbon in place of unknown element %s in: %s" \
                    % (name4, card)
                print msg #bruce 070410 added this print
                env.history.message( redmsg( msg ))

                ##e It would probably be better to create a fake atom, so the 
                # CONECT records would still work.
                #bruce 080508 let's do that:
                sym = "C"
                
                # Better still might be to create a fake element, 
                # so we could write out the pdb file again
                # (albeit missing lots of info). [bruce 070410 comment]
                
                # Note: an advisor tells us:
                #   PDB files sometimes encode atomtypes,
                #   using C_R instead of C, for example, to represent sp2 
                #   carbons.
                # That particular case won't trigger this exception, since we
                # only look at 2 characters [eventually, after trying more, as of 080508],
                # i.e. C_ in that case. It would be better to realize this means
                # sp2 and set the atomtype here (and perhaps then use it when
                # inferring bonds,  which we do later if the file doesn't have 
                # any bonds). [bruce 060614/070410 comment]

            # Now the element name is in sym.
            xyz = map(float, [card[30:38], card[38:46], card[46:54]] )
            n = int(card[6:11])
            a = Atom(sym, A(xyz), mol)
            ndix[n] = a            
        elif key == "conect":
            try:
                a1 = ndix[int(card[6:11])]
            except:
                #bruce 050322 added this level of try/except and its message;
                # see code below for at least two kinds of errors this might
                # catch, but we don't try to distinguish these here. BTW this 
                # also happens as a consequence of not finding the element 
                # symbol, above,  since atoms with unknown elements are not 
                # created.
                env.history.message( redmsg( "Warning: Pdb file: can't find first atom in CONECT record: %s" % (card,) ))
            else:
                for i in range(11, 70, 5):
                    try:
                        a2 = ndix[int(card[i:i+5])]
                    except ValueError:
                        # bruce 050323 comment:
                        # we assume this is from int('') or int(' ') etc;
                        # this is the usual way of ending this loop.
                        break
                    except KeyError:
                        #bruce 050322-23 added history warning for this,
                        # assuming it comes from ndix[] lookup.
                        env.history.message( redmsg( "Warning: Pdb file: can't find atom %s in: %s" % (card[i:i+5], card) ))
                        continue
                    bond_atoms(a1, a2)
                    numconects += 1
            
        if showProgressDialog: # Update the progress dialog.
            _progressValue += 1
            if _progressValue >= _progressFinishValue:
                win.progressDialog.setLabelText("Building model...")
            elif _progressDialogDisplayed:
                win.progressDialog.setValue(_progressValue)
            else:
                _timerDuration = time.time() - _timerStart
                if _timerDuration > 0.25: 
                    # Display progress dialog after 0.25 seconds
                    win.progressDialog.setValue(_progressValue)
                    _progressDialogDisplayed = True
    
    if showProgressDialog: # Make the progress dialog go away.
        win.progressDialog.setValue(_progressFinishValue) 
    
    #bruce 050322 part of fix for bug 433: don't return an empty chunk
    if not mol.atoms:
        env.history.message( redmsg( "Warning: Pdb file contained no atoms"))
        return None
    if numconects == 0:
        msg = orangemsg("PDB file has no bond info; inferring bonds")
        env.history.message(msg)
        # let user see message right away (bond inference can take significant 
        # time) [bruce 060620]
        env.history.h_update() 
        inferBonds(mol)
    return mol
Example #49
0
 def orangemsg(self, msg, **kws):  #bruce 080201
     """
     Shorthand for self.message(orangemsg(msg), <options>).
     """
     self.message(orangemsg(msg), **kws)
     return
def delete_bare_atoms(
    changed_atoms
):  # rename; also make not delete, just error (### need to review error propogation system)
    """
    Delete excessively-bare atoms (defined as axis atoms without strand atoms,
     or any other PAM atoms that are not allowed to exist with as few neighbors
     as they have -- note that the rules for strand atoms are in flux as of
     080117 since the representation of single-stranded DNA is as well).

    [must tolerate killed atoms; can kill more atoms and break bonds;
     can record more changes to neighbors of deleted atoms]
    """
    # Q. Which changes recorded by our side effects are needed in subsequent
    # dna updater steps?
    # A. The changed neighbor atoms are needed, in case they're the only
    # indicator of a change to the chain they're on (especially if the killed
    # atom was in a different dna ladder). But their classes needn't be changed,
    # and their deletion can't cause any more atoms to become bare (due to
    # the current meaning of bare), so no earlier updater steps need to be
    # repeated.

    # Note: if these debug prefs are not both True, errors might occur in the
    # dna updater. The goal is for these errors to be harmless (just debug
    # prints). As of 071205 the defaults are True, False, respectively.
    # TODO: revise following code to debug-print when these prefs make it
    # refrain from killing a bare atom (giving a count, per PAM model).
    fix_PAM3 = pref_fix_bare_PAM3_atoms()
    fix_PAM5 = pref_fix_bare_PAM5_atoms()

    delete_these_atoms = []  # change to mark them as errors

    fix_these_bonds = {}  # id(bond) -> bond

    for atom in changed_atoms.itervalues():
        pam = atom.element.pam
        if pam:  # optimization
            if (pam == MODEL_PAM3 and fix_PAM3) or \
               (pam == MODEL_PAM5 and fix_PAM5):
                if not atom.killed():
                    if atom_is_bare(atom):
                        delete_these_atoms.append(atom)
                    else:
                        # Do something about rung bonds between mismatched PAM atoms
                        # (in pam model or pam3+5 properties)
                        # [bruce 080405 new feature, for PAM3+5 safety]
                        # Note: if this kills bonds, we'd need to do that first,
                        # then recheck atom_is_bare (or always check it later).
                        # But it doesn't.
                        #
                        #update 080407: maybe this is not necessary:
                        # - we could permit these mismatches until DnaLadders
                        #   are formed, then fix them much more easily;
                        # - or we could even permit them then
                        #   (each rail would be uniform within itself),
                        #   with a little extra complexity in PAM conversion,
                        #   but it might even be useful to display "one strand
                        #   in PAM5", for example.
                        # So for now, I won't finish this code here, though I'll
                        # leave it in for long enough to see if it prints
                        # anything; then it should be removed, in case it's
                        # slow. ##### @@@@@
                        for bond in atom.bonds:
                            if bond.is_rung_bond():
                                if not PAM_atoms_allowed_in_same_ladder(
                                        bond.atom1, bond.atom2):
                                    fix_these_bonds[id(bond)] = bond

    for bond in fix_these_bonds.values():
        # REVIEW: can one of its atoms be in delete_these_atoms?
        # (probably doesn't matter even if it is)
        print "*** need to fix this bad rung bond (nim, so bugs will ensue): %r" % bond  #####
        # pam model mismatch: mark them as errors, so not in any chains or ladders
        # pam option mismatch: reset options to default on all chunks connected by rung bonds (or could mark as errors)
        # other (if possible) (eg display styles, if that matters) --
        #  those differences would be ok here, only matters along axis
        #  (we'll make the func distinguish this if it ever looks at those)
        #
        # no need to be fast, this only happens for rare errors
        a1, a2 = bond.atom1, bond.atom2
        if a1.element.pam != a2.element.pam:
            # since it's a rung bond, we know the pams are both set
            print " nim: mark as errors", a1, a2  ## NIM here, look up how other code does it before propogation
        elif a1.molecule.display_as_pam != a2.molecule.display_as_pam or \
             a1.molecule.save_as_pam != a2.molecule.save_as_pam:
            # transclose to find chunks connected by rung bonds, put in a dict, reset pam props below
            print " nim: reset pam props starting at", a1, a2
        continue

    for atom in delete_these_atoms:
        #bruce 080515: always emit a deferred_summary_message,
        # since it can seem like a bug otherwise
        # (review this, if it happens routinely)
        summary_format = \
            "Warning: dna updater deleted [N] \"bare\" %s pseudoatom(s)" % \
            ( atom.element.symbol, )
        env.history.deferred_summary_message(orangemsg(summary_format))
        atom.kill()

    return
Example #51
0
    def _convert_selection_to_pam_model(self,
                                        which_pam,
                                        commandname = "",
                                        make_ghost_bases = True, # only implemented for PAM3, so far
                                        ## remove_ghost_bases_from_PAM3 = True
                                       ): #bruce 080413
        """
        Convert the selected atoms (including atoms into selected chunks),
        which don't have errors (in the atoms or their dnaladders), into
        the specified pam model (some, none, or all might already be
        in that pam model), along with all atoms in the same basepairs,
        but only for kinds of ladders for which conversion is yet
        implemented. Print summaries to history.

        This is a user operation, so the dna updater has run
        and knows which atoms are errors, knows ladders of atoms, etc.
        We take advantage of that to simplify the implementation.
        """
        if not commandname:
            commandname = "Convert to %s" % which_pam # kluge, doesn't matter yet
                
        # find all selected atoms (including those in selected chunks)
        atoms = dict(self.selatoms)
        for chunk in self.selmols:
            atoms.update(chunk.atoms)

        if not atoms:
            env.history.message( greenmsg(commandname + ": ") + redmsg("Nothing selected.") )
            return
        
        # expand them to cover whole basepairs -- use ladders to help?
        # (the atoms with errors are not in valid ladders, so that
        #  is also an easy way to exclude those)

        num_atoms_with_good_ladders = 0
        ladders = {}
        ghost_bases = {} # initially only holds PAM5 ghost bases
        
        for atom in atoms.itervalues():
            try:
                ladder = atom.molecule.ladder 
            except AttributeError: # for .ladder
                continue # not the right kind of atom, etc
            if not ladder or not ladder.valid:
                continue
            if ladder.error:
                continue
            if not ladder.strand_rails: # bare axis
                continue
            num_atoms_with_good_ladders += 1
            ladders[ladder] = ladder
            # note: if atom is Pl, its Ss neighbors are treated specially
            # lower down in this method
            if atom.ghost and atom.element.pam == MODEL_PAM5:
                ghost_bases[atom.key] = atom
            continue

        orig_len_atoms = len(atoms) # for history messages, in case we add some

        # now iterate on the ladders, scanning their atoms to find the ones
        # in atoms, noting every touched baseindex

        # BUG: this does not notice Pls which are in atoms without either neighbor Ss being in atoms.
        # Future: fix by noticing them above in atom loop; see comment there.

        atoms_to_convert = {}
        ladders_to_inval = {}

        number_of_basepairs_to_convert = 0
        number_of_unpaired_bases_to_convert = 0

        ladders_needing_ghost_bases = {} # maps ladder to (ladder, list of indices) #bruce 080528
        
        for ladder in ladders:
            # TODO: if ladder can't convert (nim for that kind of ladder),
            # say so as a summary message, and skip it. (But do we know how
            # many atoms in our dict it had? if possible, say that too.)

            # TODO: if ladder doesn't need to convert (already in desired model),
            # skip it.
            
            length = len(ladder)
            index_set = {} # base indexes in ladder of basepairs which touch our dict of atoms
            rails = ladder.all_rails()
            if len(ladder.strand_rails) not in (1, 2):
                continue
            for rail in rails:
                for ind in range(length):
                    atom = rail.baseatoms[ind]
                    if atom.key in atoms:
                        # convert this base pair
                        index_set[ind] = ind
                        pass
                    continue
                continue
            # conceivable that for some ladders we never hit them;
            # for now, warn but skip them in that case
            if not index_set:
                print "unexpected: scanned %r but found nothing to convert (only Pls selected??)" % ladder # env.history?
            else:
                if len(ladder.strand_rails) == 2:
                    number_of_basepairs_to_convert += len(index_set)
                else:
                    number_of_unpaired_bases_to_convert += len(index_set)
                        # note: we do this even if the conversion will fail
                        # (as it does initially for single strand domains),
                        # since the summary error message from that is useful.
                    if make_ghost_bases and ladder.can_make_ghost_bases():
                        # initially, this test rules out free floating single strands;
                        # later we might be able to do this for them, which is why
                        # we do the test using that method rather than directly.
                        ladders_needing_ghost_bases[ladder] = (ladder, index_set.values())
                # see related code in _cmd_convert_to_pam method
                # in DnaLadder_pam_conversion.py
                ladders_to_inval[ladder] = ladder
                if 0 in index_set or (length - 1) in index_set:
                    for ladder2 in ladder.strand_neighbor_ladders():
                        # might contain Nones or duplicate entries
                        if ladder2 is not None:
                            ladders_to_inval[ladder2] = ladder2 # overkill if only one ind above was found
                for rail in rails:
                    baseatoms = rail.baseatoms
                    for ind in index_set:
                        atom = baseatoms[ind]
                        atoms_to_convert[atom.key] = atom # note: we also add ghost base atoms, below
                pass
            continue # next ladder

        if not atoms_to_convert:
            assert not number_of_basepairs_to_convert
            assert not number_of_unpaired_bases_to_convert
            assert not ladders_needing_ghost_bases
            if num_atoms_with_good_ladders < orig_len_atoms:
                # warn if we're skipping some atoms [similar code occurs twice in this method]
                msg = "%d atom(s) skipped, since not in valid, error-free DnaLadders"
                env.history.message( greenmsg(commandname + ": ") + orangemsg("Warning: " + fix_plurals(msg)))
            env.history.message( greenmsg(commandname + ": ") + redmsg("Nothing found to convert.") )
            return
        
        # print a message about what we found to convert
        what1 = what2 = ""
        if number_of_basepairs_to_convert:
            what1 = fix_plurals( "%d basepair(s)" % number_of_basepairs_to_convert )
        if number_of_unpaired_bases_to_convert:
            # doesn't distinguish sticky ends from free-floating single strands (fix?)
            what2 = fix_plurals( "%d unpaired base(s)" % number_of_unpaired_bases_to_convert )
        if what1 and what2:
            what = what1 + " and " + what2
        else:
            what = what1 + what2
        
        env.history.message( greenmsg(commandname + ": ") + "Will convert %s ..." % what )

        # warn if we're skipping some atoms [similar code occurs twice in this method]
        if num_atoms_with_good_ladders < orig_len_atoms:
            msg = "%d atom(s) skipped, since not in valid, error-free DnaLadders"
            env.history.message( orangemsg("Warning: " + fix_plurals(msg)))

        print "%s will convert %d atoms, touching %d ladders" % \
              ( commandname, len(atoms_to_convert), len(ladders_to_inval) )

        # make ghost bases as needed for this conversion (if enabled -- not by default since not yet working ####)
        # (this must not delete any baseatoms in atoms, or run the dna updater
        #  or otherwise put atoms into different ladders, but it can make new
        #  atoms in new chunks, as it does)

        if debug_pref_enable_pam_convert_sticky_ends():
            for ladder, index_list in ladders_needing_ghost_bases.itervalues():
                baseatoms = ladder.make_ghost_bases(index_list) # note: index_list is not sorted; that's ok
                    # note: this makes them in a separate chunk, and returns them
                    # as an atom list, but doesn't add the new chunk to the ladder.
                    # the next dna updater run will fix that (making a new ladder
                    # that includes all atoms in baseatoms and the old ladder).
                for ind in index_list:
                    atom = baseatoms[ind]
                    atoms_to_convert[atom.key] = atom
        
        # cause the dna updater (which would normally run after we return,
        #  but is also explicitly run below) to do the rest of the conversion
        # (and report errors for whatever it can't convert)
        
        for ladder in ladders_to_inval:
            ladder._dna_updater_rescan_all_atoms()

        for atom in atoms_to_convert:
            _f_baseatom_wants_pam[atom] = which_pam

        # run the dna updater explicitly
        
        print "about to run dna updater for", commandname
        self.assy.update_parts() # not a part method
            # (note: this catches dna updater exceptions and turns them into redmsgs.)
        print "done with dna updater for", commandname

        if debug_pref_remove_ghost_bases_from_pam3():
            # note: in commented out calling code above, this was a flag
            # option, remove_ghost_bases_from_PAM3;
            # that will be revived if we have a separate command for this.
            #
            # actually we only remove the ones we noticed as PAM5 above,
            # and succeeded in converting to PAM3.
            good = bad = 0
            for atom in ghost_bases.values(): # must not be itervalues
                if atom.element.pam == MODEL_PAM3:
                    good += 1
                    for n in atom.neighbors():
                        if n.is_ghost():
                            ghost_bases[n.key] = n
                else:
                    bad += 1
                    del ghost_bases[atom.key]
                continue
            if good:
                print "removing %d ghost base(s) we converted to PAM3" % good
            if bad:
                print "leaving %d ghost base(s) we didn't convert to PAM3" % bad
            if not bool(good) == bool(ghost_bases): # should never happen
                print "bug: bool(good) != bool(ghost_bases), for", good, ghost_bases
            del good, bad
            if ghost_bases:
                for atom in ghost_bases.itervalues():
                    atom.kill()
                    # todo: probably should use prekill code to avoid
                    # intermediate bondpoint creation, even though there
                    # are not usually a lot of atoms involved at once
                    continue
                print "about to run dna updater 2nd time for", commandname
                self.assy.update_parts()
                print "done with dna updater 2nd time for", commandname
            pass

        env.history.message( greenmsg( commandname + ": " + "Done." ))

        self.assy.w.win_update()

        return
Example #52
0
def _readgms(assy, filename, isInsert=False):
    """
    Read the atoms from a GAMESS DAT file into a single new chunk, which is returned,
    unless there are no atoms in the file, in which case a warning is printed
    and None is returned. (The new chunk (if returned) is in assy, but is not
    yet added into any Group or Part in assy -- caller must do that.)
    """
    fi = open(filename, "rU")
    lines = fi.readlines()
    fi.close()

    dir, nodename = os.path.split(filename)
    ndix = {}
    mol = Chunk(assy, nodename)
    countdown = 0
    equilibruim_found = False
    atoms_found = False

    for card in lines:

        if failpat.search(
                card):  # GAMESS Aborted.  No atom data will be found.
            print card
            break

        # If this card is found:
        # "1     ***** EQUILIBRIUM GEOMETRY LOCATED *****\n"
        # we know we have a successfully optimized structure/set of atoms.
        # If this card is not found, the optimization failed for some reason.
        # Atom positions begin soon after this card.
        if card == "1     ***** EQUILIBRIUM GEOMETRY LOCATED *****\n":
            equilibruim_found = True
            continue

        # The atom positions we want ALWAYS begin 2 lines after this card:
        # " COORDINATES OF ALL ATOMS ARE (ANGS)\n"
        # which follows the previous card.
        # This is one way to fix the problem mentioned above.
        # I've commented the code below out since it needs further work to do what
        # we need, and there is a chance we will not need this if GAMESS-US has
        # the same number of lines (6) after the "EQUILIBRIUM" card above.
        #
        # P.S. The reason we do not just look for this card by itself is that there
        # can be many of them.  There is only one "EQUILIBRIUM" card, and the
        # good atoms follow that card.
        # 050624 Mark

        if equilibruim_found:
            if card == " COORDINATES OF ALL ATOMS ARE (ANGS)\n":
                atoms_found = True
                reading_atoms = True
                countdown = 2
                continue

        if not equilibruim_found or not atoms_found:
            continue

        if countdown:
            countdown -= 1
            #            print countdown, card # for debugging only.
            continue

        # The current card contains atom type and position.

        n = 0

        if reading_atoms:
            if len(card) < 10:
                reading_atoms = False  # Finished reading atoms.
                break
            m = irecpat.match(card)
            sym = capitalize(m.group(1))
            try:
                PeriodicTable.getElement(sym)
            except:
                env.history.message(
                    redmsg(
                        "Warning: GAMESS DAT file: unknown element %s in: %s" %
                        (sym, card)))
            else:
                xyz = map(float, (m.group(2), m.group(3), m.group(4)))
                a = Atom(sym, A(xyz), mol)
                ndix[n] = a
                n += 1

    # Don't return an empty chunk.
    if not mol.atoms:
        msg = "Warning: GAMESS file contains no equilibrium geometry.  No atoms read into part."
        env.history.message(redmsg(msg))
        return None

    # Need to compute and add bonds for this chunk.  I'll ask Bruce how to best accomplish this.
    # In the meantime, let's warn the user that no bonds have been formed since it
    # is impossible to see this in vdW display mode.
    # Mark 050623.
    msg = "Warning: Equilibrium geometry found.  Atoms read into part, but there are no bonds."
    env.history.message(orangemsg(msg))
    return mol
    def raytrace_scene(self, tmpscene=False):
        """
        Render scene. 
        If tmpscene is False, the INI and pov files are written to the 'POV-Ray Scene Files' directory.
        If tmpscene is True, the INI and pov files are written to a temporary directory (~/Nanorex/POV-Ray).
        Callers should set <tmpscene> = True when they want to render the scene but don't need to 
        save the files and create a POV-Ray Scene node in the MT (i.e. 'View > POV-Ray').
        The caller is responsible for adding the POV-Ray Scene node (self) to the model tree, if desired.
        Prints any necessary error messages to history; returns nothing.
        """
        #bruce 060710 corrected inaccuracies in docstring
        cmd = greenmsg("POV-Ray: ")
        if env.debug():
            #bruce 060707 (after Windows A8, before Linux/Mac A8)
            # compromise with what's best, so it can be ok for A8 even if only on some platforms
            env.history.message(_graymsg("POV-Ray: "))
            env.history.h_update()
            env.history.widget.update(
            )  ###@@@ will this help? is it safe? should h_update do it?

        ini, pov, out = self.get_povfile_trio(tmpscene)

        if not ini:
            ## return 1, "Problem getting POV-Ray filename trio."
            # [bruce 060710 replaced the above with the following, since it no longer matches the other return statements, or any calls]
            env.history.message(
                cmd + redmsg("Problem getting POV-Ray filename trio."))
            ###e should fix this to improve the message, by including errortext from get_povfile_trio retval (which is nim)
            return

        if tmpscene or not os.path.isfile(self.povrayscene_file):
            # write a new .pov file and save its name in self
            #
            #bruce 060711 comment (about a bug, not yet reported): ###@@@
            #   If an existing pov file has unexpectedly gone missing,
            # this code (I think) rerenders the current model, without even informing the user of the apparent error.
            #   That is extremely bad behavior, IMHO. What it ought to do is put up a dialog to inform the
            # user that the file is missing, and allow one of three actions: cancel, rerender current model,
            # or browse for the file to try to find it. If that browse is cancelled, it should offer the other
            # options, or if that finds the file but it's external, it should offer to copy it or make an
            # external link (or cancel), and then to continue or do no more. All this is desirable for any kind
            # of file node, not just PovrayScene. As it is, this won't be fixed for Mac A8; don't know about 8.1.
            self.povrayscene_file = pov
            writepovfile(self.assy.part, self.assy.o, self.povrayscene_file)
            # bruce 060711 question (possible bug): what sets self.width, self.height,  self.output_type in this case,
            # if the ones used by writepovfile differ from last time they were set in this node?
            # Guess: nothing does (bug, not yet reported). ###@@@

        # figure out renderer to use (POV-Ray or MegaPOV), its path, and its include_dir
        # (note: this contains most of the error checks that used to be inside launch_povray_or_megapov)
        # [bruce 060711 for Mac A8]
        win = self.assy.w
        ask_for_help = True  # give user the chance to fix problems in the prefs dialog
        errorcode, errortext_or_info = decode_povray_prefs(win,
                                                           ask_for_help,
                                                           greencmd=cmd)
        if errorcode:
            errortext = errortext_or_info
            env.history.message(
                cmd + redmsg(errortext)
            )  # redmsg in Mac A8, orangemsg in Windows A8 [bruce 060711]
            return
        info = errortext_or_info
        (program_nickname, program_path, include_dir) = info

        pov = self.povrayscene_file  ###k btw, is this already true?

        #k is out equal to whatever in self might store it, if anything? maybe it's not stored in self.

        write_povray_ini_file(ini, pov, out, info, self.width, self.height,
                              self.output_type)

        if tmpscene:
            msg = "Rendering scene. Please wait..."
        else:
            msg = "Rendering raytrace image from POV-Ray Scene file. Please wait..."
        env.history.message(cmd + msg)
        env.history.h_update(
        )  #bruce 060707 (after Windows A8, before Linux/Mac A8): try to make this message visible sooner
        # (doesn't work well enough, at least on Mac -- do we need to emit it before write_povray_ini_file?)
        env.history.widget.update(
        )  ###@@@ will this help? is it safe? should h_update do it?
        ###e these history widget updates fail to get it to print. Guess: we'd need qapp process events. Fix after Mac A8.
        # besides, we need this just before the launch call, not here.

        if os.path.exists(
                out
        ):  #bruce 060711 in Mac A8 not Windows A8 (probably all of Mac A8 code will also be in Linux A8)
            #e should perhaps first try moving the file to a constant name, so user could recover it manually if they wanted to
            #e (better yet, we should also try to avoid this situation when choosing the filename)
            msg = "Warning: image file already exists; removing it first [%s]" % out
            env.history.message(cmd + orangemsg(msg))
            try:
                os.remove(out)
            except:
                # this code was tested with a fake exception [060712 1041am]
                msg1 = "Problem removing old image file"
                msg2a = " [%s]" % out
                msg2b = "-- will try to overwrite it, "\
                      "but undetected rendering errors might leave it unchanged [%s]" % out
                print_compact_traceback("%s: " % (msg1 + msg2a))
                msg = redmsg(msg1) + msg2b
                #e should report the exception text in the history, too
                env.history.message(msg)
            pass

        # Launch raytrace program (POV-Ray or MegaPOV)
        errorcode, errortext = launch_povray_or_megapov(win, info, ini)

        if errorcode:
            env.history.message(
                cmd + redmsg(errortext)
            )  # redmsg in Mac A8, orangemsg in Windows A8 [bruce 060711]
            return

        #bruce 060707 (after Windows A8, before Linux/Mac A8): make sure the image file exists.
        # (On Mac, on that date [not anymore, 060710], we get this far (no error return, or maybe another bug hid one),
        # but the file is not there.)
        if not os.path.exists(out):
            msg = "Error: %s program finished, but failed to produce expected image file [%s]" % (
                program_nickname, out)
            env.history.message(cmd + redmsg(msg))
            return

        env.history.message(cmd + "Rendered image: " + out)

        # Display image in a window.
        imageviewer = ImageViewer(out, win)
        #bruce 060707 comment: if the file named <out> doesn't exist, on Mac,
        # this produces a visible and draggable tiny window, about 3 pixels wide and maybe 30 pixels high.
        imageviewer.display()

        return  # from raytrace_scene out
Example #54
0
    def _apply_bondTool_on_selected_atoms(self):
        """
        Converts the bonds between the selected atoms to one specified by
        self.graphicsMode..bondclick_v6. Example: When user selects all atoms in
        a nanotube and clicks 'graphitic' bond button, it converts all the
        bonds between the selected atoms to graphitic bonds.
        @see: self.activateBondsTool()
        @see: self.changeBondTool()
        @see: bond_utils.apply_btype_to_bond()
        @see: BuildAtoms_GraphicsMode.bond_change_type()
        """
        #Method written on 2008-05-04 to support NFR bug 2832. This need to
        #be reviewed for further optimization (not urgent) -- Ninad
        #This flag is set while activating the bond tool.
        #see self.activateBondsTool()
        if self._suppress_apply_bondTool_on_selected_atoms:
            return

        bondTypeString = self.getBondTypeString()

        #For the history message
        converted_bonds = 0
        non_converted_bonds = 0
        deleted_bonds = 0
        #We wil check the bond dictionary to see if a bond between the
        #selected atoms is already operated on.
        bondDict = {}

        bondList = []
        #all selected atoms
        atoms = self.win.assy.selatoms.values()
        for a in atoms:
            for b in a.bonds[:]:
                #If bond 'b' is already in the bondDict, skip this
                #iteration.
                if bondDict.has_key(id(b)):
                    continue
                else:
                    bondDict[id(b)] = b
                #neigbor atom of the atom 'a'
                neighbor = b.other(a)

                if neighbor.element != Singlet and neighbor.picked:
                    bond_type_changed = self.graphicsMode.bond_change_type(
                        b,
                        allow_remake_bondpoints=True,
                        suppress_history_message=True)
                    if bond_type_changed:
                        converted_bonds += 1
                    else:
                        non_converted_bonds += 1


        msg = "%d bonds between selected atoms "\
            "converted to %s" %(converted_bonds,
                                bondTypeString)
        msg2 = ''
        if non_converted_bonds:
            msg2 = orangemsg(" [Unable to convert %d "\
                             "bonds to %s]"%(non_converted_bonds,
                                             bondTypeString))
        if msg2:
            msg += msg2

        if msg:
            env.history.message(msg)

        self.glpane.gl_update()
Example #55
0
def delete_bare_atoms( changed_atoms): # rename; also make not delete, just error (### need to review error propogation system)
    """
    Delete excessively-bare atoms (defined as axis atoms without strand atoms,
     or any other PAM atoms that are not allowed to exist with as few neighbors
     as they have -- note that the rules for strand atoms are in flux as of
     080117 since the representation of single-stranded DNA is as well).

    [must tolerate killed atoms; can kill more atoms and break bonds;
     can record more changes to neighbors of deleted atoms]
    """
    # Q. Which changes recorded by our side effects are needed in subsequent
    # dna updater steps?
    # A. The changed neighbor atoms are needed, in case they're the only
    # indicator of a change to the chain they're on (especially if the killed
    # atom was in a different dna ladder). But their classes needn't be changed,
    # and their deletion can't cause any more atoms to become bare (due to
    # the current meaning of bare), so no earlier updater steps need to be
    # repeated.

    # Note: if these debug prefs are not both True, errors might occur in the
    # dna updater. The goal is for these errors to be harmless (just debug
    # prints). As of 071205 the defaults are True, False, respectively.
    # TODO: revise following code to debug-print when these prefs make it
    # refrain from killing a bare atom (giving a count, per PAM model).
    fix_PAM3 = pref_fix_bare_PAM3_atoms()
    fix_PAM5 = pref_fix_bare_PAM5_atoms()

    delete_these_atoms = [] # change to mark them as errors

    fix_these_bonds = {} # id(bond) -> bond
    
    for atom in changed_atoms.itervalues():
        pam = atom.element.pam
        if pam: # optimization
            if (pam == MODEL_PAM3 and fix_PAM3) or \
               (pam == MODEL_PAM5 and fix_PAM5):
                if not atom.killed():
                    if atom_is_bare(atom):
                        delete_these_atoms.append(atom)
                    else:
                        # Do something about rung bonds between mismatched PAM atoms
                        # (in pam model or pam3+5 properties)
                        # [bruce 080405 new feature, for PAM3+5 safety]
                        # Note: if this kills bonds, we'd need to do that first,
                        # then recheck atom_is_bare (or always check it later).
                        # But it doesn't.
                        #
                        #update 080407: maybe this is not necessary:
                        # - we could permit these mismatches until DnaLadders
                        #   are formed, then fix them much more easily;
                        # - or we could even permit them then
                        #   (each rail would be uniform within itself),
                        #   with a little extra complexity in PAM conversion,
                        #   but it might even be useful to display "one strand
                        #   in PAM5", for example.
                        # So for now, I won't finish this code here, though I'll
                        # leave it in for long enough to see if it prints
                        # anything; then it should be removed, in case it's
                        # slow. ##### @@@@@
                        for bond in atom.bonds:
                            if bond.is_rung_bond():
                                if not PAM_atoms_allowed_in_same_ladder(bond.atom1, bond.atom2):
                                    fix_these_bonds[id(bond)] = bond

    for bond in fix_these_bonds.values():
        # REVIEW: can one of its atoms be in delete_these_atoms?
        # (probably doesn't matter even if it is)
        print "*** need to fix this bad rung bond (nim, so bugs will ensue): %r" % bond #####
        # pam model mismatch: mark them as errors, so not in any chains or ladders
        # pam option mismatch: reset options to default on all chunks connected by rung bonds (or could mark as errors)
        # other (if possible) (eg display styles, if that matters) --
        #  those differences would be ok here, only matters along axis
        #  (we'll make the func distinguish this if it ever looks at those)
        #
        # no need to be fast, this only happens for rare errors
        a1, a2 = bond.atom1, bond.atom2
        if a1.element.pam != a2.element.pam:
            # since it's a rung bond, we know the pams are both set
            print " nim: mark as errors", a1, a2 ## NIM here, look up how other code does it before propogation
        elif a1.molecule.display_as_pam != a2.molecule.display_as_pam or \
             a1.molecule.save_as_pam != a2.molecule.save_as_pam:
            # transclose to find chunks connected by rung bonds, put in a dict, reset pam props below
            print " nim: reset pam props starting at", a1, a2
        continue
    
    for atom in delete_these_atoms:
        #bruce 080515: always emit a deferred_summary_message,
        # since it can seem like a bug otherwise
        # (review this, if it happens routinely)
        summary_format = \
            "Warning: dna updater deleted [N] \"bare\" %s pseudoatom(s)" % \
            ( atom.element.symbol, )
        env.history.deferred_summary_message( orangemsg(summary_format) )
        atom.kill()

    return
Example #56
0
def writepdb(part, 
             filename, 
             mode = 'w', 
             excludeFlags = EXCLUDE_BONDPOINTS | EXCLUDE_HIDDEN_ATOMS
             ):
    """
    Write a PDB file of the I{part}.
    
    @param part: The part.
    @type  part: assembly
    
    @param filename: The fullpath of the PDB file to write. 
                     We don't care if it has the .pdb extension or not.
    @type  filename: string
                 
    @param mode: 'w' for writing (the default)
                 'a' for appending
    @type  mode: string
    
    @param excludeFlags: used to exclude certain atoms from being written, 
        where:
        WRITE_ALL_ATOMS = 0 (even writes hidden and invisble atoms)
        EXCLUDE_BONDPOINTS = 1 (excludes bondpoints)
        EXCLUDE_HIDDEN_ATOMS = 2 (excludes invisible atoms)
        EXCLUDE_DNA_ATOMS = 4 (excludes PAM3 and PAM5 pseudo atoms)
        EXCLUDE_DNA_AXIS_ATOMS = 8 (excludes PAM3 axis atoms)
        EXCLUDE_DNA_AXIS_BONDS = 16 (supresses PAM3 axis bonds)
    @type  excludeFlags: integer
    
    @note: Atoms and bonds of hidden chunks are never written.
    
    @see: U{B{PDB File Format}<http://www.wwpdb.org/documentation/format23/v2.3.html>}
    """

    if mode != 'a': # Precaution. Mark 2007-06-25
        mode = 'w'
    
    f = open(filename, mode) 
    # doesn't yet detect errors in opening file [bruce 050927 comment]
    
    # Atom object's key is the key, the atomSerialNumber is the value  
    atomsTable = {}
    # Each element of connectLists is a list of atoms to be connected with the
    # 1st atom in the list, i.e. the atoms to write into a CONECT record
    connectLists = []
    
    atomSerialNumber = 1

    from protein.model.Protein import enableProteins
    
    def exclude(atm): #bruce 050318
        """
        Exclude this atom (and bonds to it) from the file under the following
        conditions (as selected by excludeFlags):
            - if it is a singlet
            - if it is not visible
            - if it is a member of a hidden chunk
            - some dna-related conditions (see code for details)
        """
        # Added not visible and hidden member of chunk. This effectively deletes
        # these atoms, which might be considered a bug.
        # Suggested solutions:
        # - if the current file is a PDB and has hidden atoms/chunks, warn user
        #   before quitting NE1 (suggest saving as MMP).
        # - do not support native PDB. Open PDBs as MMPs; only allow export of
        #   PDB.
        # Fixes bug 2329. Mark 070423
        
        if excludeFlags & EXCLUDE_BONDPOINTS:
            if atm.element == Singlet: 
                return True # Exclude
        if excludeFlags & EXCLUDE_HIDDEN_ATOMS:
            if not atm.visible():
                return True # Exclude
        if excludeFlags & EXCLUDE_DNA_AXIS_ATOMS:
##            if atm.element.symbol in ('Ax3', 'Ae3'):
            #bruce 080320 bugfix: revise to cover new elements and PAM5.
            if atm.element.role == 'axis':
                return True # Exclude
        if excludeFlags & EXCLUDE_DNA_ATOMS:
            # PAM5 atoms begin at 200.
            #
            # REVIEW: better to check atom.element.pam?
            # What about "carbon nanotube pseudoatoms"?
            # [bruce 080320 question]
            if atm.element.eltnum >= 200:
                return True # Exclude
        # Always exclude singlets connected to DNA p-atoms.
        if atm.element == Singlet: 
            for a in atm.neighbors():
                if a.element.eltnum >= 200:
                    # REVIEW: see above comment about atom.element.pam vs >= 200
                    return True
        return False # Don't exclude.

    excluded = 0
    molnum   = 1
    chainIdChar  = 65 # ASCII "A"
    
    if mode == 'w':
        writePDB_Header(f)
        
    for mol in part.molecules:
        if mol.hidden:
            # Atoms and bonds of hidden chunks are never written.
            continue
        for a in mol.atoms.itervalues():
            if exclude(a):
                excluded += 1
                continue
            atomConnectList = []
            
            atomsTable[a.key] = atomSerialNumber
            if enableProteins:
                # piotr 080709 : Use more robust ATOM output code for Proteins.
                resId = 1
                resName = "UNK"
                atomName = a.element.symbol
                if mol.protein:
                    res = mol.protein.get_residuum(a)
                    if res:
                        resId = res.get_id()
                        resName = res.get_three_letter_code()
                        atomName = res.get_atom_name(a)
                writepdb_atom(a, 
                              f, 
                              atomSerialNumber, 
                              atomName, 
                              chr(chainIdChar), 
                              resId, 
                              resName)
            else:
                a.writepdb(f, atomSerialNumber, chr(chainIdChar))
            atomConnectList.append(a)
    
            for b in a.bonds:
                a2 = b.other(a)
                # The following removes bonds b/w PAM3 axis atoms.
                if excludeFlags & EXCLUDE_DNA_AXIS_BONDS:
##                    if a.element.symbol in ('Ax3', 'Ae3'):
##                        if a2.element.symbol in ('Ax3', 'Ae3'):
##                            continue
                    #bruce 080320 bugfix: revise to cover new elements and PAM5.
                    if a.element.role == 'axis' and a2.element.role == 'axis':
                            continue
                        
                if a2.key in atomsTable:
                    assert not exclude(a2) # see comment below
                    atomConnectList.append(a2)
                #bruce 050318 comment: the old code wrote every bond twice
                # (once from each end). I doubt we want that, so now I only
                # write them from the 2nd-seen end. (This also serves to
                # not write bonds to excluded atoms, without needing to check
                # that directly. The assert verifies this claim.)
            
            atomSerialNumber += 1
            if len(atomConnectList) > 1:
                connectLists.append(atomConnectList)
                # bruce 050318 comment: shouldn't we leave it out if 
                # len(atomConnectList) == 1?
                # I think so, so I'm doing that (unlike the previous code).

        # Write the chain TER-minator record
        #
        # COLUMNS     DATA TYPE         FIELD           DEFINITION
        # ------------------------------------------------------
        #  1 - 6      Record name       "TER     "
        #  7 - 11     Integer           serial          Serial number.
        # 18 - 20     Residue name      resName         Residue name.
        # 22          Character         chainID         Chain identifier.
        # 23 - 26     Integer           resSeq          Residue sequence number.
        # 27          AChar             iCode           Insertion code.
        f.write("TER   %5d          %1s\n" % (molnum, chr(chainIdChar)))

        molnum += 1
        chainIdChar += 1
        if chainIdChar > 126: # ASCII "~", end of PDB-acceptable chain chars
            chainIdChar = 32 # Rollover to ASCII " "
            
    for atomConnectList in connectLists:
        # Begin CONECT record ----------------------------------
        f.write("CONECT")
        for a in atomConnectList:
            index = atomsTable[a.key]
            f.write("%5d" % index)
        f.write("\n")
        # End CONECT record ----------------------------------
        connectLists = []
            
    f.write("END\n")
    
    f.close()
    
    if excluded:
        msg  = "Warning: excluded %d open bond(s) from saved PDB file; " \
             % excluded
        msg += "consider Hydrogenating and resaving." 
        msg  = fix_plurals(msg)
        env.history.message( orangemsg(msg))
    return # from writepdb