Writing a Macro in Python - Wondering if what I want to do is a major or minor task

I wrote a plugin in Python, several years ago. I know I can do it and can relearn quickly. What I’m trying to work out, first, is if what I want to do is possible and if it’ll take hours or days or weeks. In other words, I’m checking to see if this is something that will take a lot or a moderate amount of time and effort.

I want to write a macro I can easily call. First I’d select an object, then call my macro (using the “Quick Favorites” menu is fine for this). I want the macro to use a dialog/requestor box to let me pick a size, integers from 8-16. I’d be okay using a slider or drop-down or anything that gives me multiple choice. I’d like, also, to be able to include a text box so I can enter in a number out of that range by hand.

Once I get a number from the dialog input, I would use a formula to scale the item from a value derived from the choice picked in the dialog. (For instance, 8 does not mean scale to 8%, not even to 108%, but to 108.9%.)

Of course, the dialog would have to include a “Cancel” option to stop the macro.

I think I remember that when writing a Python macro, there were issues using any kind of dialog or requestor in Blender.

I can do the work myself, I just want to determine if writing a macro or plugin like this in Python is simple or a big undertaking and if I can do what I want with the dialog. As I said, it’s been years since I wrote any Python code for Blender.

(And if there’s an easier or better way to do it, I’m open to it.)

Well, I don’t think it’s a big work … For an experienced user it’s likely to take a few hours…
If you’re new but clever probably a day could be enough.

What you can do to make things even simpler is to start with a template, and rather than doing a dialog box which is doable but a bit involved in blender, you can use the redo panel to expose your settings :

On top of that it looks simple enough so chat GPT can probably do something there and there is also a lot of examples in stackexchange or here…

Have fun !

1 Like

Okay, that’s one of the two main things I wanted to know. I know it may seem like a vague question, but often, with developing any software, or working with any API or software that includes things like me customizing it and compiling it, knowing if it’s a major project or smaller one is a HUGE help. Then I know whether to put it on my short to-do list or on the “Do when I have a few days off” list.

Quoting out of order to handle this first. So I can do a dialog in Blender? I didn’t remember if I could. I remember having trouble when I tried it before. Is the dialog done through Blender or through a Python library? (I remember one issue I had before is that Blender uses its own Python interpreter, so keeping Python updated for Blender could be, from what I remember, a nightmare if you add many libraries to the Blender instance of Python. I remember, in the past, writing my plugin, then upgrading Blender, and having to re-install Python libraries - either in a new folder for the update or something like that. It wasn’t fun.)

Okay, I need to look that up. I’ve been doing a lot of work with ESP32 libraries in the MCU world and, for now, the word “template” is inseperable from some work in there. I don’t remember if I’ve used Blender templates or not - or if I know how they work. My head is just coming out of “MCU space” and returning to “Blender space.”

Is the “redo panel” a new thing? I don’t think I’ve heard of that before. But now that you bring this up, I think I remember (am I right about this?) that I could add a panel for my plugin and it was easier to input data through that panel than through a requestor. But it’s been so long, I could misremember. And if I remember correctly, is it easy to get a panel and a Python macro/plugin to interact? I’m wondering if I could just click on an object, go to that panel, pick the percentage increaese, click, “Apply,” and have it do its thing.

I dont know if you are aware of Serpens?

The addon to make addons with?

Hope that helps

1 Like

Yes you have different way of exposing parameters. You can do a pop-up window with a “run” button. You can do a panel in the interface…

I really don’t recommend using an external library for that, and it would be much simpler to use blender’s own API for that.

Well maybe template is a misleading word there. In blender it’s just simple example scripts that you can study or use as a basis to do your own thing.
But you can probably find a lot of simple examples online too !

Hum I think it’s there since forever :slight_smile: If you had a primitive you’ll see the redo panel in the corner of the screen. That allows you to set parameters.
Since in blender the logic is generally “do the action first, then do the settings” . Where in other software you generally have to enter the settings and then do the operation.

Anyway, adding a floating window isn’t too much of a trouble either.
The redo panel might just save you a bit of time …

And as @AlphaChannel said, Serpens can be interesting to look at indeed !

2 Likes

Or hours with chatGPT!

Never heard of it, so I’ll be looking into it. Thanks!

Does that kind of thing require a paid account? I have a free account with them, but it seems quite limited.

Okay - quite useful, then.

Is the issue with external libraries the need to update them when you update Blender, or just because it’s easier with the Blender API?

Thanks! In my initial search, everything I was finding was way out of date! I have found more recent docs, though. That’s a start.

I’ll be looking into it.

