Beispiel #1
0
 def widget_msg(self, msg, options):
     #e improved timestamp?
     #e use html for color etc? [some callers put this directly in the msg, for now]
     _quote_html = options.pop(
         'quote_html', False
     )  #bruce 060126 new feature, improving on message_no_html interface ##k
     if _quote_html:
         msg = quote_html(msg)
     _color = options.pop('color', None)
     if _color:
         #bruce 060126 new feature; for now only permits 4 fixed color name strings;
         # should someday permit any color name (in any format) or object (of any kind) #e
         funcs = {
             'green': greenmsg,
             'orange': orangemsg,
             'red': redmsg,
             'gray': graymsg
         }
         func = funcs[
             _color]  # any colorname not in this dict is an exception (ok for now)
         msg = func(msg)
     _compact_stack = options.pop('compact_stack', "")  #bruce 060720
     if _compact_stack:
         msg += graymsg("; history.message() call stack: %s" %
                        quote_html(_compact_stack))
     # any unrecognized options are warned about below
     self._print_msg(msg)
     if options:
         msg2 = "fyi: bug: widget_msg got unsupported options: %r" % options
         print msg2  # too important to only print in the history file --
         # could indicate that not enough info is being saved there
         self._print_msg(msg2)
     return
Beispiel #2
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
Beispiel #3
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
Beispiel #4
0
def mark_atom_by_name(assy, name):
    """
    If you can find an atom of the given name, mark it visibly.
    """
    atom = find_atom_by_name(assy, name)
    if atom:
        env.history.message(quote_html("found atom %r: %r, in part %r" % (name, atom, atom.molecule.part)))
        mark_one_atom(atom)
    else:
        env.history.message(quote_html("can't find atom %r (in part %r)" % (name, assy.part,)))
    return
def mark_atom_by_name(assy, name):
    """
    If you can find an atom of the given name, mark it visibly.
    """
    atom = find_atom_by_name(assy, name)
    if atom:
        env.history.message(quote_html("found atom %r: %r, in part %r" % (name, atom, atom.molecule.part)))
        mark_one_atom(atom)
    else:
        env.history.message(quote_html("can't find atom %r (in part %r)" % (name, assy.part,)))
    return
Beispiel #6
0
 def blaberr():
     text = str(simProcess.readStderr(
     ))  # str since it's QString (i hope it can't be unicode)
     print "stderr:", text
     env.history.message(
         redmsg("%s stderr: " % self.plugin_name +
                quote_html(text)))
Beispiel #7
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
Beispiel #8
0
 def accept(self):
     'Slot for the OK button'
     try:
         self.create_comment()
         self.done_history_msg()
         self.comment = None
     except Exception, e:
         print_compact_traceback("Bug: exception in CommentProp.accept: ") #bruce Qt4 070502
         env.history.message(cmd + redmsg("Bug: " + quote_html(" - ".join(map(str, e.args)))))
             #bruce Qt4 070502 bugfixes: use quote_html, say it's a bug (could say "internal error" if desired)
         self.remove_comment()
Beispiel #9
0
def mark_selected_atoms_command(glpane): # untested
    """
    current part only...
    """
    assy = glpane.win.assy
    atoms = assy.selatoms.values()
    mark_atoms(atoms)
    msg = "marked %d selected atom(s)" % len(atoms) #e could use part of this string in jig name too
    msg = fix_plurals(msg)
    env.history.message(quote_html(msg))
    return
def mark_selected_atoms_command(glpane): # untested
    """
    current part only...
    """
    assy = glpane.win.assy
    atoms = assy.selatoms.values()
    mark_atoms(atoms)
    msg = "marked %d selected atom(s)" % len(atoms) #e could use part of this string in jig name too
    msg = fix_plurals(msg)
    env.history.message(quote_html(msg))    
    return
Beispiel #11
0
 def accept(self):
     """
     Slot for the OK button
     """
     try:
         self._create_comment()
         self._done_history_msg()
         self.comment = None
     except Exception, e:
         print_compact_traceback("Bug: exception in CommentProp.accept: ") #bruce Qt4 070502
         env.history.message(cmd + redmsg("Bug: " + quote_html(" - ".join(map(str, e.args)))))
             #bruce Qt4 070502 bugfixes: use quote_html, say it's a bug (could say "internal error" if desired)
         self._remove_comment()
    def _getToolTipInfo(self):  # VirtualSiteJig
        # untested, since some ###BUG prevents this from being shown
        """
        Return a string for display in self's Dynamic Tool tip.

        (Appears when user highlights this jig's drawing,
         but unrelated to tooltip on our site_atom itself.)
        
        [overridden from class Jig]
        """
        self._update_props()  # doesn't yet matter in this method
        msg = "%s: %s" % (self.sym, quote_html(self.name)) + \
              "<br><font color=\"#0000FF\">" \
              "%s</font>" % (self._props,)
        return msg
Beispiel #13
0
 def widget_msg(self, msg, options):
     #e improved timestamp?
     #e use html for color etc? [some callers put this directly in the msg, for now]
     _quote_html = options.pop('quote_html', False) #bruce 060126 new feature, improving on message_no_html interface ##k
     if _quote_html:
         msg = quote_html(msg)
     _color = options.pop('color', None)
     if _color:
         #bruce 060126 new feature; for now only permits 4 fixed color name strings;
         # should someday permit any color name (in any format) or object (of any kind) #e
         funcs = {'green':greenmsg, 'orange':orangemsg, 'red':redmsg, 'gray':graymsg}
         func = funcs[_color] # any colorname not in this dict is an exception (ok for now)
         msg = func(msg)
     _compact_stack = options.pop('compact_stack', "") #bruce 060720
     if _compact_stack:
         msg += graymsg("; history.message() call stack: %s" % quote_html(_compact_stack))
     # any unrecognized options are warned about below
     self._print_msg(msg)
     if options:
         msg2 = "fyi: bug: widget_msg got unsupported options: %r" % options
         print msg2 # too important to only print in the history file --
             # could indicate that not enough info is being saved there
         self._print_msg(msg2)
     return
    def _getToolTipInfo(self): # VirtualSiteJig
        # untested, since some ###BUG prevents this from being shown
        """
        Return a string for display in self's Dynamic Tool tip.

        (Appears when user highlights this jig's drawing,
         but unrelated to tooltip on our site_atom itself.)

        [overridden from class Jig]
        """
        self._update_props() # doesn't yet matter in this method
        msg = "%s: %s" % (self.sym, quote_html(self.name)) + \
              "<br><font color=\"#0000FF\">" \
              "%s</font>" % (self._props,)
        return msg
Beispiel #15
0
def select_atoms_with_errors_command(glpane):
    """
    current part only...
    """
    count = 0
    assy = glpane.win.assy
    for mol in assy.molecules: # current part only
        for atom in mol.atoms.itervalues():
            if atom._dna_updater__error:
                count += 1 # whether or not already selected
                atom.pick() # should be safe inside itervalues
                    ### REVIEW: selection filter effect not considered
    msg = "found %d pseudoatom(s) with dna updater errors in %r" % (count, assy.part)
    msg = fix_plurals(msg)
    env.history.message(quote_html(msg))
    return
def select_atoms_with_errors_command(glpane):
    """
    current part only...
    """
    count = 0
    assy = glpane.win.assy
    for mol in assy.molecules: # current part only
        for atom in mol.atoms.itervalues():
            if atom._dna_updater__error:
                count += 1 # whether or not already selected
                atom.pick() # should be safe inside itervalues
                    ### REVIEW: selection filter effect not considered
    msg = "found %d pseudoatom(s) with dna updater errors in %r" % (count, assy.part)
    msg = fix_plurals(msg)
    env.history.message(quote_html(msg))
    return
Beispiel #17
0
 def fatal(self, errortext, errorcode = 1):
     """
     Our submethods call this to report a fatal setup/use error; it prints errortext appropriately
     and sets self.errorcode and self.errortext.
     """
     if not errorcode:
         print "bug: fatal errorcode must be a boolean-true value, not %r" % (errorcode,)
         errorcode = 1
     if self.errorcode:
         print "plugin %r bug: self.errorcode was already set before fatal was called" % (self.plugin_name,)
     if not self.errorcode or not self.errortext:
         self.errortext = errortext # permanent record for use by callers
     self.errorcode = errorcode
     msg = "plugin %r fatal error: %s" % (self.plugin_name, errortext,)
     print msg
     env.history.message(redmsg(quote_html(msg))) # it might be too early for this to be seen
     return errorcode
