"Axis Center " Script button reuse problem



  • Hi:
    I am a beginner of Python, my English is not very good, please forgive me.I wrote a script that was broadly similar to the new "Axis Center" feature in R21, but had difficulty writing an interactive interface.The main problem is that the first time you click the "Null" button, you can normally create a Null object, but the second time the error is reported, what is going on? How do buttons run repeatedly ?

    # -*- coding: UTF-8 -*-
    
     import c4d
    from c4d import gui
    
    __TITLE__ = "Axis Center"
    null = []
    
     class MyDialog(gui.GeDialog):
    def CreateLayout(self):
        self.SetTitle(__TITLE__ )
        self.GroupBegin(id=1000, flags=c4d.BFH_FIT, cols=2, title="Axes")
        self.GroupBorder(c4d.BORDER_GROUP_IN)
        self.GroupSpace(5, 5)
        self.GroupBorderSpace(10, 0, 10, 4)
        self.AddEditSlider(id = 1001, flags=c4d.BFH_RIGHT, initw=60, inith=15)
        self.AddEditSlider(id = 1002, flags=c4d.BFH_RIGHT, initw=60, inith=15)
        self.AddEditSlider(id = 1003, flags=c4d.BFH_RIGHT, initw=60, inith=15)
        self.AddCheckbox(id = 1004, flags=c4d.BFH_RIGHT, initw=80, inith=15, name="Null")
        self.GroupEnd()
    
        self.AddButton(1005, flags=c4d.BFH_LEFT, initw=180, inith=10, name="GO")
        return True
    
    def Command(self,id,msg):
        if id == 1004:
            null.append(c4d.BaseObject(5140))
        else:
            pass
    
        if id == 1005:
            main()
        return True
    
    def main():
    present = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
    if not present:
        print "The polygon object was not selected"
        gui.MessageDialog('The polygon object was not selected!')
        return
    
    for obj in present:
        pl  = obj.GetAllPoints()
        pc  = len(pl)
        sel = obj.GetPointS()
        sel = sel.GetAll(pc)
    
    indexe  = [i for i,o in enumerate(pl)]
    position  = [o for i,o in enumerate(pl)]
    il  = [i for i,o in enumerate(pl) if sel[i] == 1]
    spl = [o for i,o in enumerate(pl) if sel[i] == 1]
    
    if len(spl) < 1:
        print "no enough points"
        gui.MessageDialog('no enough points!')
        return False
    
    x_record =  [round(a[0],7) for a in spl if round(a[0],7) != -0.0]
    x_record += [0 for b in spl if round(b[0],7) == -0.0]
    
    y_record =  [round(c[1],7) for c in spl if round(c[1],7) != -0.0]
    y_record += [0 for d in spl if round(d[1],7) == -0.0]
    
    z_record =  [round(e[2],7) for e in spl if round(e[2],7) != -0.0]
    z_record += [0 for f in spl if round(f[2],7) == -0.0]
    
    x_record.sort(reverse=True)
    y_record.sort(reverse=True)
    z_record.sort(reverse=True)
    
    x = (round((x_record[0] - x_record[-1]) * dlg.GetFloat(1001) * 0.01 , 5)) + x_record[-1]
    y = (round((y_record[0] - y_record[-1]) * dlg.GetFloat(1002) * 0.01 , 5)) + y_record[-1]
    z = (round((z_record[0] - z_record[-1]) * dlg.GetFloat(1003) * 0.01 , 5)) + z_record[-1]
    
    m = obj.GetMg()
    mx   = obj.GetMg()
    mx.off = mx.Mul(c4d.Vector(x,y,z))
    
    matrix = []
    for orientation in position:
        mt = c4d.Matrix()
        mt.off = orientation
        m_mt = m.__mul__(mt)
        swop = m_mt.__invert__()
        result = swop.__mul__(mx)
        matrix.append(result.__invert__())
    
    doc.StartUndo()
    doc.AddUndo(c4d.UNDOTYPE_CHANGE,obj)
    
    obj.SetMg(mx)
    if not null:
        pass
    
    else:
        doc.InsertObject(null[0])
        null[0].SetMg(mx)
    
    point_position = [(t.off) for t in matrix]
    point_indexe   = [([indexe[z],point_position[z]]) for z in range(len(indexe))]
    set_point      = [obj.SetPoint(k[0],k[1]) for k in point_indexe]
    
    obj.Message(c4d.MSG_UPDATE)
    c4d.EventAdd()
    doc.EndUndo()
    
    if __name__=='__main__':
    __anchor__ = "\u98de\u821e\u7684\u56e2\u5b50"
    dlg = MyDialog()
    dlg.Open(c4d.DLG_TYPE_ASYNC, 450, 200, 300, 300)


  • Hi,

    welcome to the forum and no worries, most people here do not speak perfect English. But you might want to do two things in the future: Check if the indentations are correct in your code and give us the exact exceptions if you run into them.

    In your code the problem lies in the way how you handle your "create null" checkbox. You will find details in the commented code below (which I have also sort of fixed for you). Only sort of, because there a quite a few other problems with your code if I am not totally misunderstanding your goals, which I have neither mentioned nor rectified. I also took the freedom of giving some unwanted stylistic advice ;)

    With all that nagging I feel that it is necessary to point out, that you did pretty well with your code, especially some of the list comprehensions are quite elegant for a beginner. Keep at it!

    # -*- coding: UTF-8 -*-
    """
    """
    
    import c4d
    from c4d import gui
    
    __TITLE__ = "Axis Center"
    null = []
    
    
    class MyDialog(gui.GeDialog):
        """You should put little bits of explanation what you are doing for
        yourself and others in Pythons doc strings, i.e. the thing we are
        currently typing in.
        """
    
        def CreateLayout(self):
            """
            """
            self.SetTitle(__TITLE__)
            self.GroupBegin(id=1000, flags=c4d.BFH_FIT, cols=2, title="Axes")
            self.GroupBorder(c4d.BORDER_GROUP_IN)
            self.GroupSpace(5, 5)
            self.GroupBorderSpace(10, 0, 10, 4)
            self.AddEditSlider(id=1001, flags=c4d.BFH_RIGHT, initw=60, inith=15)
            self.AddEditSlider(id=1002, flags=c4d.BFH_RIGHT, initw=60, inith=15)
            self.AddEditSlider(id=1003, flags=c4d.BFH_RIGHT, initw=60, inith=15)
            self.AddCheckbox(id=1004, flags=c4d.BFH_RIGHT,
                             initw=80, inith=15, name="Null")
            self.GroupEnd()
    
            self.AddButton(1005, flags=c4d.BFH_LEFT,
                           initw=180, inith=10, name="GO")
            return True
    
        def Command(self, id, msg):
            """
            """
            # Here lies the major problem with your script, the following lines
            # will only be executed when you have ticked the checkbox. So when
            # you press the 'GO' button twice in a row after ticking the
            # checkbox, only one null will be inserted. This then means that
            # you will try to insert the same object again into the document.
    
            # This also means that your 'null' list will be modified when
            # the user unchecks the checkbox. In the code in 'main' you are also
            # grabbing the wrong null, because you pick the first null (null[0]),
            # but here you do append and do not insert in front.
    
            # Since you do not use the list as a list at all, I simply removed it
            # all together, because there is also the problem of ambiguity which
            # null object is meant to be used for which polygon object.
    
            # Also module scope variables should be all uppercase and not
            # ambiguous, something like 'NULL_OBJECTS' for example.
    
            # if id == 1004:
            #     null.append(c4d.BaseObject(5140))
            # else:
            #     pass
    
            if id == 1005:
                # Poll the checkbox if it is selected.
                insert_null = self.GetBool(1004)
                # And pass is to your 'main' function as an argument.
                main(insert_null)
            return True
    
    
    def main(insert_null):
        """This function should probably be split into multiple parts, as it does
        too many things at once.
    
        Args:
            insert_null (bool): If to create a null object or not.
        """
        # Its good to be defensive, but you are only going half way here,
        # you should actually check if the object is a PolygonObject. This
        # is more a opinion, but I think you should also use exceptions and
        # not 'print'. You are also using the method to get all objects, but
        # do not really use it in the end.
        nodes = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
        # if not nodes:
        #     print "The polygon object was not selected"
        #     gui.MessageDialog('The polygon object was not selected!')
        #     return
        if not nodes:
            raise ValueError("No objects selected.")
        for node in nodes:
            if not isinstance(node, c4d.PolygonObject):
                raise TypeError("Selection contains non PolygonObject.")
    
        # As already mentioned, this does not make much sense, you calculate
        # all this stuff for every object and throw it away after that without
        # using it, only the stats calculated for the last object will be used.
    
        # On a side note, the variable naming is not very good here. I know that
        # you see a lot of code like that out in the wild, and especially MAXON
        # themselves are also a big offender in this department, but you should
        # not use abbreviated variable names, as they do make things harder to
        # read.
        for obj in nodes:
            pl = obj.GetAllPoints()
            pc = len(pl)
            sel = obj.GetPointS()
            sel = sel.GetAll(pc)
    
        # This is quite nice and pythonic, especially for a beginner (good job!),
        # but still could be compacted a lot.
    
        # selected_point_index = {i: p for i, p in enumerate(pl) if sel[i]}
    
        indexe = [i for i, o in enumerate(pl)]
        position = [o for i, o in enumerate(pl)]
        il = [i for i, o in enumerate(pl) if sel[i] == 1]
        spl = [o for i, o in enumerate(pl) if sel[i] == 1]
    
        if len(spl) < 1:
            print "no enough points"
            gui.MessageDialog('no enough points!')
            return False
    
        # I think you are trying to compute the bounding box here. First of all
        #  there is the method 'BaseObject.GetRad' which will give you the
        #  bounding box vector for an object.
    
        # While the first line of each 'record' line pair is a bit complicated,
        # but all together fine, the second one does not make any sense
        # whatsoever, especially paired with the sorting you do employ
        # afterwards. I did not touch this code, because I do not get what
        # you were after, but here almost certainly lies a bug.
    
        x_record = [round(a[0], 7) for a in spl if round(a[0], 7) != -0.0]
        x_record += [0 for b in spl if round(b[0], 7) == -0.0]
    
        y_record = [round(c[1], 7) for c in spl if round(c[1], 7) != -0.0]
        y_record += [0 for d in spl if round(d[1], 7) == -0.0]
    
        z_record = [round(e[2], 7) for e in spl if round(e[2], 7) != -0.0]
        z_record += [0 for f in spl if round(f[2], 7) == -0.0]
    
        x_record.sort(reverse=True)
        y_record.sort(reverse=True)
        z_record.sort(reverse=True)
    
        # This part is fine again for the bounding box calculation.
    
        x = (round((x_record[0] - x_record[-1]) *
                   dlg.GetFloat(1001) * 0.01, 5)) + x_record[-1]
        y = (round((y_record[0] - y_record[-1]) *
                   dlg.GetFloat(1002) * 0.01, 5)) + y_record[-1]
        z = (round((z_record[0] - z_record[-1]) *
                   dlg.GetFloat(1003) * 0.01, 5)) + z_record[-1]
    
        m = obj.GetMg()
        mx = obj.GetMg()
        mx.off = mx.Mul(c4d.Vector(x, y, z))
    
        # Also fine, but underscore methods are not intended to be called
        # directly, instead you should use the operator (this is only syntactic
        # sugar)
        matrix = []
        for orientation in position:
            mt = c4d.Matrix()
            mt.off = orientation
            m_mt = m.__mul__(mt)  # m * mt
            swop = m_mt.__invert__()  # ~m_mt
            result = swop.__mul__(mx)
            matrix.append(result.__invert__())
    
        doc.StartUndo()
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj)
    
        obj.SetMg(mx)
    
        # Here is the only place where you use your 'null' list again. Since I
        # commented the code out in the dialog, we have to this instead.
    
        # if not null:
        #     pass
        # else:
        #     doc.InsertObject(null[0])
        #     null[0].SetMg(mx)
    
        # Create a null and set its global matrix to your 'mx' matrix if we
        # indicated to do so with the function call.
        if insert_null:
            node = c4d.BaseList2D(c4d.Onull)
            node.SetMg(mx)
            doc.InsertObject(node)
    
        point_position = [(t.off) for t in matrix]
        point_indexe = [([indexe[z], point_position[z]])
                        for z in range(len(indexe))]
        set_point = [obj.SetPoint(k[0], k[1]) for k in point_indexe]
    
        obj.Message(c4d.MSG_UPDATE)
        c4d.EventAdd()
        doc.EndUndo()
    
    
    if __name__ == '__main__':
        __anchor__ = "\u98de\u821e\u7684\u56e2\u5b50"
        dlg = MyDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, 450, 200, 300, 300)
    

    Cheers.
    zipit



  • Thank you very much for your help to solve the previous doubts. I will continue to work hard.


Log in to reply