registering classes in Blender for all time

Hi guys!

I’ve seen a lot of examples of making an add-on and they all have the register() function wich makes the class available while the actual Blender’s window is open. But not once you close it.
In fact I would like to make new bmesh.ops functions or bpy.ops and keep them like some guys do when they make an add-on wich is installed inside Blender that’s all I know…
Does anybody knows something more about that ?

Well, I have not quite reached the point of trying that myself. My own home-built operator is still being tweaked. However, I did find this bit of info, at the very bottom of http://yorik.uncreated.net/guestblog.php?2013=314

Then, we can run that script from the text editor (Text -> Run script, or Alt+P). It will not be permanent, but we can check if all went okay. If no error arises, and if the script does what it is meant to do, then we can consider it finished, and install it permanently, from the Preferences screen (addons -> from file…)

If you also have questions about creating a menu entry, I found this Blender page to be helpful:
https://wiki.blender.org/index.php/Dev:Py/Scripts/Cookbook/Code_snippets/Interface
Scroll down, a little past half-way, to the section “Adding an operator and appending it to a menu”, with the “twisted cylinder” example. That page also has a number of other good code examples, that have helped me.

I hope this little bit has been helpful. I wish I knew more.

Ok thanks for you effort anachronon.
The pages are really long but I like Yorik’s page it is helpful.
I’ve observed the same duality Alt+P/install from file.
Before you answered, I’ve looked inside a written tutorial from Michel Anders. I’ve found that when you put the value ‘PRESET’ in bl_info (inside the class case), it makes possible to save the file in Blender.

I’ve tried with a basic class:

class MakeTetrahedron(bpy.types.Operator) :
bl_idname = “mesh.make_tetrahedron”
bl_label = “Add Tetrahedron”
bl_options = {‘UNDO’,‘PRESET’}
def invoke(self, context, event) :
print(‘Kris is testing’)
return {“FINISHED”}

def register() :
bpy.utils.register_class(MakeTetrahedron)

if name == “main” :
register()

and I went in Blender-User preferences-add ons-install from file but the file didn’t appear.
Then I saw that in bl_info at the very beginning wich is used in Yorik’s site too ;).

bl_info = {
“name”: “IvyGen”,
“author”: “testscreenings, PKHG, TrumanBlending”,
“version”: (0, 1, 1),
“blender”: (2, 59, 0),
“location”: “View3D > Add > Curve”,
“description”: "Adds generated ivy to a mesh object starting "
“at the 3D cursor”,
“warning”: “”,
“wiki_url”: “http://wiki.blender.org/index.php/Extensions:2.6/Py/
“Scripts/Curve/Ivy_Gen”,
“category”: “Add Curve”,
}

“location”: “View3D > Add > Curve” helps to indentify the category of your add-on and probably helps to make a saved add-on and at the same time a bpy.ops function.

I’ll figure out a bit later. If you got time, maybe you could try some things too.
Thanks again !

Thanks for that. I found the Michel Anders page to be quite helpful. However, I’m not quite ready to tackle the add-on procedure, just yet. I still have a couple tweaks to make on my project. Maybe, in a couple days? Hopefully, someone who knows more will reply. But, help in the coding section has been a bit hard to come-by, of late. Everyone on vacation?

Maybe, but you tried, and that makes the difference between someone who fail and someone who succeeds ;).
Happy that the page helps you. What project are you working on actually ?

Hey Kris. Honestly, it seems that we have had the Python forum all to ourselves for at least the past week. The only other two posts were several days ago, with questions somewhat similar to yours. No answers though. No, wait, I take it back. I did get one helpful answer, to a simple question that I asked. But, beyond that, it seems that all of the expert coders have gone on vacation.

As for the project I’m working on, it’s an operator that creates a wire or cable, hanging between two points specified by the user. I use a bezier curve, which can simulate either a catenary or a parabola, depending on the constants used. You can see a sample of it in my other active BA thread, “Makes No Sense”. The basic concept works without a hitch. But, the little bits of polish and cleanup have been an endless fight.

Woa !.. I haven’t watched the dates but it sounds scarying… lol.
Hopefully for me I could figure out a bit.

Great! Mmm… that’s for a new add-on wich can be used like for urbanism. Yeah I’ve watched a bit. If the cable is hanging you just have to use cosh function it gives you exactly the form, I’ve learnt it in my physic studies. However, if it’s generally Bezier is really necessary. I’m working on Bezier forms in another project. It could draw like independently from curve objects just by using the formula for drawing vases or fruits etc…