Beispiel #18
0
def debug_run_command(command,
                      source="user debug input"
                      ):  #bruce 040913-16 in GLPane.py; modified 040928
    """
    Execute a python command, supplied by the user via some sort of debugging interface (named by source),
    in debug.py's globals. Return 1 for ok (incl empty command), 0 for any error.

    Caller should not print exception diagnostics -- this function does that
    (and does not reraise the exception).
    """
    #e someday we might record time, history, etc
    command = "" + command  # i.e. assert it's a string
    #k what's a better way to do the following?
    while command and command[0] == '\n':
        command = command[1:]
    while command and command[-1] == '\n':
        command = command[:-1]
    if not command:
        print "empty command (from %s), nothing executed" % (source, )
        return 1
    if '\n' not in command:
        msg = "will execute (from %s): %s" % (source, command)
    else:
        nlines = command.count('\n') + 1
        msg = "will execute (from %s; %d lines):\n%s" % (source, nlines,
                                                         command)
    print msg
    try:
        # include in history file, so one can search old history files for useful things to execute [bruce 060409]
        from utilities.Log import _graymsg, quote_html
        env.history.message(_graymsg(quote_html(msg)))
    except:
        print_compact_traceback("exception in printing that to history: ")
    command = command + '\n'  #k probably not needed
    try:
        ## exec command in globals()
        legally_exec_command_in_globals(command, globals())
    except:
        print_compact_traceback("exception from that: ")
        return 0
    else:
        print "did it!"
        return 1
    pass
 def _getToolTipInfo(self):  # VirtualBondJig
     """
     Return a string for display in self's Dynamic Tool tip.
     
     [overridden from class Jig]
     """
     self._update_props()
     ks = self._ks  # N/m
     r0 = self._r0  # pm
     length = self._getLength()  # pm
     force = ks * (length - r0)  # pN
     msg = "%s: %s" % (self.sym, quote_html(self.name)) + \
           "<br><font color=\"#0000FF\">" \
           "ks = %f N/m<br>" \
           "r0 = %f pm<br>" \
           "len = %f pm<br>" \
           "len/r0 = %f<br>" \
           "force = %f pN</font>" % (ks, r0, length, length/r0, force)
     return msg
    def _getToolTipInfo(self): # VirtualBondJig
        """
        Return a string for display in self's Dynamic Tool tip.

        [overridden from class Jig]
        """
        self._update_props()
        ks = self._ks # N/m
        r0 = self._r0 # pm
        length = self._getLength() # pm
        force = ks * (length - r0) # pN
        msg = "%s: %s" % (self.sym, quote_html(self.name)) + \
              "<br><font color=\"#0000FF\">" \
              "ks = %f N/m<br>" \
              "r0 = %f pm<br>" \
              "len = %f pm<br>" \
              "len/r0 = %f<br>" \
              "force = %f pN</font>" % (ks, r0, length, length/r0, force)
        return msg
Beispiel #21
0
def debug_run_command(command, source = "user debug input"): #bruce 040913-16 in GLPane.py; modified 040928
    """
    Execute a python command, supplied by the user via some sort of debugging interface (named by source),
    in debug.py's globals. Return 1 for ok (incl empty command), 0 for any error.

    Caller should not print exception diagnostics -- this function does that
    (and does not reraise the exception).
    """
    #e someday we might record time, history, etc
    command = "" + command # i.e. assert it's a string
    #k what's a better way to do the following?
    while command and command[0] == '\n':
        command = command[1:]
    while command and command[-1] == '\n':
        command = command[:-1]
    if not command:
        print "empty command (from %s), nothing executed" % (source,)
        return 1
    if '\n' not in command:
        msg = "will execute (from %s): %s" % (source, command)
    else:
        nlines = command.count('\n')+1
        msg = "will execute (from %s; %d lines):\n%s" % (source, nlines, command)
    print msg
    try:
        # include in history file, so one can search old history files for useful things to execute [bruce 060409]
        from utilities.Log import _graymsg, quote_html
        env.history.message( _graymsg( quote_html( msg)))
    except:
        print_compact_traceback("exception in printing that to history: ")
    command = command + '\n' #k probably not needed
    try:
        ## exec command in globals()
        legally_exec_command_in_globals( command, globals() )
    except:
        print_compact_traceback("exception from that: ")
        return 0
    else:
        print "did it!"
        return 1
    pass
Beispiel #22
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
Beispiel #23
0
    def _depositLibraryPart(self, newPart, hotspotAtom, atom_or_pos): 
        # probably by Huaicai; revised by bruce 051227, 060627, 070501
        """
        This method serves as an overloaded method, <atom_or_pos> is 
        the Singlet atom or the empty position that the new part <newPart>
        [which is an assy, at least sometimes] will be attached to or placed at.
        [If <atom_or_pos> is a singlet, <hotspotAtom> should be an atom in some 
        chunk in <newPart>.]
        Currently, it doesn't consider group or jigs in the <newPart>. 
        Not so sure if my attempt to copy a part into another assembly is all
        right. [It wasn't, so bruce 051227 revised it.]
        Copies all molecules in the <newPart>, change their assy attribute to 
        current assembly, move them into <pos>.
        [bruce 051227 new feature:] return a list of new nodes created, and a 
        message for history (currently almost a stub).
        [not sure if subrs ever print history messages...
        if they do we'd want to return those instead.]
        """

        attach2Bond = False
        stuff = [] # list of deposited nodes [bruce 051227 new feature]

        if isinstance(atom_or_pos, Atom):
            attch2Singlet = atom_or_pos
            if hotspotAtom and hotspotAtom.is_singlet() and \
               attch2Singlet .is_singlet():

                newMol = hotspotAtom.molecule.copy_single_chunk(None)
                    # [this can break interchunk bonds,
                    #  thus it still has bug 2028]
                newMol.setAssy(self.o.assy)
                hs = newMol.hotspot
                ha = hs.singlet_neighbor() # hotspot neighbor atom
                attch2Atom = attch2Singlet.singlet_neighbor() # attach to atom

                rotCenter = newMol.center
                rotOffset = Q(ha.posn()-hs.posn(), 
                              attch2Singlet.posn()-attch2Atom.posn())
                newMol.rot(rotOffset)

                moveOffset = attch2Singlet.posn() - hs.posn()
                newMol.move(moveOffset)
    
                self.graphicsMode._createBond(hs, ha, attch2Singlet, attch2Atom)

                self.o.assy.addmol(newMol)
                stuff.append(newMol)

                #e if there are other chunks in <newPart>, 
                #they are apparently copied below. [bruce 060627 comment]

            else: ## something is wrong, do nothing
                return stuff, "internal error"
            attach2Bond = True
        else:
            placedPos = atom_or_pos
            if hotspotAtom:
                hotspotAtomPos = hotspotAtom.posn()
                moveOffset = placedPos - hotspotAtomPos
            else:
                if newPart.molecules:
                    moveOffset = placedPos - newPart.molecules[0].center #e not 
                    #the best choice of center [bruce 060627 comment]

        if attach2Bond: # Connect part to a bondpoint of an existing chunk
            for m in newPart.molecules:
                if not m is hotspotAtom.molecule: 
                    newMol = m.copy_single_chunk(None)
                        # [this can break interchunk bonds,
                        #  thus it still has bug 2028]
                    newMol.setAssy(self.o.assy)

                    ## Get each of all other chunks' center movement for the 
                    ## rotation around 'rotCenter'
                    coff = rotOffset.rot(newMol.center - rotCenter)
                    coff = rotCenter - newMol.center + coff 

                    # The order of the following 2 statements doesn't matter
                    newMol.rot(rotOffset)
                    newMol.move(moveOffset + coff)

                    self.o.assy.addmol(newMol)
                    stuff.append(newMol)
        else: # Behaves like dropping a part anywhere you specify, independent 
            #of existing chunks.
            # copy all nodes in newPart (except those in clipboard items), 
            # regardless of node classes;
            # put it in a new Group if more than one thing [bruce 070501]
            # [TODO: this should be done in the cases above, too, but that's 
            # not yet implemented,
            #  and requires adding rot or pivot to the Node API and revising
            #  the rot-calling code above,
            #  and also reviewing the definition of the "hotspot of a Part" and 
            # maybe of a "depositable clipboard item".]
            assert newPart.tree.is_group()
            nodes = list(newPart.tree.members) # might be []
            assy = self.o.assy
            newnodes = copied_nodes_for_DND(nodes, 
                                            autogroup_at_top = True, 
                                            assy = assy)
                # Note: that calls name_autogrouped_nodes_for_clipboard 
                # internally, if it forms a Group,
                # but we ignore that and rename the new node differently below,
                # whether or not it was autogrouped. We could just as well do 
                # the autogrouping ourselves...
                # Note [bruce 070525]: it's better to call copied_nodes_for_DND 
                # here than copy_nodes_in_order, even if we didn't need to 
                # autogroup. One reason is that if some node is not copied,
                # that's not necessarily an error, since we don't care about 1-1
                # orig-copy correspondence here.
            if not newnodes:
                if newnodes is None:
                    print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
                        # TODO: This might be possible, for arbitrary partlib 
                        # contents, just not for legitimate ones...
                        # but partlib will probably be (or is) user-expandable, 
                        #so we should turn this into history message,
                        # not a bug print. But I'm not positive it's possible
                        #w/o a bug, so review first. ###FIX [bruce 070501 comment]
                    import utilities.debug as debug
                    debug._bugnodes = nodes
                    newnodes = []
                msg = redmsg( "error: nothing to deposit in [%s]" % quote_html(str(newPart.name)) )
                return [], msg
            assert len(newnodes) == 1 # due to autogroup_at_top = True
            # but the remaining code works fine regardless of len(newnodes), 
            #in case we make autogroup a preference
            for newnode in newnodes:
                # Rename newnode based on the partlib name and a unique number.
                # It seems best to let the partlib mmp file contents (not just 
                # filename)
                # control the name used here, so use newPart.tree.name rather 
                # than just newPart.name.
                # (newPart.name is a complete file pathname; newPart.tree.name 
                #is usually its basename w/o extension.)
                basename = str(newPart.tree.name)
                if basename == 'Untitled':
                    # kluge, for the sake of 3 current partlib files, and files 
                    #saved only once by users (due to NE1 bug in save)
                    dirjunk, base = os.path.split(newPart.name)
                    basename, extjunk = os.path.splitext(base)
                from utilities.constants import gensym
                newnode.name = gensym( basename, assy) # name library part
                    #bruce 080407 basename + " " --> basename, and pass assy
                    # (per Mark NFR desire)
                #based on basename recorded in its mmp file's top node
                newnode.move(moveOffset) #k not sure this method is correctly 
                #implemented for measurement jigs, named views
                assy.addnode(newnode)
                stuff.append(newnode)

