 Hello and welcome to Scripting for Artists, my name is Siebelen and in this episode I will show you how to create your own operator. So far we only created snippets of code, you can use them but they're not the most user friendly. Many people have asked for ways to create menus and buttons and custom panels and stuff. In this episode we will not look at that yet, but we will look at a necessary first step which is creating your first operator. First we will look at what an operator is exactly, then we will see how to create your own operator and how to make it more flexible by passing parameters to it and then we conclude by limiting the operator to a specific context. An operator is to glue between menus, buttons and hotkeys and the code that should be executed by them. In the first episode we already worked with operators by copy-pasting them from menus and buttons into our own script. Operators can be implemented in Blender itself in the C programming language or they can of course be written in Python and no matter how they're made they all have the same ingredients. First is an identifier. So this is what you would put after the bpi.ops. So in this operator example, it's far from complete but we're building it up. So in this example it would be bpi.ops.sfa.example. These are always in the form of some category.name. Usually it's best to pick a category that already exists like object or mesh or sculpt or whatever is suitable for your operator. But as you can see you can also create your own. Then we have a label. This is what appears on the menu items and buttons. And a description that is shown as a tooltip. Then everything is combined into a class and this is something that we will look at in a minute. For now just think of it as some pot in which we gather all the ingredients of our operator. You can see that the class is named after the operator. So we have the category in capitals, then ot for the operator type and then the rest of the identifier. So far we've seen descriptions but not actual code yet. This can be added in different ways for different purposes. So here are the possibilities from simple to complex. You have execute. This is where you put the main functionality of the code. If you want to create 600 monkeys then this is place to do it. The execute function is also what gets called from Python directly. So if you call an operator like bpi.ops.something, this is the function that gets called. Then we have invoke. This function is called when the operator is called for a menu or a button. If you want to do something extra in that case or you won't have slightly different behavior then you can do that in invoke and you can then call your execute function from there. And now we get to the most complex, the modal one. The modal function is for special operators that need to run for a longer period of time. This receives all kinds of events from Blender, from when you move your mouse you click on buttons, you use the keyword, that kind of stuff. Things like the Blender Cloud Texture Browser or CGCookers Retopoflow are implemented like this. If after all of this you're in doubt about which function to use, keep it simple just use the execute function. If that turns out not to be able to do what you want to do then you have a concrete reason to look at something more complex but before that just keep things simple. Now let's turn this into an actual operator that we can use. Let's create some more monkeys. Let's start by removing what we don't need. Let's keep it simple, just an execute function. If you look at the mesh operators, you can see that they start with BpiObsMeshPrimitiveMonkeyAd for example or your BpiObsMeshPrimitiveIcosphereAd. So all the operators that create in your mesh object they start with mesh. So let's keep doing that. So the ID name becomes mesh and we're going to create a monkey grid. So monkey grid, label monkey grid. The tool tip will be let's spread some joy and then the class name we have to change that becomes the category underscore OT underscore monkey grid. The execute function has to do something but it also has to tell Blender whether everything succeeded or not. So if everything was all right, your return finished and that tells Blender that the code actually performed well. But maybe your operator cannot work. Maybe it is for extruding a mesh and the mesh doesn't have any vertices. So in that case, your return canceled. There are a few other options and they are all described in the manual to go there, you go to help and then Python API reference and then to the section about Bpi.Obs. So let's assume that everything will work correctly. We will return finished and now all we have to do is put the code here that creates our monkey grid. So let's copy the code from chapter three of scripting for artists and put it here. So this creates our 600 monkeys and rows of 25 having one unit like Blender unit between them. The operator is almost done already. So we have all the ingredients here. The only thing that we need to do now is to tell Blender that this operator exists. And this is called registration. Fortunately, this is rather simple. We just call Bpi.yotels.registerclass and then the name of the class in our case Mesh OT monkey grid. For symmetry, there should also be a unregister function. So that looks like this instead of calling register class, it calls unregister class. So it basically undoes what the register function was doing. And then we're almost there. It's just that as you know, when you have a function like this, it's not automatically called when you run the script. And there's a little trick for this. If you add some magic like this in here, and it looks a bit weird, but basically what this means is that if this script is run, then it should call the register function. There's multiple ways in which Blender can access your script. It can execute it, but it can also be imported from something else. Just like we import Bpi, other code can import this file as well. So there's a distinction between being imported from somewhere else and being run. And only when it's being run should it call the register function. And that is what this code does. So now what we can do, let's just name this monkey grid.py for good measure. And then let's click play. And nothing happened, which is kind of a good thing. We didn't see any error message. Our code doesn't show any message. So the silence is kind of good here. So let's see what happens now, Bpi, Ops, Mesh, there we go. We can create a monkey grid. The operator is there. Let's spread some joy. Let's not call it here. Let's see if we can find our new operator here in the F3 menu. We tap monkey and there's your monkey grid. So that's really cool. Press enter to execute it. And as you can see, Blender now freezes while it's creating the 600 monkeys. And this is what happens when you do something that takes time in the execute function. And that is okay. The code is part of this blend file. So let's see what happens when we create a new blend file. And as you can see, the operator is still there. It's been loaded into memory of Blender. It's been registered and it just keeps taking around. Now let's see what happens if we restart Blender. And now the monkey grid operator is no longer there. And this is because Blender doesn't just execute all the scripts that it can find in a blend file. You have to tell it to do that. First you have to make sure that the name of the text file ends in .py. And then here in the text menu, you can check the registered checkbox. Now let's save, restart Blender and now it's there. So let's have some fun and add some parameters. When you add a cube, what you see here on the left is called the redo panel. When you open it, you'll see different parameters that you can set. So what happens is that when you change a parameter's value, Blender pushes the undo button automatically and then reexecutes the operator. And that is why it's called the redo panel because it's redoing things all the time. So let's add support for the redo panel to our operator. We start by declaring the properties that we want to have. And this will be the number of monkeys in the x direction, the number of monkeys in the y direction and monkey size. Just like operators live in Bpy.ops, the properties live in Bpy.props. So again, if you want to have more information, just go to help, Python API reference and then click on the Bpy.props section. I will show you what it looks like. Let's call it count x, Bpy.props in property. In stands for integer, which means a whole number because we cannot have half monkeys. So count x has to be either one or two or three, but it can never be like 2.5 or something. So it's whole numbers only. And this declares that we want to have that property. There's a big difference between the column that we have here and the equal sign that we have there. This sets a name to a value. And this is for things that just have to be set. We have to set BLIT name to mesh monkey grid to make it work. But here, we don't really set anything. We just declare to blender, this is an int property, please act accordingly. You can add some parameters here to change the behavior. First of all, we should set a name. We can call it exit, maybe a bit cryptic, but for now we'll do. And we can add a description, number of monkeys in the x direction. So let's copy this to the y, name is y and number of monkeys in the y direction. And we have a size, which is another Bpy props dot float property. Float stands for floating point. It's a technical term for just decimal numbers, name is size, description is size of age monkey. So this gives us our properties, but they're not used yet. So let's take a look at the execute function where we're supposed to use them. This comment, it has to go because we're no longer creating 600 monkeys in rows of 25, but it's all flexible. So let's remove that for now. Now we have to access the properties. So we've declared them, and this self gives us access to the actual value. Self is a Python thing, and I won't go into detail here about what it represents. Just know that if you declare a property here as count x, then you can access it here as self dot count x. So the total number of monkeys is the number of monkeys in the x and in the y direction. But this 25 here was the number of monkeys in the x direction. And then all that is left is to set a size. And that's it. Let's give it a try. Let's see what happens in the console first. And there you can see our properties, count x, count y, size, and they're all equal to zero because we haven't set any default yet. And there we have our three monkeys. Before we go to actually call it from the viewport, let's set some sensible defaults. Default is three. For the y default is two. I choose different values there on purpose. This is a default. So when somebody clicks on the monkey grub button or selects it from the F3 list, this will be what they see. Because they see a three by two grid, they immediately know what they need to change. If they want to change the three to a four, they just change the three to a four without having to think about whether it's the x or the y direction. So if we were to give them the same default, then they wouldn't be able to see it. And then they would have to think like which direction is x, which direction is y, which value do I want to change? And this is why I give them distinct values. Finally, default for the size, run again, delete everything. And there you can see your defaults. And when we execute without any values, it just uses those defaults. And the same for the F3 menu. There's one more step left, and that is to get that redo panel. We have the properties. So Blender knows what should hypothetically go in there. It just doesn't know yet that we support that redo panel. So let's tell Blender that we do. This is done through Beal options. The default value is register, and we have to add undo to it. This will give us the final ingredient. There you go. Now we can make them big and small and give them even invalid values. So to make it pleasant to use for people, we should really set limits to the slider so that it's all sensible values that you can select there. Fortunately, this is again quite simple. We can set a minimum. Let's set it to one because creating zero monkeys doesn't make sense, and creating negative monkeys even less. So let's start at one. Let's set the maximum to 10 for now. We can do the same for the count Y, and we can set the size zero to minimum, and the maximum, let's set it to one. And now we can drag and then things are still nice and sane. You can see that it already gets a little bit slower when these numbers increase, and we still can't get to the 600 we had before. Even when I type 100 here, it is cut off to the maximum. So this is why Blender has a soft limit. The hard limit is what we set now. That's the absolute limit of the value of the property, whereas the soft limit is what the slider will do when you drag around. So if you want to allow maximum values that are bigger than 10, or sizes that are bigger than one, but still want these limits as the limit for the slider, you can change the maximum into a soft maximum. The minimum that we have now really is the minimum because creating zero monkeys doesn't make sense, negative monkeys is impossible. Also creating a negatively sized monkey is impossible. So let's change the maximum to soft. Now the dragging is still the same, but we can set it to create much more monkeys than you would get with dragging. Now let's get back to that class declaration. I will explain a little bit more about how classes work in an intuitive way, I hope, without going too much into technical details. Classes use a system called inheritance. Just like with people, it's like a parent-child structure. So in this case, bpi.types.operator is the parent, and mesh.otmonkinggrid acts as the child. If you ask a child a question that he knows the answer to, say, you want to have a cookie, the child will say yes immediately, without asking the parent, of course. But if you ask about taxes, then please help me out here because I don't know anything. So then the child defers that question to the parent. And this is the same way that classes work in Python, roughly. We could do without the be-all options. Our child didn't know anything about it, so Python asked the parent. And when we want to have something different, we can add be-all options to our child, to our class. And then that is asked first, so that takes precedence over whatever the parent wants. So we always have lucky children who gets all the cookies they want. So before I end this video, I have two more things that I wanted to tell you. One, again, we look at the execute function. You see that it gets a context parameter, and this is pretty much like the context we've seen before. It gives us context.object, context.scene, context.selectedobjects, all these things you've seen before in bpi.context, it gets here as well, but then localize specifically for this operator. So as a general rule of thumb, if you have a function that gets a context, use that context. And don't use bpi.context. So if you want to do something with all the selected post bones in this execute function, you would use context.selectedpostbones. Another function, and that is the second thing that I wanted to show you. Another function that gets a context is the poll function. And it's written in a bit weird way, but just take it from me, it works like that. Again, you get a context in this function. But instead of doing something, like instead of performing an action, this function is meant to check whether that action will be possible. So this poll function is called by Blender every time this operator is supposed to show up in a menu or as a button on a panel. I can show you how this works. But now it returns true, which means that it is always allowed just as it was before. And just as it was before, it shows up here in the F3 menu. When I change this to false, it will never show up anywhere, which is kind of useless. Let's tweak this to make it a bit more useful. You can see here in the console, we have autocomplete, which makes sense in a Python console. But that same operator does not work in the 3D viewport because the context is wrong. This is also something we can do ourselves right now when I change this to true again. The monkey grid operator also shows up in the context of the Python console. And it will show up anywhere in Blender, which is not exactly the right way to go. Let's take another look at a variable inside that context. Area gives you the area that is currently active in Blender's user interface. So in this case, it will be the console. But if the mouse is here and we pull up the F3 menu, then all of a sudden it's the 3D viewport that is active. And here it will be the outliner. So every area in Blender has its own area type, and we can use that in our pull function to make sure that it's only working in the area that we wanted to work in. One thing I don't really like is guesswork. So we know now that the area type of the console is console. But is it in 3D viewport? Is it 3D view? 3D underscore view? Is it 3D viewport? I don't know. So let's make our script tell us instead. I can just say print. My area is context.area.type. And let's say what happens on the terminal where all these kinds of prints are sent to. Here you see the console. And immediately you see it being called my area is few 3D. But when I do it here in the outliner, my area is outliner. If I do it here in the properties panel, my area has properties. So that way you can just use these print statements to know what you have to put where. So now we know which value to use for the 3D viewport, so let's just do that. If context.area.type equals view 3D, then return true. Otherwise we can return false. Let's see how this works. Here it still shows up. But here is no longer there. And of course here and there. Now it only shows up where we want it to show up. One final thing. These kind of constructs, I see them a lot. Also from experienced programmers. It's more complex than necessary actually. Because this guy is already an expression that is true or false. So basically what the code is saying is that if the value of this thing, this selected expression, if the value of that thing is true, return true. And if the value of that thing is false, return false. So that basically means return the value of this thing. Which you can of course write as this. So that was it for this episode of Scripting for Artists. If you have any questions or remarks, please leave a comment down below. And I will see you soon.