And about this actual project, I’ve done finally the registering and I could see the bpy.ops function and use it. gonna make some print screen to show the result.

Yeah, the a cable add-on could be quite useful. It would work for urban scenes, with things like power lines or suspension bridges. It could also be used in nature scenes, for things like hanging vines. Also, indoor scenes, for things like strings of Christmas lights. Actually, the math part was pretty easy, as I am only using a 3-point bezier curve. With only 3 points, I can easily simulate the shape of either a parabola or a cosh function. The hard part has been making a working operator.

Can’t wait to see your screen-shot result. Could you also post some code?

in the end you can see the operator appearing in the info console.
You can also see an Error message just because in the script I haven’t put unregister, the only important for me was that it registers for the beginning. Not bad uh !?

For the add-on I’ve first took an existing addon wich had just the class without the beginning: bl_info = {…}
I’ve thrown away all lines and kept just what could make the ad-on work and show me that the add-on works.
Here is the script:

import bpy

bl_info = {
“name”: “Test”,
“author”: “Kristof Smiljanic”,
“version”: (0, 3, 0),
“blender”: (2, 74, 5),
“location”: “View3D > Add > Mesh”,
“description”: “test class”,
“warning”: “unuseful”,
“category”: “Add Mesh”,
}

class MakeTetrahedron(bpy.types.Operator) :
bl_idname = “mesh.make_tetrahedron”
bl_label = “Add Tetrahedron”
bl_options = {‘REGISTER’,‘UNDO’,‘PRESET’}
def invoke(self, context, event):
print(‘Kris is testing’)
return {“FINISHED”}

def register() :
bpy.utils.register_class(MakeTetrahedron)

if name == “main” :
register()

Attachments




Kuhl! The screen shots are extremely helpful. Just a couple questions. Did you have to manually copy your add-on to the addon’s folder? Or, did Blender do it for you. Also, “bl_info” needs to be outside of your operator class, “MakeTetrahedron”? Finally, did adding an unregister() function clear the error messages?

bl_info is necessarely put at the beginning (outside) that’s the first thing.
But, I’m not sure if you could’nt put another bl_info inside, haven’t tried yet, I didn’t have the time today.
Without any doubt it’s THE REASON I always trust the console never lied to me. I have tried to add an unregister and it showed me another problem, it was about a bl_rna bla bla… Have to make it more clear.
We’ll see it soon ! :wink:

PS: Yeah ! I actually don’t have to, but I did it because I’ve read that it has to, but I’ve realised that you can put it anywhere when you install from file an add-on it always asks you where is the file, no matter the place.

Did you need to remove the statement?:

if name == “main” :
register()

I read that this was needed when the script was being run from the Text Editor. Does it have to be taken out, when the script is run as an Add-On?

On the “unregister()” error question, you could be running into a Blender problem that has given me so many headaches—Blender appending “.001”, “,002”, “.003”, etc to duplicate items. In other words, instead of getting “MakeTetrahedron”, you may be getting “MakeTetrahedron.001”, “MakeTetrahedron.002”, etc. If so, “bpy.utils.unregister_class(MakeTetrahedron)” will return an error: “class ‘MakeTetrahedron’ does not exist” Try completely closing Blender and restarting. This may clear the old classes out of the memory. Then, before running your script again, add your unregister() function. This may clear the error problem.

Or, have you tried using these statements instead?:


def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)

I seem to see this method used in more examples, than “register_class”. Perhaps, this avoids the problem of duplicates altogether?

Good remark ! I’ve read it too but I totally forgot about this detail!
MakeTetrahedron was a script uniquely made for Text Editor and that’s why there is this form of register. I’ve let it thinking that it was ok, on my desire to solve it quickly. Now I’ve look an add-on inside Blender’s add-ons file and I found the same form that you wrote, so that certainly woul change the problem finally, it wasn’t only about unregister function. :slight_smile:
I’m gonna try it to see the result.

PS: Got tired Yesterday and today so wasn’t really active.

Hopefully, that will fix the problem. Maybe, when you feel up to it, you could write to say whether that worked or not. My little project is almost ready to go to the add-on stage. if it works, I’ll try to post a copy, so that others can test it.