##            #bruce 060627 new code: fix bug 2028 (non-hotspot case only) 
##            about interchunk bonds being broken
##            nodes = newPart.molecules
##            newnodes = copied_nodes_for_DND(nodes)
##            if newnodes is None:
##                print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
##                debug._bugnodes = nodes
##                newnodes = [] # kluge
##            for newMol in newnodes:
##                # some of the following probably only work for Chunks,
##                # though coding them for other nodes would not be hard
##                newMol.setAssy(self.o.assy)
##                newMol.move(moveOffset)
##                self.o.assy.addmol(newMol)
##                stuff.append(newMol)

##            # pre-060627 old code, breaks interchunk bonds since it copies 
##            #chunks one at a time (bug 2028)
##            for m in nodes:
##                newMol = m.copy(None) # later: renamed Chunk method to copy_single_chunk -- not sure if this was only called on chunks
##                newMol.setAssy(self.o.assy) #bruce 051227 revised this
##                
##                newMol.move(moveOffset)
##                
##                self.o.assy.addmol(newMol)
##                stuff.append(newMol)
##            pass
        self.o.assy.update_parts() #bruce 051227 see if this fixes the 
                                    #atom_debug exception in checkparts

        msg = greenmsg("Deposited library part: ") + " [" + \
            quote_html(str(newPart.name)) + "]"  #ninad060924 fix bug 1164 

        return stuff, msg  ####@@@@ should revise this message 
 def blaberr():
     text = str(simProcess.readStderr()) # str since it's QString (i hope it can't be unicode)
     print "stderr:", text
     env.history.message(redmsg("%s stderr: " % self.plugin_name + quote_html(text)))
