示例#1
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
示例#2
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
示例#3
0
def _bond_type_menu_section(bond): #bruce 050716; replaces bond_type_submenu_spec for Alpha6
    """
    Return a menu_spec for changing the bond_type of this bond
    (as one or more checkmark items, one per permitted bond-type given the atomtypes),
    or if the bond-type is unchangeable, a disabled menu item for displaying the type
    (which looks the same as when the bond type is changeable, except for being disabled).
    (If the current bond type is not permitted, it's still present and checked, but disabled,
     and it might have a warning saying it's illegal.)
    """
    # this assert is true, but it would cause an import loop:
    ## assert isinstance(bond, Bond)
    btype_now = btype_from_v6(bond.v6)
    poss1 = possible_bond_types(bond) # a list of strings which are bond-type names, in order of increasing bond order
    poss, permitted1, permitted2 = possible_bond_types_for_elements(bond) # new feature 060703
    ##e could put weird ones (graphitic, carbomeric) last and/or in parens, in subtext below
    types = list(poss)
    for btype in poss1:
        if btype not in types:
            print "should never happen: %r not in %r" % (btype, poss) # intentional: "not in types" above, "not in poss" here
            types.append(btype)
    if btype_now not in types:
        types.append(btype_now) # put this one last, since it's illegal; warning for it is computed later
    assert len(types) > 0
    # types is the list of bond types for which to make menu items, in order;
    # now make them, and figure out which ones are checked and/or disabled;
    # we disable even legal ones iff there is only one bond type in types
    # (which means, if current type is illegal, it is disabled and the sole legal type is enabled).
    disable_legal_types = (len(types) == 1)
    res = []
    for btype in types: # include current value even if it's illegal
        subtext = "%s bond" % btype # this string might be extended below
        checked = (btype == btype_now)
        command = ( lambda arg1=None, arg2=None, btype=btype, bond=bond: apply_btype_to_bond(btype, bond) )
        warning = warning2 = ""
        if btype not in poss:
            # illegal btype (note: it will be the current one, and thus be the only checked one)
            warning = "illegal"
            disabled = True
        else:
            # legal btype
            warning = bond_type_warning(bond, btype) # might be "" (or None??) for no warning
            if btype not in poss1:
                # new feature 060703
                # try1: too long and boring (when in most menu entries):
                ## warning2 = "would change atomtypes"
                # try2: say which atomtypes we'd change to, in same order of atoms as the bond name
                v6 = v6_from_btype(btype)
                atype1 = best_atype(bond.atom1, permitted1[v6])
                atype2 = best_atype(bond.atom2, permitted2[v6])
                in_order = [atype1, atype2] ##e stub; see code in Bond.__str__
                warning2 = "%s<->%s" % tuple([atype.name for atype in in_order])
            disabled = disable_legal_types
                # might change this if some neighbor bonds are locked (nim), or if we want to show non-possible choices
        if warning2:
            subtext += " (%s)" % warning2
        if warning:
            subtext += " (%s)" % warning
        res.append(( subtext, command,
                         disabled and 'disabled' or None,
                         checked and 'checked' or None ))
    ##e if >1 legal value, maybe we should add a toggleable checkmark item to permit "locking" the bond to its current bond type;
    # this won't be needed until we have better bond inference (except maybe for bondpoints),
    # since right now [still true 060703] we never alter real bond types except when the user does an action on that specific bond.
    if not bond.is_open_bond():
        ## command = ( lambda arg1 = None, arg2 = None, bond = bond: bond.bust() )
        command = ( lambda bond = bond: delete_bond(bond) )
        res.append(None) # separator
        res.append(("Delete Bond", command))
    return res
示例#4
0
def _bond_type_menu_section(
        bond):  #bruce 050716; replaces bond_type_submenu_spec for Alpha6
    """
    Return a menu_spec for changing the bond_type of this bond
    (as one or more checkmark items, one per permitted bond-type given the atomtypes),
    or if the bond-type is unchangeable, a disabled menu item for displaying the type
    (which looks the same as when the bond type is changeable, except for being disabled).
    (If the current bond type is not permitted, it's still present and checked, but disabled,
     and it might have a warning saying it's illegal.)
    """
    # this assert is true, but it would cause an import loop:
    ## assert isinstance(bond, Bond)
    btype_now = btype_from_v6(bond.v6)
    poss1 = possible_bond_types(
        bond
    )  # a list of strings which are bond-type names, in order of increasing bond order
    poss, permitted1, permitted2 = possible_bond_types_for_elements(
        bond)  # new feature 060703
    ##e could put weird ones (graphitic, carbomeric) last and/or in parens, in subtext below
    types = list(poss)
    for btype in poss1:
        if btype not in types:
            print "should never happen: %r not in %r" % (
                btype, poss
            )  # intentional: "not in types" above, "not in poss" here
            types.append(btype)
    if btype_now not in types:
        types.append(
            btype_now
        )  # put this one last, since it's illegal; warning for it is computed later
    assert len(types) > 0
    # types is the list of bond types for which to make menu items, in order;
    # now make them, and figure out which ones are checked and/or disabled;
    # we disable even legal ones iff there is only one bond type in types
    # (which means, if current type is illegal, it is disabled and the sole legal type is enabled).
    disable_legal_types = (len(types) == 1)
    res = []
    for btype in types:  # include current value even if it's illegal
        subtext = "%s bond" % btype  # this string might be extended below
        checked = (btype == btype_now)
        command = (lambda arg1=None, arg2=None, btype=btype, bond=bond:
                   apply_btype_to_bond(btype, bond))
        warning = warning2 = ""
        if btype not in poss:
            # illegal btype (note: it will be the current one, and thus be the only checked one)
            warning = "illegal"
            disabled = True
        else:
            # legal btype
            warning = bond_type_warning(
                bond, btype)  # might be "" (or None??) for no warning
            if btype not in poss1:
                # new feature 060703
                # try1: too long and boring (when in most menu entries):
                ## warning2 = "would change atomtypes"
                # try2: say which atomtypes we'd change to, in same order of atoms as the bond name
                v6 = v6_from_btype(btype)
                atype1 = best_atype(bond.atom1, permitted1[v6])
                atype2 = best_atype(bond.atom2, permitted2[v6])
                in_order = [atype1, atype2]  ##e stub; see code in Bond.__str__
                warning2 = "%s<->%s" % tuple(
                    [atype.name for atype in in_order])
            disabled = disable_legal_types
            # might change this if some neighbor bonds are locked (nim), or if we want to show non-possible choices
        if warning2:
            subtext += " (%s)" % warning2
        if warning:
            subtext += " (%s)" % warning
        res.append((subtext, command, disabled and 'disabled'
                    or None, checked and 'checked' or None))
    ##e if >1 legal value, maybe we should add a toggleable checkmark item to permit "locking" the bond to its current bond type;
    # this won't be needed until we have better bond inference (except maybe for bondpoints),
    # since right now [still true 060703] we never alter real bond types except when the user does an action on that specific bond.
    if not bond.is_open_bond():
        ## command = ( lambda arg1 = None, arg2 = None, bond = bond: bond.bust() )
        command = (lambda bond=bond: delete_bond(bond))
        res.append(None)  # separator
        res.append(("Delete Bond", command))
    return res