So, I tried it ! And it seems like working ! No error is appearing, so that means that the add-on is fully accepted by Blender.
Only one thing, when I call the function inside Python console, it returns {'PASS THROUGH"} that means that the apply of the function is in fact cancelled. It seems to be ok if we consider that I haven’t put the context as a parameter inside an operator.
There is various types of add-ons and as I’ve understood every operator has to have context inside for sure. So in your case you have to use something like:

class…

def function(…context):

that’s all I know, you can try and tell me what it gives to you.

I believe that you are getting “PASS_THROUGH” because you have no “execute()” function. Your script isn’t failing—it’s just not doing anything. “Pass through” simply means that the operator loaded correctly, and that there was noting more to do after that.

If it helps, here is a simple bit of code, that I downloaded from the Blender reference site. It is a good example of the use of the execute() function. It is also a good example of operator properties, where the user can enter values. The code is fully functional, and can be run from the text editor. Hopefully, you will find it as helpful as I did.


#----------------------------------------------------------
# File twisted.py
#----------------------------------------------------------
import bpy, math
 
def addTwistedCylinder(context, r, nseg, vstep, nplanes, twist):
    verts = []
    faces = []
    w = 2*math.pi/nseg
    a = 0
    da = twist*math.pi/180
    for j in range(nplanes+1):    
        z = j*vstep
        a += da
        for i in range(nseg):
            verts.append((r*math.cos(w*i+a), r*math.sin(w*i+a), z))
            if j > 0:
                i0 = (j-1)*nseg
                i1 = j*nseg
                for i in range(1, nseg):
                    faces.append((i0+i-1, i0+i, i1+i, i1+i-1))
                faces.append((i0+nseg-1, i0, i1, i1+nseg-1))
 
    me = bpy.data.meshes.new("TwistedCylinder")
    me.from_pydata(verts, [], faces)
    ob = bpy.data.objects.new("TwistedCylinder", me)
    context.scene.objects.link(ob)
    context.scene.objects.active = ob
    return ob
 
#
#    User interface
#
 
from bpy.props import *
 
class MESH_OT_primitive_twisted_cylinder_add(bpy.types.Operator):
    '''Add a twisted cylinder'''
    bl_idname = "mesh.primitive_twisted_cylinder_add"
    bl_label = "Add twisted cylinder"
    bl_options = {'REGISTER', 'UNDO'}
 
    radius = FloatProperty(name="Radius",
            default=1.0, min=0.01, max=100.0)
    nseg = IntProperty(name="Major Segments",
            description="Number of segments for one layer",
            default=12, min=3, max=256)
    vstep = FloatProperty(name="Vertical step",
            description="Distance between subsequent planes",
            default=1.0, min=0.01, max=100.0)
    nplanes = IntProperty(name="Planes",
            description="Number of vertical planes",
            default=4, min=2, max=256)
    twist = FloatProperty(name="Twist angle",
            description="Angle between subsequent planes (degrees)",
            default=15, min=0, max=90)
 
    location = FloatVectorProperty(name="Location")
    rotation = FloatVectorProperty(name="Rotation")
    # Note: rotation in radians!
 
    def execute(self, context):
        ob = addTwistedCylinder(context, 
            self.radius, self.nseg, self.vstep, self.nplanes, self.twist)
        ob.location = self.location
        ob.rotation_euler = self.rotation
        #context.scene.objects.link(ob)
        #context.scene.objects.active = ob
        return {'FINISHED'}
 
#
#    Registration
#    Makes it possible to access the script from the Add > Mesh menu
#
 
def menu_func(self, context):
    self.layout.operator("mesh.primitive_twisted_cylinder_add", 
        text="Twisted cylinder", 
        icon='MESH_TORUS')
 
def register():
   bpy.utils.register_module(__name__)
   bpy.types.INFO_MT_mesh_add.prepend(menu_func)
 
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.INFO_MT_mesh_add.remove(menu_func)
 
if __name__ == "__main__":
    register()

My function is trully doing barely nothing special, it should be that too, that’s why I didn’t use the context, because nothing happends. I’m just thinking if execute is the arbitrary name of the function that will execute actions or, necessary to be called like that.
Ine every case, I think we’re proach to the solution, just have to verify some details.

Try adding just these two lines of code, to you operator:


    def execute(self, context):
        return {'FINISHED'}

The operator still won’t do anything, because execute() has no guts. But, your message should change from “pass through” to “finished”