As I mentioned, I wrote a plugin in the past, one to work for doing architectural plans. I wish I had time to go back and make it work the way I’d really like to make it work, but that would take a lot of time I don’t have. (My idea was, since plans would need different views, to add cameras for each view at the start and the plugin would add a plane for each camera, that would only show up when seen from that one camera. Then, when it was necessary to add notes and measurements, you select the view or camera and can easily write a note that you can place on the plane for that camera, so it’s easy to add plan notes and measurements without them getting in the way of your work.)

I think I remember creating a special panel just for that plugin. I’ll have to check out the code to see what I did and how that’ll help. I’ll also have to look up the redo panel. Since I’m self taught and have been focusing on using Blender for plans for work we do on our lot (and the plans have to be submitted to the county planning office), and for 3D printing, my background is limited and I know I’ve missed a lot!

I used a free chatGPT.

You have to ask it for a step by step and I fed it the three lines of code I needed to draw a curve.

Heres the final result of basically half a days work, but…

It was really solved by two other users on here as I put the question to a few coders and blenderheads last night too.

So you be the judge.

This is an addon that will give you a button, in the tools menu, when you click it it gives you an empty curve object to start drawing with

1 Like

They don’t integrates well with blender, it’s possible to use a QT interface but you’ll see that nearly all addons / plugin don’t do that.
Basically you’re heading into a lot of trouble if you want to do that so you should have a really good reason to do so…

At least it’s a quick and dirty way… If it’s speed that matters maybe do the simplest thing. If you’re building a tool that you’ll have to improve later, add functionalities then it’s worth planning a bit more. Or you can start simple and build a more robust addon later.

Also you shouldn’t overthink it too much, start experimenting by allocating one hour here and there, and it’s going to be fine :slight_smile:

2 Likes

@AlphaChannel

I’m looking over Serpens and watching the video. I see all the stuff about how it works with nodes and I’m seeing a lot of features, but I’m still not too clear on just what Serpens can and can’t do.

It looks to me like Serpens handles the UI work so I can focus on just my actual code, not on Bender UI stuff or interfaces. Is that right?

seems to be, Ive not used it at all, just seen some quite complex addons being made with it.

I know it generates quite messy code, being node based, but it has the advantage of a lower bar to entry than just python and learning all the complexities of Blenders API.

Hope that helps

My add-on I want to do now is seriously simple - just pick the size and scale to that point. Using just the scale function would seem to be simple, but the scale amount is a kind of reverse from the actual number I need. Without going into details, this is about clay shrinkage after you make something and run it through the kiln. So if a clay body will shrink 10% in that time, then I need to do a reverse of 10% for the mold I’m making, in Blender, for 3 D printing.

For example, if the clay will shrink by 10%, then the mold needs to be more than 110% of the original size. The formula would be something like this:

100 / (100 - 10) = 111.11

So if I’m printing a mold for clay that shrinks 10% of its pre-fired size, the mold has to be 111.11% of the final size it will be. It’s hard, as a human, to keep track of those actual numbers, so I’d select a shrink percentage, like 10%, and then the add-on would do the calculation and scale the mold up 111.11%.

So that’s the only calculation. I can see this being very few lines of Python - a few lines to get the shrinkage rate from the UI, then a line to calculate how much to scale up the mold, then maybe a line or two to actually scale the selected object or objects.

I think the UI would be the most complex part of this, since what it does is rather simple. I’m going over Serpens now - found the demo version (almost overlooked that), so I’m investigating it and hoping it leaves very little for me to do.

This should do it ? From the simple operator template and https://docs.blender.org/api/current/bpy.types.WindowManager.html#bpy.types.WindowManager.invoke_props_dialog

import bpy


class SimpleOperator(bpy.types.Operator):
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_options= {"REGISTER", "UNDO"}

    factor: bpy.props.IntProperty(default=10, soft_min=8, soft_max=16, max=99, min=0, name="Factor")

    @classmethod
    def poll(cls, context):
        return context.active_object        

    def invoke(self, context, event):
        context.window_manager.invoke_props_dialog()
        
    def execute(self, context):
        s = context.active_object.scale
        mul = 100 / (100 - self.factor)
        context.active_object.scale = (s[0] * mul, s[1] * mul, s[2] * mul)
        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(SimpleOperator.bl_idname, text=SimpleOperator.bl_label)


# Register and add to the "object" menu (required to also use F3 search "Simple Object Operator" for quick access).
def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.append(menu_func)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()

It will add an operator at the bottom of the View > Object menu. Run it and expand the panel in the lower left of the 3D screen to tweak the factor.

3 Likes

Thank you!

I’ll be making a few changes, but that gets me going!

I went in a different direction and used a DialogOperator. While I can’t make it work from the object context menu (I’ve asked about that under the Python tag), I was trying to reduce pointer movement. (I use a 4K screen and sometimes it’s a long way from an object I’m working with to the Blender menu.)