Beispiel #25
0
def make_or_remove_crossover(twoPls, make=True, cmdname=None):
    """
    Make or Remove (according to make option) a crossover, given Pl5_recognizers for its two Pl atoms.
    """

    # What we are doing is recognizing one local structure and replacing it with another
    # made from the same atoms. It'd sure be easier if I could do the manipulation in an mmp file,
    # save that somewhere, and read those to generate the operation! I'd have two sets of atoms, before and after,
    # and see how bonds and atomtypes got changed.

    # In this case it's not too hard to hand-code... I guess only the Pl atoms and their bonds are affected.
    # We probably do have to look at strand directions -- hmm, probably we should require them to exist before saying it's ok!
    # Or maybe better to give history error message when the command is chosen, saying you need to set them (or repair them) first...
    # Then we have to move the new/moved Pl atoms into a good position...

    # Note: Pl.ordered_bases are ordered by bond direction, to make this easier...
    # but if we want to patch up the directions in the end, do we need to care exactly which ones were defined?
    # or only "per-Pl"? hmm... it's per-Pl for now

    assert cmdname

    for pl in twoPls:
        if pl.ordered_bases is None:  # should no longer be possible -- now checked before menu commands are offered [bruce 070604]
            ###BUG: this could have various causes, not only the one reported below! Somehow we need access to the
            # message supplied to the RecognizerError, for use here.
            ###REVIEW: Does that mean it should pass through compute methods (probably in a controlled way)
            # rather than making computed values None?
            # Or, should the value not be None, but a "frozen" examinable and reraisable version of the error exception??
            msg = "%s: Error: bond direction is locally undefined or inconsistent around %s" % (
                cmdname, pl.atom)  ###UNTESTED
            print "should no longer be possible:", msg  #bruce 070604
            env.history.message(redmsg(quote_html(msg)))
            return

    Pl1, Pl2 = twoPls
    a, b = Pl1.ordered_bases
    d, c = Pl2.ordered_bases  # note: we use d,c rather than c,d so that the atom arrangement is as shown in the diagram below.

    # Note: for either the Make or Remove operation, the geometric arrangement is initially:
    #
    # c <-- Pl2 <-- d
    #
    # a --> Pl1 --> b
    #
    # and it ends up being (where dots indicate arrowheads, to show bond direction):
    #
    # c        d
    #  .      /
    #   \    .
    #  Pl1  Pl2
    #   .    \
    #  /      .
    # a        b
    #
    # Note: Pl1 stays attached to a, and Pl2 to d. Which two opposite bonds to preserve like that
    # is an arbitrary choice -- as long as Make and Remove make the same choice about that,
    # they'll reverse each other's effects precisely (assuming the sugars were initially correct as Ss or Sj).

    # break the bonds we no longer want
    for obj1, obj2 in [(Pl1, b), (Pl2, c)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.bust(make_bondpoints=False)

    # make the bonds we want and didn't already have
    for obj1, obj2 in [(Pl1, c), (Pl2, b)]:
        assert not atoms_are_bonded(obj1.atom, obj2.atom)
        ###e we should make bond_atoms do this assert itself, or maybe tolerate it (or does it already??)
        bond_atoms_faster(obj1.atom, obj2.atom, V_SINGLE)

    # set directions of all 4 bonds (even the preserved ones -- it's possible they were not set before,
    #  if some but not all bonds had directions set in the part of a strand whose directions we look at.)
    for obj1, obj2 in [(a, Pl1), (Pl1, c), (d, Pl2), (Pl2, b)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.set_bond_direction_from(obj1.atom, 1)

    # WARNING: after that bond rearrangement, don't use our Pl5_recognizers in ways that depend on Pl bonding,
    # since it's not well defined whether they think about the old or new bonding to give their answers.
    Pl_atoms = Pl1.atom, Pl2.atom
    del Pl1, Pl2, twoPls

    # transmute base sugars to Sj or Ss as appropriate
    if dna_updater_is_enabled():
        want = Element_Ss5  #bruce 080320 bugfix
    else:
        want = make and Element_Sj5 or Element_Ss5
    for obj in (a, b, c, d):
        obj.atom.Transmute(want)
        # Note: we do this after the bond making/breaking so it doesn't add singlets which mess us up.

    # move Pl atoms into better positions
    # (someday, consider using local minimize; for now, just place them directly between their new neighbor atoms,
    #  hopefully we leave them selected so user can easily do their own local minimize.)
    for pl in Pl_atoms:
        pl.setposn(
            average_value(map(lambda neighbor: neighbor.posn(),
                              pl.neighbors())))

    env.history.message(
        greenmsg(cmdname + ": ") + quote_html("(%s - %s)" % tuple(Pl_atoms)))

    #e need assy.changed()? evidently not.

    return  # from make_or_remove_crossover
Beispiel #26
0
def apply_btype_to_bond(btype,
                        bond,
                        allow_remake_bondpoints = True,
                        suppress_history_message = False): #bruce 060703 added allow_remake_bondpoints for bug 833-1
    """
    Apply the given bond-type name (e.g. 'single') to the given bond, iff this is permitted by its atomtypes
    (or, new feature 060523, if it's permitted by its real atoms' possible atomtypes and their number of real bonds),
    and do whatever inferences are presently allowed [none are implemented as of 050727].
    Emit an appropriate history message. Do appropriate invals/updates.
    [#e should the inference policy and/or some controlling object be another argument? Maybe even a new first arg 'self'?]

    @param suppress_history_message: If True, it quietly converts the bondtypes
            without printing any history message.
    """
    # Note: this can be called either from a bond's context menu, or by using a Build mode dashboard tool to click on bonds
    # (or bondpoints as of 060702) and immediately change their types.

    #This flag will be returned by this function to tell the caller whether the
    #bond type of the given bond was changed
    bond_type_changed = True

    v6 = v6_from_btype(btype)
    oldname = quote_html( str(bond) )
    def changeit(also_atypes = None):
        if v6 == bond.v6:
            bond_type_changed = False
            if not suppress_history_message:
                env.history.message( "bond type of %s is already %s" % (oldname, btype))
        else:
            if also_atypes:
                # change atomtypes first (not sure if doing this first matters)
                atype1, atype2 = also_atypes
                def changeatomtype(atom, atype):
                    if atom.atomtype is not atype:
                        if not suppress_history_message:
                            msg = "changed %r from %s to %s" % (atom,
                                                                atom.atomtype.name,
                                                                atype.name )
                            env.history.message(msg)
                        atom.set_atomtype(atype)
                        ### note[ probably 060523]:
                        # if we're an open bond, we have to prevent this process from removing us!
                        # (this is nim, so we're not yet safe to offer on open bonds.
                        #  Thus in fix for 833-1 [060703], atomtype changes are not allowed.)
                        pass
                    return # from changeatomtype
                changeatomtype(bond.atom1, atype1)
                changeatomtype(bond.atom2, atype2)
            bond.set_v6(v6) # this doesn't affect anything else or do any checks ####k #####@@@@@ check that
            ##e now do inferences on other bonds
            bond.changed() ###k needed?? maybe it's now done by set_v6??
            if not suppress_history_message:
                env.history.message( "changed bond type of %s to %s" % (oldname,
                                                                        btype))
            ###k not sure if it does gl_update when needed... how does menu use of this do that?? ###@@@
        return # from changeit
    poss = poss1 = possible_bond_types(bond) # only includes the ones which don't change the atomtypes -- try these first
    if btype in poss1:
        changeit()
        return bond_type_changed
    # otherwise figure out if we can change the atomtypes to make this work.
    # (The following code is predicted to work for either real or open bonds,
    #  but it is not safe to offer on open bonds for other reasons (commented above in changeatomtype).
    #  But we'll still figure out the situation, so the history message can be more useful.)
    if 1:
        # this is needed for allow_remake_bondpoints,
        # or for history advice about what that could have permitted:
        poss2, permitted1, permitted2 = possible_bond_types_for_elements(bond)
            # the only purpose of having the whole sequence poss2
            # (not just one element of it, equal to btype) is the error message
        if btype in poss2:
            atype1 = best_atype(bond.atom1, permitted1[v6])
            atype2 = best_atype(bond.atom2, permitted2[v6])
    if allow_remake_bondpoints:
        poss = poss2 # poss is whichever of poss1 or poss2 was actually allowed
        if btype in poss2:
            changeit((atype1, atype2))
            return bond_type_changed
    # It failed, but a variety of situations should be handled in the error message.
    # For error messages, sort them all the same way.
    poss1.sort()
    poss2.sort()
    poss.sort() #k not really needed, it's same mutable list, but keep this in case someone changes that
    if poss2 == poss : # note, this happens if poss2 == poss1, or if they differ but allow_remake_bondpoints is true
        # permitting changing of atomtypes wouldn't make any difference
        if not suppress_history_message:
            msg = "can't change bond type of %s to %s" % (oldname, btype)
            msg2 = " -- permitted types are %s" % (poss)
                #e improve message -- %s of list looks like repr (for strings too)
            env.history.message( orangemsg( msg) + msg2 )
        bond_type_changed = False
    elif btype in poss2:
        if allow_remake_bondpoints:
            print_compact_stack( "bug: allow_remake_bondpoints should not be true here: " )
        # the only reason we refused is that the UI won't allow remaking of bondpoints;
        # explain what the user would have to do to make it work (using the things computed above as if it had been permitted)
        # (as of 060703 this happens only when you click a bond type changing tool on a bondpoint,
        #  but following code will try to cover this for a real bond as well)
        unless = ""
        for atom, atype in [(bond.atom1, atype1), (bond.atom2, atype2)]: ##e ideally, in same order as printed in bond name
            if atype != atom.atomtype:
                if atom.is_singlet():
                    # should never happen
                    if env.debug:
                        print "debug: bug: %r is bondpoint but user is advised to change its atomtype" % atom
                if not unless:
                    unless = "change atomtype of %s to %s" % (atom, atype.name)
                else:
                    # this is not expected to ever happen, when called from UI as of 060703; it's untested ##@@
                    unless += ", and of %s to %s" % (atom, atype.name)
        msg = "can't change bond type of %s to %s, " % (oldname, btype,)
        bond_type_changed = False
        if unless:
            unless_msg = greenmsg( "unless you %s" % (unless,) )
        else:
            unless_msg = redmsg( "due to a bug")
        if not suppress_history_message:
            env.history.message( orangemsg( msg) + ( unless_msg) )


    else:
        # changing atomtypes makes a difference, but either way you're not allowed to change to this bond type
        if allow_remake_bondpoints:
            print_compact_stack( "bug: allow_remake_bondpoints should not be true here: " )
        extra = complement_sequences(poss2, poss1)
        if not extra:
            print_compact_stack( "bug: extra should not be empty here: " )


        msg = "can't change bond type of %s to %s" % (oldname, btype)
        msg2 = " -- permitted types are %s, or %s if you change atomtypes" % (poss1, extra)
            #e improve message -- %s of list looks like repr (for strings too)
        bond_type_changed = False
        if not suppress_history_message:
            env.history.message( orangemsg( msg) + msg2 )

    return bond_type_changed # from apply_btype_to_bond
Beispiel #27
0
    def maybeTip(self, helpEvent):
        """
        Determines if this tooltip should be displayed. The tooltip will be displayed at
        helpEvent.globalPos() if an object is highlighted and the mouse hasn't moved for 
        some period of time, called the "wake up delay" period, which is a user pref
        (not yet implemented in the Preferences dialog) currently set to 1 second.
        
        maybeTip() is called by GLPane.timerEvent() whenever the cursor is not moving to 
        determine if the tooltip should be displayed.

        @param helpEvent: a QHelpEvent constructed by the caller
        @type helpEvent: QHelpEvent
        """
        # docstring used to also say:
        ## For more details about this member, see Qt documentation on QToolTip.maybeTip().
        # but this is unclear to me (since this class does not inherit from
        # QToolTip), so I removed it. [bruce 081208]

        debug = debug_pref("GLPane: graphics debug tooltip?",
                           Choice_boolean_False,
                           prefs_key = True )

        glpane = self.glpane
        selobj = glpane.selobj

        if debug:
            # russ 080715: Graphics debug tooltip.
            # bruce 081208/081211: revised, moved out of _getToolTipText,
            # made debug_pref.
            # note: we don't use glpane.MousePos since it's not reliable --
            # only some graphicsModes store it, and only in mouse press events.

            # Note: double buffering applies only to the color buffer,
            # not the stencil or depth buffers, which have only one copy.
            # The setting of GL_READ_BUFFER should have no effect on
            # glReadPixelsf from those buffers.
            # [bruce 081211 comment, based on Russ report of OpenGL doc]
            
            pos = helpEvent.pos()
            wX = pos.x()
            wY = glpane.height - pos.y() #review: off by 1??
            wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT)[0][0]
            stencil = glReadPixelsi(wX, wY, 1, 1, GL_STENCIL_INDEX)[0][0]
            savebuff = glGetInteger(GL_READ_BUFFER)
            whichbuff = {GL_FRONT:"front", GL_BACK:"back"}.get(savebuff, "unknown")
            redraw_counter = env.redraw_counter
            # Pixel data is sign-wrapped, in spite of specifying unsigned_byte.
            def us(b):
                if b < 0:
                    return 256 + b
                else:
                    return b
            def pixvals(buff):
                glReadBuffer(buff)
                gl_format, gl_type = GL_RGBA, GL_UNSIGNED_BYTE
                rgba = glReadPixels( wX, wY, 1, 1, gl_format, gl_type )[0][0]
                return (
                    "rgba %u, %u, %u, %u" %
                    (us(rgba[0]), us(rgba[1]), us(rgba[2]), us(rgba[3]))
                 )
            def redifnot(v1, v2, text):
                if v1 != v2:
                    return redmsg(text)
                else:
                    return text
            front_pixvals = pixvals(GL_FRONT)
            back_pixvals = pixvals(GL_BACK)
            glReadBuffer(savebuff)      # restore the saved value
            tipText = (
                "env.redraw = %d; selobj = %s<br>" % (redraw_counter, quote_html(str(selobj)),) +
                    # note: sometimes selobj is an instance of _UNKNOWN_SELOBJ_class... relates to testmode bug from renderText
                    # (confirmed that renderText zaps stencil and that that alone causes no bug in other graphicsmodes)
                    # TODO: I suspect this can be printed even during rendering... need to print glpane variables
                    # which indicate whether we're doing rendering now, e.g. current_glselect, drawing_phase;
                    # also modkeys (sp?), glselect_wanted
                "mouse position (xy): %d, %d<br>" % (wX, wY,) +
                "depth %f, stencil %d<br>" % (wZ, stencil) +
                redifnot(whichbuff, "back",
                         "current read buffer: %s<br>" % whichbuff ) +
                redifnot(glpane.glselect_wanted, 0,
                         "glselect_wanted: %s<br>" % (glpane.glselect_wanted,) ) + 
                redifnot(glpane.current_glselect, False,
                         "current_glselect: %s<br>" % (glpane.current_glselect,) ) + 
                redifnot(glpane.drawing_phase, "?",
                         "drawing_phase: %s<br>" % (glpane.drawing_phase,) ) +
                "front: " + front_pixvals + "<br>" +
                redifnot(back_pixvals, front_pixvals,
                         "back:  " + back_pixvals )
             )
            global _last_tipText
            if tipText != _last_tipText:
                print
                print tipText
                _last_tipText = tipText
            pass # use tipText below

        else:
            
            # <motionlessCursorDuration> is the amount of time the cursor (mouse) has been motionless.
            motionlessCursorDuration = time.time() - glpane.cursorMotionlessStartTime
            
            # Don't display the tooltip yet if <motionlessCursorDuration> hasn't exceeded the "wake up delay".
            # The wake up delay is currently set to 1 second in prefs_constants.py. Mark 060818.
            if motionlessCursorDuration < env.prefs[dynamicToolTipWakeUpDelay_prefs_key]:
                self.toolTipShown = False
                return
            
            # If an object is not currently highlighted, don't display a tooltip.
            if not selobj:
                return
            
            # If the highlighted object is a singlet, 
            # don't display a tooltip for it.
            if isinstance(selobj, Atom) and (selobj.element is Singlet):
                return
                
            if self.toolTipShown:
                # The tooltip is already displayed, so return. 
                # Do not allow tip() to be called again or it will "flash".
                return
        
            tipText = self._getToolTipText()

            pass

        # show the tipText
        
        if not tipText:            
            tipText = "" 
            # This makes sure that dynamic tip is not displayed when
            # the highlightable object is 'unknown' to the dynamic tip class.
            # (From QToolTip.showText doc: "If text is empty the tool tip is hidden.")

        showpos = helpEvent.globalPos()

        if debug:
            # show it a little lower to avoid the cursor obscuring the tooltip.
            # (might be useful even when not debugging, depending on the cursor)
            # [bruce 081208]
            showpos = showpos + QPoint(0, 10)
        
        QToolTip.showText(showpos, tipText)  #@@@ ninad061107 works fine but need code review
               
        self.toolTipShown = True
    def _depositLibraryPart(self, newPart, hotspotAtom, atom_or_pos):
        # probably by Huaicai; revised by bruce 051227, 060627, 070501
        """
        This method serves as an overloaded method, <atom_or_pos> is
        the Singlet atom or the empty position that the new part <newPart>
        [which is an assy, at least sometimes] will be attached to or placed at.
        [If <atom_or_pos> is a singlet, <hotspotAtom> should be an atom in some
        chunk in <newPart>.]
        Currently, it doesn't consider group or jigs in the <newPart>.
        Not so sure if my attempt to copy a part into another assembly is all
        right. [It wasn't, so bruce 051227 revised it.]
        Copies all molecules in the <newPart>, change their assy attribute to
        current assembly, move them into <pos>.
        [bruce 051227 new feature:] return a list of new nodes created, and a
        message for history (currently almost a stub).
        [not sure if subrs ever print history messages...
        if they do we'd want to return those instead.]
        """

        attach2Bond = False
        stuff = []  # list of deposited nodes [bruce 051227 new feature]

        if isinstance(atom_or_pos, Atom):
            attch2Singlet = atom_or_pos
            if hotspotAtom and hotspotAtom.is_singlet() and attch2Singlet.is_singlet():

                newMol = hotspotAtom.molecule.copy_single_chunk(None)
                # [this can break interchunk bonds,
                #  thus it still has bug 2028]
                newMol.set_assy(self.o.assy)
                hs = newMol.hotspot
                ha = hs.singlet_neighbor()  # hotspot neighbor atom
                attch2Atom = attch2Singlet.singlet_neighbor()  # attach to atom

                rotCenter = newMol.center
                rotOffset = Q(ha.posn() - hs.posn(), attch2Singlet.posn() - attch2Atom.posn())
                newMol.rot(rotOffset)

                moveOffset = attch2Singlet.posn() - hs.posn()
                newMol.move(moveOffset)

                self.graphicsMode._createBond(hs, ha, attch2Singlet, attch2Atom)

                self.o.assy.addmol(newMol)
                stuff.append(newMol)

                # e if there are other chunks in <newPart>,
                # they are apparently copied below. [bruce 060627 comment]

            else:  ## something is wrong, do nothing
                return stuff, "internal error"
            attach2Bond = True
        else:
            placedPos = atom_or_pos
            if hotspotAtom:
                hotspotAtomPos = hotspotAtom.posn()
                moveOffset = placedPos - hotspotAtomPos
            else:
                if newPart.molecules:
                    moveOffset = placedPos - newPart.molecules[0].center  # e not
                    # the best choice of center [bruce 060627 comment]

        if attach2Bond:  # Connect part to a bondpoint of an existing chunk
            for m in newPart.molecules:
                if not m is hotspotAtom.molecule:
                    newMol = m.copy_single_chunk(None)
                    # [this can break interchunk bonds,
                    #  thus it still has bug 2028]
                    newMol.set_assy(self.o.assy)

                    ## Get each of all other chunks' center movement for the
                    ## rotation around 'rotCenter'
                    coff = rotOffset.rot(newMol.center - rotCenter)
                    coff = rotCenter - newMol.center + coff

                    # The order of the following 2 statements doesn't matter
                    newMol.rot(rotOffset)
                    newMol.move(moveOffset + coff)

                    self.o.assy.addmol(newMol)
                    stuff.append(newMol)
        else:  # Behaves like dropping a part anywhere you specify, independent
            # of existing chunks.
            # copy all nodes in newPart (except those in clipboard items),
            # regardless of node classes;
            # put it in a new Group if more than one thing [bruce 070501]
            # [TODO: this should be done in the cases above, too, but that's
            # not yet implemented,
            #  and requires adding rot or pivot to the Node API and revising
            #  the rot-calling code above,
            #  and also reviewing the definition of the "hotspot of a Part" and
            # maybe of a "depositable clipboard item".]
            assert newPart.tree.is_group()
            nodes = list(newPart.tree.members)  # might be []
            assy = self.o.assy
            newnodes = copied_nodes_for_DND(nodes, autogroup_at_top=True, assy=assy)
            # Note: that calls name_autogrouped_nodes_for_clipboard
            # internally, if it forms a Group,
            # but we ignore that and rename the new node differently below,
            # whether or not it was autogrouped. We could just as well do
            # the autogrouping ourselves...
            # Note [bruce 070525]: it's better to call copied_nodes_for_DND
            # here than copy_nodes_in_order, even if we didn't need to
            # autogroup. One reason is that if some node is not copied,
            # that's not necessarily an error, since we don't care about 1-1
            # orig-copy correspondence here.
            if not newnodes:
                if newnodes is None:
                    print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
                    # TODO: This might be possible, for arbitrary partlib
                    # contents, just not for legitimate ones...
                    # but partlib will probably be (or is) user-expandable,
                    # so we should turn this into history message,
                    # not a bug print. But I'm not positive it's possible
                    # w/o a bug, so review first. ###FIX [bruce 070501 comment]
                    import utilities.debug as debug

                    debug._bugnodes = nodes
                    newnodes = []
                msg = redmsg("error: nothing to deposit in [%s]" % quote_html(str(newPart.name)))
                return [], msg
            assert len(newnodes) == 1  # due to autogroup_at_top = True
            # but the remaining code works fine regardless of len(newnodes),
            # in case we make autogroup a preference
            for newnode in newnodes:
                # Rename newnode based on the partlib name and a unique number.
                # It seems best to let the partlib mmp file contents (not just
                # filename)
                # control the name used here, so use newPart.tree.name rather
                # than just newPart.name.
                # (newPart.name is a complete file pathname; newPart.tree.name
                # is usually its basename w/o extension.)
                basename = str(newPart.tree.name)
                if basename == "Untitled":
                    # kluge, for the sake of 3 current partlib files, and files
                    # saved only once by users (due to NE1 bug in save)
                    dirjunk, base = os.path.split(newPart.name)
                    basename, extjunk = os.path.splitext(base)
                from utilities.constants import gensym

                newnode.name = gensym(basename, assy)  # name library part
                # bruce 080407 basename + " " --> basename, and pass assy
                # (per Mark NFR desire)
                # based on basename recorded in its mmp file's top node
                newnode.move(moveOffset)  # k not sure this method is correctly
                # implemented for measurement jigs, named views
                assy.addnode(newnode)
                stuff.append(newnode)

        ##            #bruce 060627 new code: fix bug 2028 (non-hotspot case only)
        ##            # about interchunk bonds being broken
        ##            nodes = newPart.molecules
        ##            newnodes = copied_nodes_for_DND(nodes)
        ##            if newnodes is None:
        ##                print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
        ##                debug._bugnodes = nodes
        ##                newnodes = [] # kluge
        ##            for newMol in newnodes:
        ##                # some of the following probably only work for Chunks,
        ##                # though coding them for other nodes would not be hard
        ##                newMol.set_assy(self.o.assy)
        ##                newMol.move(moveOffset)
        ##                self.o.assy.addmol(newMol)
        ##                stuff.append(newMol)

        self.o.assy.update_parts()  # bruce 051227 see if this fixes the
        # atom_debug exception in checkparts

        msg = (
            greenmsg("Deposited library part: ") + " [" + quote_html(str(newPart.name)) + "]"
        )  # ninad060924 fix bug 1164

        return stuff, msg  ####@@@@ should revise this message
Beispiel #29
0
 def _done_history_msg(self):
     env.history.message(cmd + quote_html("%s %s." % (self.comment.name, self.action)))
Beispiel #30
0
    def getToolTipInfoForBond(self, bond):
        """
        Tooltip information when the cursor is over a strand bond.
        As of 2008-11-09, it gives the information in the following form:

        """

        #Bond direction will always be atm1 --> atm2
        #@see: Bond.bond_direction_from()
        atm1 = bond.atom1
        atm2 = bond.atom2

        strandInfo = ""


        if not (atm1 and atm2):
            strandInfo = self.getDefaultToolTipInfo()
            return strandInfo


        threePrimeEndAtom = self.get_three_prime_end_base_atom()
        fivePrimeEndAtom  = self.get_five_prime_end_base_atom()
        allAtoms = self.get_strand_atoms_in_bond_direction(filterBondPoints = True)

        tooltipDirection = "3<--5"
        left_atom = bond_left_atom(bond, quat = self.assy.glpane.quat)
        right_atom = bond.other(left_atom)

        if bond.bond_direction_from(left_atom) == 1:
            tooltipDirection = "5-->3"
        else:
            tooltipDirection = "3<--5"

        left_atm_index = None

        try:
            left_atm_index = allAtoms.index(left_atom)
        except:
            print_compact_traceback("bug in getting strand info string "\
                                    "atom %s not in list"%left_atom)

        if left_atm_index:
            #@BUG: The computation of numOfBases_next_crossover_5prime and
            #numOfBases_next_crossover_3prime is wrong in some cases. So,
            #that information is not displayed.
            numOfBases_next_crossover_5prime, numOfBases_next_crossover_3prime = \
                                            self._number_of_atoms_before_next_crossover(
                                                left_atom,
                                                tooltipDirection = tooltipDirection)

            if threePrimeEndAtom and fivePrimeEndAtom:
                if tooltipDirection == "3<--5":
                    numOfBasesDown_3PrimeDirection = len(allAtoms[left_atm_index:])
                    #Note: This does not include atm1 , which is intentional--
                    numOfBasesDown_5PrimeDirection = len(allAtoms[:left_atm_index])
                    ##strandInfo += " 3' < " + str(numOfBasesDown_3PrimeDirection) + "/" + str(numOfBases_next_crossover_3prime)
                    strandInfo += " 3' < " + str(numOfBasesDown_3PrimeDirection)
                    strandInfo += " --(%s)-- "%(len(allAtoms))
                    ##strandInfo += str(numOfBases_next_crossover_5prime) + "/" + str(numOfBasesDown_5PrimeDirection) + " < 5'"
                    strandInfo += str(numOfBasesDown_5PrimeDirection) + " < 5'"
                else:
                    numOfBasesDown_3PrimeDirection = len(allAtoms[left_atm_index + 1:])
                    #Note: This does not include atm1 , which is intentional--
                    numOfBasesDown_5PrimeDirection = len(allAtoms[:left_atm_index + 1])
                    ##strandInfo += " 5' > " + str(numOfBasesDown_5PrimeDirection)  + "/" + str(numOfBases_next_crossover_5prime)
                    strandInfo += " 5' > " + str(numOfBasesDown_5PrimeDirection)
                    strandInfo += " --(%s)-- "%(len(allAtoms))
                    ##strandInfo += str(numOfBases_next_crossover_3prime) + "/" + str(numOfBasesDown_3PrimeDirection) + " > 3'"
                    strandInfo += str(numOfBasesDown_3PrimeDirection) + " > 3'"

        #Make sure that symbol like > are converted to html
        strandInfo = quote_html(strandInfo)

        return strandInfo
Beispiel #31
0
def make_or_remove_crossover(twoPls, make = True, cmdname = None):
    """
    Make or Remove (according to make option) a crossover, given Pl5_recognizers for its two Pl atoms.
    """

    # What we are doing is recognizing one local structure and replacing it with another
    # made from the same atoms. It'd sure be easier if I could do the manipulation in an mmp file,
    # save that somewhere, and read those to generate the operation! I'd have two sets of atoms, before and after,
    # and see how bonds and atomtypes got changed.

    # In this case it's not too hard to hand-code... I guess only the Pl atoms and their bonds are affected.
    # We probably do have to look at strand directions -- hmm, probably we should require them to exist before saying it's ok!
    # Or maybe better to give history error message when the command is chosen, saying you need to set them (or repair them) first...
    # Then we have to move the new/moved Pl atoms into a good position...

    # Note: Pl.ordered_bases are ordered by bond direction, to make this easier...
    # but if we want to patch up the directions in the end, do we need to care exactly which ones were defined?
    # or only "per-Pl"? hmm... it's per-Pl for now

    assert cmdname

    for pl in twoPls:
        if pl.ordered_bases is None: # should no longer be possible -- now checked before menu commands are offered [bruce 070604]
            ###BUG: this could have various causes, not only the one reported below! Somehow we need access to the
            # message supplied to the RecognizerError, for use here.
            ###REVIEW: Does that mean it should pass through compute methods (probably in a controlled way)
            # rather than making computed values None?
            # Or, should the value not be None, but a "frozen" examinable and reraisable version of the error exception??
            msg = "%s: Error: bond direction is locally undefined or inconsistent around %s" % (cmdname, pl.atom) ###UNTESTED
            print "should no longer be possible:", msg #bruce 070604
            env.history.message( redmsg( quote_html( msg)))
            return

    Pl1, Pl2 = twoPls
    a,b = Pl1.ordered_bases
    d,c = Pl2.ordered_bases # note: we use d,c rather than c,d so that the atom arrangement is as shown in the diagram below.

    # Note: for either the Make or Remove operation, the geometric arrangement is initially:
    #
    # c <-- Pl2 <-- d
    #
    # a --> Pl1 --> b
    #
    # and it ends up being (where dots indicate arrowheads, to show bond direction):
    #
    # c        d
    #  .      /
    #   \    .
    #  Pl1  Pl2
    #   .    \
    #  /      .
    # a        b
    #
    # Note: Pl1 stays attached to a, and Pl2 to d. Which two opposite bonds to preserve like that
    # is an arbitrary choice -- as long as Make and Remove make the same choice about that,
    # they'll reverse each other's effects precisely (assuming the sugars were initially correct as Ss or Sj).

    # break the bonds we no longer want
    for obj1, obj2 in [(Pl1, b), (Pl2, c)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.bust(make_bondpoints = False)

    # make the bonds we want and didn't already have
    for obj1, obj2 in [(Pl1, c), (Pl2, b)]:
        assert not atoms_are_bonded(obj1.atom, obj2.atom)
            ###e we should make bond_atoms do this assert itself, or maybe tolerate it (or does it already??)
        bond_atoms_faster(obj1.atom, obj2.atom, V_SINGLE)

    # set directions of all 4 bonds (even the preserved ones -- it's possible they were not set before,
    #  if some but not all bonds had directions set in the part of a strand whose directions we look at.)
    for obj1, obj2 in [(a, Pl1), (Pl1, c), (d, Pl2), (Pl2, b)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.set_bond_direction_from(obj1.atom, 1)

    # WARNING: after that bond rearrangement, don't use our Pl5_recognizers in ways that depend on Pl bonding,
    # since it's not well defined whether they think about the old or new bonding to give their answers.
    Pl_atoms = Pl1.atom, Pl2.atom
    del Pl1, Pl2, twoPls

    # transmute base sugars to Sj or Ss as appropriate
    if dna_updater_is_enabled():
        want = Element_Ss5 #bruce 080320 bugfix
    else:
        want = make and Element_Sj5 or Element_Ss5
    for obj in (a,b,c,d):
        obj.atom.Transmute(want)
        # Note: we do this after the bond making/breaking so it doesn't add singlets which mess us up.

    # move Pl atoms into better positions
    # (someday, consider using local minimize; for now, just place them directly between their new neighbor atoms,
    #  hopefully we leave them selected so user can easily do their own local minimize.)
    for pl in Pl_atoms:
        pl.setposn( average_value( map( lambda neighbor: neighbor.posn() , pl.neighbors() )))

    env.history.message( greenmsg( cmdname + ": ") + quote_html("(%s - %s)" % tuple(Pl_atoms)))

    #e need assy.changed()? evidently not.

    return # from make_or_remove_crossover
Beispiel #32
0
 def _done_history_msg(self):
     env.history.message(cmd + quote_html("%s %s." % (self.comment.name, self.action)))
Beispiel #33
0
def apply_btype_to_bond(
    btype,
    bond,
    allow_remake_bondpoints=True,
    suppress_history_message=False
):  #bruce 060703 added allow_remake_bondpoints for bug 833-1
    """
    Apply the given bond-type name (e.g. 'single') to the given bond, iff this is permitted by its atomtypes
    (or, new feature 060523, if it's permitted by its real atoms' possible atomtypes and their number of real bonds),
    and do whatever inferences are presently allowed [none are implemented as of 050727].
    Emit an appropriate history message. Do appropriate invals/updates.
    [#e should the inference policy and/or some controlling object be another argument? Maybe even a new first arg 'self'?]

    @param suppress_history_message: If True, it quietly converts the bondtypes
            without printing any history message.
    """
    # Note: this can be called either from a bond's context menu, or by using a Build mode dashboard tool to click on bonds
    # (or bondpoints as of 060702) and immediately change their types.

    #This flag will be returned by this function to tell the caller whether the
    #bond type of the given bond was changed
    bond_type_changed = True

    v6 = v6_from_btype(btype)
    oldname = quote_html(str(bond))

    def changeit(also_atypes=None):
        if v6 == bond.v6:
            bond_type_changed = False
            if not suppress_history_message:
                env.history.message("bond type of %s is already %s" %
                                    (oldname, btype))
        else:
            if also_atypes:
                # change atomtypes first (not sure if doing this first matters)
                atype1, atype2 = also_atypes

                def changeatomtype(atom, atype):
                    if atom.atomtype is not atype:
                        if not suppress_history_message:
                            msg = "changed %r from %s to %s" % (
                                atom, atom.atomtype.name, atype.name)
                            env.history.message(msg)
                        atom.set_atomtype(atype)
                        ### note[ probably 060523]:
                        # if we're an open bond, we have to prevent this process from removing us!
                        # (this is nim, so we're not yet safe to offer on open bonds.
                        #  Thus in fix for 833-1 [060703], atomtype changes are not allowed.)
                        pass
                    return  # from changeatomtype

                changeatomtype(bond.atom1, atype1)
                changeatomtype(bond.atom2, atype2)
            bond.set_v6(
                v6
            )  # this doesn't affect anything else or do any checks ####k #####@@@@@ check that
            ##e now do inferences on other bonds
            bond.changed()  ###k needed?? maybe it's now done by set_v6??
            if not suppress_history_message:
                env.history.message("changed bond type of %s to %s" %
                                    (oldname, btype))
            ###k not sure if it does gl_update when needed... how does menu use of this do that?? ###@@@
        return  # from changeit

    poss = poss1 = possible_bond_types(
        bond
    )  # only includes the ones which don't change the atomtypes -- try these first
    if btype in poss1:
        changeit()
        return bond_type_changed
    # otherwise figure out if we can change the atomtypes to make this work.
    # (The following code is predicted to work for either real or open bonds,
    #  but it is not safe to offer on open bonds for other reasons (commented above in changeatomtype).
    #  But we'll still figure out the situation, so the history message can be more useful.)
    if 1:
        # this is needed for allow_remake_bondpoints,
        # or for history advice about what that could have permitted:
        poss2, permitted1, permitted2 = possible_bond_types_for_elements(bond)
        # the only purpose of having the whole sequence poss2
        # (not just one element of it, equal to btype) is the error message
        if btype in poss2:
            atype1 = best_atype(bond.atom1, permitted1[v6])
            atype2 = best_atype(bond.atom2, permitted2[v6])
    if allow_remake_bondpoints:
        poss = poss2  # poss is whichever of poss1 or poss2 was actually allowed
        if btype in poss2:
            changeit((atype1, atype2))
            return bond_type_changed
    # It failed, but a variety of situations should be handled in the error message.
    # For error messages, sort them all the same way.
    poss1.sort()
    poss2.sort()
    poss.sort(
    )  #k not really needed, it's same mutable list, but keep this in case someone changes that
    if poss2 == poss:  # note, this happens if poss2 == poss1, or if they differ but allow_remake_bondpoints is true
        # permitting changing of atomtypes wouldn't make any difference
        if not suppress_history_message:
            msg = "can't change bond type of %s to %s" % (oldname, btype)
            msg2 = " -- permitted types are %s" % (poss)
            #e improve message -- %s of list looks like repr (for strings too)
            env.history.message(orangemsg(msg) + msg2)
        bond_type_changed = False
    elif btype in poss2:
        if allow_remake_bondpoints:
            print_compact_stack(
                "bug: allow_remake_bondpoints should not be true here: ")
        # the only reason we refused is that the UI won't allow remaking of bondpoints;
        # explain what the user would have to do to make it work (using the things computed above as if it had been permitted)
        # (as of 060703 this happens only when you click a bond type changing tool on a bondpoint,
        #  but following code will try to cover this for a real bond as well)
        unless = ""
        for atom, atype in [
            (bond.atom1, atype1), (bond.atom2, atype2)
        ]:  ##e ideally, in same order as printed in bond name
            if atype != atom.atomtype:
                if atom.is_singlet():
                    # should never happen
                    if env.debug:
                        print "debug: bug: %r is bondpoint but user is advised to change its atomtype" % atom
                if not unless:
                    unless = "change atomtype of %s to %s" % (atom, atype.name)
                else:
                    # this is not expected to ever happen, when called from UI as of 060703; it's untested ##@@
                    unless += ", and of %s to %s" % (atom, atype.name)
        msg = "can't change bond type of %s to %s, " % (
            oldname,
            btype,
        )
        bond_type_changed = False
        if unless:
            unless_msg = greenmsg("unless you %s" % (unless, ))
        else:
            unless_msg = redmsg("due to a bug")
        if not suppress_history_message:
            env.history.message(orangemsg(msg) + (unless_msg))

    else:
        # changing atomtypes makes a difference, but either way you're not allowed to change to this bond type
        if allow_remake_bondpoints:
            print_compact_stack(
                "bug: allow_remake_bondpoints should not be true here: ")
        extra = complement_sequences(poss2, poss1)
        if not extra:
            print_compact_stack("bug: extra should not be empty here: ")

        msg = "can't change bond type of %s to %s" % (oldname, btype)
        msg2 = " -- permitted types are %s, or %s if you change atomtypes" % (
            poss1, extra)
        #e improve message -- %s of list looks like repr (for strings too)
        bond_type_changed = False
        if not suppress_history_message:
            env.history.message(orangemsg(msg) + msg2)

    return bond_type_changed  # from apply_btype_to_bond
Beispiel #34
0
    def maybeTip(self, helpEvent):
        """
        Determines if this tooltip should be displayed. The tooltip will be displayed at
        helpEvent.globalPos() if an object is highlighted and the mouse hasn't moved for
        some period of time, called the "wake up delay" period, which is a user pref
        (not yet implemented in the Preferences dialog) currently set to 1 second.

        maybeTip() is called by GLPane.timerEvent() whenever the cursor is not moving to
        determine if the tooltip should be displayed.

        @param helpEvent: a QHelpEvent constructed by the caller
        @type helpEvent: QHelpEvent
        """
        # docstring used to also say:
        ## For more details about this member, see Qt documentation on QToolTip.maybeTip().
        # but this is unclear to me (since this class does not inherit from
        # QToolTip), so I removed it. [bruce 081208]

        debug = debug_pref("GLPane: graphics debug tooltip?",
                           Choice_boolean_False,
                           prefs_key = True )

        glpane = self.glpane
        selobj = glpane.selobj

        if debug:
            # russ 080715: Graphics debug tooltip.
            # bruce 081208/081211: revised, moved out of _getToolTipText,
            # made debug_pref.
            # note: we don't use glpane.MousePos since it's not reliable --
            # only some graphicsModes store it, and only in mouse press events.

            # Note: double buffering applies only to the color buffer,
            # not the stencil or depth buffers, which have only one copy.
            # The setting of GL_READ_BUFFER should have no effect on
            # glReadPixelsf from those buffers.
            # [bruce 081211 comment, based on Russ report of OpenGL doc]

            pos = helpEvent.pos()
            wX = pos.x()
            wY = glpane.height - pos.y() #review: off by 1??
            wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT)[0][0]
            stencil = glReadPixelsi(wX, wY, 1, 1, GL_STENCIL_INDEX)[0][0]
            savebuff = glGetInteger(GL_READ_BUFFER)
            whichbuff = {GL_FRONT:"front", GL_BACK:"back"}.get(savebuff, "unknown")
            redraw_counter = env.redraw_counter
            # Pixel data is sign-wrapped, in spite of specifying unsigned_byte.
            def us(b):
                if b < 0:
                    return 256 + b
                else:
                    return b
            def pixvals(buff):
                glReadBuffer(buff)
                gl_format, gl_type = GL_RGBA, GL_UNSIGNED_BYTE
                rgba = glReadPixels( wX, wY, 1, 1, gl_format, gl_type )[0][0]
                return (
                    "rgba %u, %u, %u, %u" %
                    (us(rgba[0]), us(rgba[1]), us(rgba[2]), us(rgba[3]))
                 )
            def redifnot(v1, v2, text):
                if v1 != v2:
                    return redmsg(text)
                else:
                    return text
            front_pixvals = pixvals(GL_FRONT)
            back_pixvals = pixvals(GL_BACK)
            glReadBuffer(savebuff)      # restore the saved value
            tipText = (
                "env.redraw = %d; selobj = %s<br>" % (redraw_counter, quote_html(str(selobj)),) +
                    # note: sometimes selobj is an instance of _UNKNOWN_SELOBJ_class... relates to testmode bug from renderText
                    # (confirmed that renderText zaps stencil and that that alone causes no bug in other graphicsmodes)
                    # TODO: I suspect this can be printed even during rendering... need to print glpane variables
                    # which indicate whether we're doing rendering now, e.g. current_glselect, drawing_phase;
                    # also modkeys (sp?), glselect_wanted
                "mouse position (xy): %d, %d<br>" % (wX, wY,) +
                "depth %f, stencil %d<br>" % (wZ, stencil) +
                redifnot(whichbuff, "back",
                         "current read buffer: %s<br>" % whichbuff ) +
                redifnot(glpane.glselect_wanted, 0,
                         "glselect_wanted: %s<br>" % (glpane.glselect_wanted,) ) +
                redifnot(glpane.current_glselect, False,
                         "current_glselect: %s<br>" % (glpane.current_glselect,) ) +
                redifnot(glpane.drawing_phase, "?",
                         "drawing_phase: %s<br>" % (glpane.drawing_phase,) ) +
                "front: " + front_pixvals + "<br>" +
                redifnot(back_pixvals, front_pixvals,
                         "back:  " + back_pixvals )
             )
            global _last_tipText
            if tipText != _last_tipText:
                print
                print tipText
                _last_tipText = tipText
            pass # use tipText below

        else:

            # <motionlessCursorDuration> is the amount of time the cursor (mouse) has been motionless.
            motionlessCursorDuration = time.time() - glpane.cursorMotionlessStartTime

            # Don't display the tooltip yet if <motionlessCursorDuration> hasn't exceeded the "wake up delay".
            # The wake up delay is currently set to 1 second in prefs_constants.py. Mark 060818.
            if motionlessCursorDuration < env.prefs[dynamicToolTipWakeUpDelay_prefs_key]:
                self.toolTipShown = False
                return

            # If an object is not currently highlighted, don't display a tooltip.
            if not selobj:
                return

            # If the highlighted object is a singlet,
            # don't display a tooltip for it.
            if isinstance(selobj, Atom) and (selobj.element is Singlet):
                return

            if self.toolTipShown:
                # The tooltip is already displayed, so return.
                # Do not allow tip() to be called again or it will "flash".
                return

            tipText = self._getToolTipText()

            pass

        # show the tipText

        if not tipText:
            tipText = ""
            # This makes sure that dynamic tip is not displayed when
            # the highlightable object is 'unknown' to the dynamic tip class.
            # (From QToolTip.showText doc: "If text is empty the tool tip is hidden.")

        showpos = helpEvent.globalPos()

        if debug:
            # show it a little lower to avoid the cursor obscuring the tooltip.
            # (might be useful even when not debugging, depending on the cursor)
            # [bruce 081208]
            showpos = showpos + QPoint(0, 10)

        QToolTip.showText(showpos, tipText)  #@@@ ninad061107 works fine but need code review

        self.toolTipShown = True
     try:
         return aCallable()
     except CadBug, e:
         reason = "Bug in the CAD system"
     except PluginBug, e:
         reason = "Bug in the plug-in"
     except UserError, e:
         reason = "User error"
     except Exception, e:
         #bruce 070518 revised the message in this case,
         # and revised subsequent code to set self.pluginException
         # even in this case (since I am interpreting it as a bug)
         reason = "Exception" #TODO: should improve, include exception name
     print_compact_traceback(reason + ": ")
     env.history.message(redmsg(reason + ": " +
                                quote_html(" - ".join(map(str, e.args))) ))
     self.remove_struct()
     self.pluginException = True
     return
 
 def _ok_or_preview(self, doneMsg = False, previewing = False):
     """
     Private method. Do the Done or Preview operation (and set the
     Qt wait cursor while doing it), according to flags.
     """
     ### REVIEW how to split this between GeneratorCommand and GeneratorPM,
     # and how to rename it then
     # [070724 code review]
     QApplication.setOverrideCursor( QCursor(Qt.WaitCursor) )
     self.win.assy.current_command_info(cmdname = self.cmdname)
     def aCallable():
Beispiel #36
0
        try:
            return aCallable()
        except CadBug, e:
            reason = "Bug in the CAD system"
        except PluginBug, e:
            reason = "Bug in the plug-in"
        except UserError, e:
            reason = "User error"
        except Exception, e:
            #bruce 070518 revised the message in this case,
            # and revised subsequent code to set self.pluginException
            # even in this case (since I am interpreting it as a bug)
            reason = "Exception"  #TODO: should improve, include exception name
        print_compact_traceback(reason + ": ")
        env.history.message(
            redmsg(reason + ": " + quote_html(" - ".join(map(str, e.args)))))
        self.remove_struct()
        self.pluginException = True
        return

    def _ok_or_preview(self, doneMsg=False, previewing=False):
        """
        Private method. Do the Done or Preview operation (and set the
        Qt wait cursor while doing it), according to flags.
        """
        ### REVIEW how to split this between GeneratorCommand and GeneratorPM,
        # and how to rename it then
        # [070724 code review]
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        self.win.assy.current_command_info(cmdname=self.cmdname)