I’m now using this script as an add-on. It appends the item to the end of the Object View Menu (and I added it to my Quick Menu). Using the DialogOperator saves me mouse movement, since the dialog pops up under my pointer. Since I use this with my quick menu, it’s very little mouse movement to do the scaling work.

I have 3 items I would still like to work out:

  1. I used an unregister() function the way I’ve seen it done in examples, so it should work, but I get an error if I call it. (However, when I have it installed as an add-on, and uncheck the box so it’s removed, I don’t get an error.) I’ve posted about that in another thread (as yet unanswered).

  2. As mentioned, if I use the Object Context Menu (VIEW3D_MT_object_context_menu) and pick this from that menu, the dialog never opens up. I can’t find out why. I’ve asked about it here.

  3. I want to add another button to the DialogOperator and have it trigger a cancel function. I know you can cancel with , but I’d like a button on the dialog box because often I tend to think more with the pointer than hitting a key on the keyboard. I’ve tried to find out how to add a button to the box, but I see very little that is both recent and useful for this.

Okay, so here’s the updated script, with comments:

#=============================================================================================
#Shrinkage Scaler (Add-on for Blender)
#
#Used for designing clay molds. Clays always shrink when fired. (Side note: For most pottery
#work, you make the piece, fire it to bisque it, so it's hard and moisture is removed, then put
#the glaze on, then fire again. Most clays will shrink from 8-12%, some shrink more.)
#If a mold is needed, then the size of the mold can depend on the shrinkage rate for the clay.
#
#For example, if Standard Clay's 182 will shrink 11% at cone 8. So the mold for something that
#will be fired at cone 8 needs to be made so when the clay it's used on shrinks 11%, it'll be
#the size needed. Note this means the mold has to be more than 11% bigger than the final size.
#The size can be figured out by 100 / (100 - shrink rate), or, for this, 100 / (100 - 11), or
#100/89, which comes out to 112.359550561798% of the intended size.
#
#This adds a function to the object menu "Shrinkage Scaler", that, when picked, brings up a
#dialog with one INT object in it. It defaults to 10. Select the shrinkage rate from there.
#The amount to scale up, so it will shrink to the right size, is calculated, and the selected
#object is scaled up as needed.
#
#TODO:
# * Make work from object context menu
# * Add Cancel button
#
#=============================================================================================
import bpy

bl_info = {
	"name" : "Shrinkage Scaler",
	"blender" : (4, 0, 2),
	"category" : "Object"
	}


class ShrinkageScalerDialog(bpy.types.Operator):
	bl_idname = "object.dialog_operator"
	bl_label = "Shrinkage Scaler"

	shrink_rate: bpy.props.IntProperty(name="Clay Shrink Rate",
		description="Shrinkage rate for clay body",
		default=10, min=0, max=90, subtype='PERCENTAGE')

	def execute(self, context):
		expand = 100 / (100 - self.shrink_rate)
		message = "Using shrinkage rate of %s and expansion rate of %s" % (self.shrink_rate, expand)
		self.report({'INFO'}, message)
		sel_objs = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
		# bpy.ops.object.select_all(action='DESELECT')
		for obj in sel_objs:
			s = obj.scale
			obj.scale = (s[0] * expand, s[1] * expand, s[2] * expand)
		return {'FINISHED'}

	def invoke(self, context, event):
		wm = context.window_manager
		return wm.invoke_props_dialog(self)

# Only needed if you want to add into a dynamic menu.
def menu_func(self, context):
    self.layout.operator(ShrinkageScalerDialog.bl_idname, text=ShrinkageScalerDialog.bl_label)

def register():
    bpy.utils.register_class(ShrinkageScalerDialog)
    bpy.types.VIEW3D_MT_object.append(menu_func)
    # bpy.types.VIEW3D_MT_object_context_menu.append(menu_func)

def unregister():
    bpy.utils.unregister_class(ShrinkageScalerDialog)
    bpy.types.VIEW3D_MT_object.remove(menu_func)
    # bpy.types.VIEW3D_MT_object_context_menu.remove(menu_func)

if __name__ == "__main__":
    register()
    # unregister()

As @Andrej said in this thread, you need to set the ‘INVOKE_DEFAULT’ for the operator context…

So… this will work for context_menus too:

def menu_func(self, context):
    self.layout.operator_context = "INVOKE_DEFAULT"
    self.layout.operator(SimpleOperator.bl_idname, text=SimpleOperator.bl_label)

3 Likes

Ah - thanks! Got things twisted around in my head and somehow linked that with something else. Anyway, got it all mixed up. Thanks for the clarification!

1 Like