 Hello and welcome to Scripting for Artists, my name is Sibethan and in this episode we'll visit a topic that many of you have asked for user interfaces. In the previous videos we created our own operator and then turned it into an add-on. The basics of creating your own user interface in Blender is pretty much the same. You create a class, you add some properties to it, you add a function to it and then you register the thing. So in this video we will look at the following. We'll be finding existing UI codes. So if you're wondering how did they do it, after this you'll know where to find it. We'll look at the ingredients of a panel before making our own monkey grid panel of course. What else? We'll be adding operator buttons to the panel and then tweaking the layout a little bit. Then we'll add scene and object properties to it and we'll also look at conditional drawing because sometimes there is no active object so you won't be able to draw properties from the active object and finally we'll look at adding your own operators to existing menus in Blender. Before we make your own let's take a look at existing panels in Blender. In the 3D viewport press and to show the panels and then let's look at the 3D cursor panel. Right click and choose edit source. If you don't see the edit source option go to preferences interface and enable developer extras and python tooltips and then it should show up. So let's see what happens if we click it. Blender now tells us here in the corner but it's a bit far away because of my scaling. Basically it tells us to go to a text editor and there we should find the file with the source but there's an easier way. If you split this window and make sure that there is a text editor right here you can click edit source and there you have the file it will be opened for you and even scrolls to the right line. Now let's scroll through this and see what we can find. As we've seen with operators as a class it has the same kind of naming it has the category in capitals and then pt for panel type instead of operator type and then in lower case the rest of the identifier and here you have the parent class in this case it's panel and not operator. You would expect bpi.types.panel here instead of just panel and we'll see why this is in a bit. Let's look for other commonalities first. We have BL label which is the panel's name in the user interface and then we have a category a region type and a space type. The space type and region type define where in the user face it will show up. In this case we're looking at 3D viewport and region type is UI which means it's right in the 3D viewport and not in a header or a footer. Then the category is view which is the name of the tab here and then we get to be a label which is the name of the panel. Where operators had an execute function that performs the task of the operator here we have a draw function that draws the panel. The drawing itself is done through self.layout which is a UI layout instance and you can find this with help and then python API reference and search for UI layout one word. There you'll find the class definition with everything that is possible. In this video I will show you the basics though and that will get you quite far already. Now let's go back to the class definition because we saw that panel here and this is a little bit different than we've seen before. As I said you would have expected something like bpi.types.panel and not just panel and this is because in this file it's imported in a different way. Let's scroll to the top and here you can see from bpi.types import header menu and panel and because of this you can just type panel instead of bpi.types.panel and it would refer to the same thing. Personally I would say be very careful with imports like this. It can save you some typing and that may sound like a good thing but you will read code way more often than you write it. I would always optimize for code readability and understandability rather than saving a few seconds of keystrokes. The only time where I would use an import like this is where it is very clear what's going on. In this case it's blender's user interface definition and there is only one header, there's only one menu, there's only one panel and they always come from bpi.types. So in this case I would find it acceptable. In general though I would avoid this and always use the full name of the thing so that once you see it it's clear where it comes from. Now that is out of the way. Let's just jump in and make our own panel for the monkey grid. Let's take a look at our adults code where we left off last time. So here we have the monkeygrid.py with the monkeygrid operator class and the register and unregister functions. Now to this we can add our monkey grid panel. So just like before we create our class, we name it properly. I kept the category view 3d because it's a view 3d panel anyway. We have the pt I called it monkey grid and we extend bpi.types.panel. I just copied these values from the 3d cursor panel. I named the category monkeys so that we get our own little tab there and I labeled it grid. And then the only thing that you really need to have is a draw function. For it to show up as a panel it needs to have a draw function even if that function does nothing and because a function in python always have to have something. I put pass here which is python code for don't do anything. Every code block after a colon is indented and every indented block needs a little bit of text there in order to make sure that python understands that it's indented in the first place. So that's why they have the pass keyword. Now the only thing that's left is to tell blender that this class exists in the first place which means registering it. So we can just copy the register and unregister class function calls and put in our new class and that should be it. Let's go to blender see what happens. Now all we have to do is reload the script. So go to system reload scripts and you'll see monkeys pop up here. This route also gives you access to right click and then add to quick favorites. So if you do that then you can just press q and reload scripts it will give you a fast way to reload them. When we go to the monkeys tab we can see that our grid panel is here it doesn't contain anything yet of course we don't draw anything there but at least it shows up. So let's put a button there for the operator. Let's go back to the code and here in draw cell.layout let's just draw things and to draw an operator you type operator and then you give the id name of the operator you want to have there. Let's copy paste that in go to blender reload the script and there you can see we have a monkey grip button in the grid panel and it actually works. To customize this button there's a few possibilities and let's go take a look at them. First of all we can change the text that is shown on the button. So let's call it the default grid because pressing this button will just give us the default properties. Save the file go back to blender, reload the scripts and now it's called default grid. The nice thing is that if we search for the monkey grid then it's still called monkey grid in the f3 panel because that is the name of the operator. It is just that on this particular spot in the user interface we have a different text on the button. Finally we can add an icon to it and then we have to know the name of the icon so you can either look at existing code to cheat off of that or you can activate the icon viewer add-on. It's bundled with blender but not activated by default and then in a Python console you get this button called icon viewer. Then you can click on the icon that you want say a monkey. This is copied it onto the clipboard paste it there save go back to blender, reload the script and there you have the icon. In the tool tip you can also see the description and you can see that it will actually call our operator with all the default parameters. Let's tweak this and make some buttons that use specific values for these parameters rather than the default. So again we go back to the source let's copy this call and instead of just having this we can say props equals self.layout.operator, yadda yadda yadda. Let's change this to big grid self.layout.operator is a function that returns something and the thing that it returns represents the properties of our operator. So here we can say props dot and let's take a look we had count x count y and size so here we can say props dot count x equals 10 and count y equals 10 and size equals 0.8. So let's save go back to blender, reload the script we have a new button big grid and when you hover over it you can see that it has different values for those parameters delete the monkeys. If you click on big grid automatically it will take those values of course. Now to top this off we can also make a small grid and then we have a few more buttons to play around with. So for the small grid let's set count x is 1, count y is 1 and keep the size to the default so we just don't mention it here. Save, back to blender, reload and there we are of a small grid as well which is of course just one but in the redo panel we can extend this however we want. Now you can also see the value of being able to set the text on that button because it's all the same operator but with different behaviors. Now there's one more thing I don't like how these buttons are spaced so far apart from each other because they're basically doing the same thing they're creating a monkey grid and they should really be grouped together. Fortunately we can do this by laying in the mountain a column. Now I can hear you ask but Siebren they're already in a column so what are you on about? Well a function to create a column has some parameters that we can use to influence the layout so let's take a look at our code again. Right now each operator is drawn by calling self.layout.operator and we can change it. We can create a column first and then instead of calling operator on self.layout we call operator on call. Save the code, go back to blender, reload the script and you can see they're already squeezed together a little bit more and we can go a little bit further add align equals true here and then they are exactly as I wanted them to be nicely squeezed together. So beside the operators you can also draw properties on a panel and this can be pretty much any property that you want so you could use it to group together these properties that are strewn throughout blender's user interface but you use a lot so for your specific workflow you can add them to your own panel and make your life a little bit easier. So let's take two properties one a property of cycles so let's say the number of viewport samples in cycles and the other I want to have relative to the currently selected object so let's draw the viewport visibility there. So one of the properties is this guy here the number of viewport samples and the other property that we want to add is this one here disabling viewport. Before we go to the code let's see what blender already tells us so you hover over the viewport property there it says as the bottom of the tooltip bpi data scene scene dot cycles dot preview samples and this is almost what we want but if we were to use this we would hard code the name scene in our script so if we ever rename the scene then our script is broken already and also when you use multiple scenes it also wouldn't work anymore so rather than using this literally as it stands there even though for this case it might work well we use the context again and just get the current scene with context dot scene so the property that we want to show is actually context dot scene dot cycles dot preview samples now let's go to the code let's add another column and then we call call dot prop to render the property for us it takes minimum of two parameters the first one is the thing that contains the property and then it's the name of the property so the thing that contains the property is context dot scene dot cycles everything up to the last point and then the name is preview samples let's save go back to blender reload the script and there you have it preview samples 10 so we can change this and you can see that here it changes as well which indicates that we have the right property so here you can also add these text and icon parameters they work the same as with the operator call so in order to keep this video a bit to the point i'll leave that for you to toy with now let's take a look at the second property we wanted to add which is a property of the active object i want to be able to control the viewport visibility so that is this guy over here and there it says bpi data object susan dot hide viewport and again as with the scene we don't want to do this specifically for susan only we want to do this for the currently active object which means context dot active object so the property we're going for is context dot active object dot hide viewport let's just copy what we had change the thing that contains the property to context dot active object and change the property name to hide viewport and this should render that property let's take a look back at blender we reload and there is the property that we wanted to have now let's see if it works and there you go it's working but it's a bit weird because it disappears and here it doesn't so what's going on it turns out when you hide an object it's no longer active so that means that context dot active object becomes none instead of susan none doesn't have a property hide viewport so our code is actually causing problems now let's take a look at the terminal where it started blender and here you see a rather cryptic error message here at the bottom is the terminal where I started blender from and it's complaining about the monkey grid dot pie file line 82 which is of course the call that we just added and it says error with argument one data so apparently this argument is called data function dot data does not support a none assignment it's a bit vague description but it does match what I just said the active object is none and you cannot draw a property of nothing so we have to add a little bit of a guard around this code to make it always work we know that we know that context that active object can become none so let's check for it and in that case let's just add a label otherwise we'd rather property as we did before let's see how this works now reload the script and there you have it it says no active object instead of erroring out and all the code will keep working you could argue that this is a bit of a silly button because it only works once and you will be right but I wanted to have this example to show you how things can go wrong and how to resolve that it's also a demonstration of one thing that I find quite important and that is showing why something is not there so rather than not drawing the property at all and just silently skipping over it now we draw the text no active object which indicates to whoever is using your code why the option is gone I think that's quite important so that's why I wanted to have this example here anyway even though it's rather useless you now know how to draw your own properties so it's up to you to make it useful for your particular workflow before we end this video I want to show you one more thing which is adding operators to menus given what we've seen so far this is rather easy so this is why I kept it as the last step let's add our monkey grid operator to the mesh add menu so here we have add mesh and it would be nice if underneath monkey there was a monkey grid this means we have to add it to a menu and there's a trick for that I will show you but first we have to know which menu it is so you can hover here over add and it shows view 3d empty add and you'll probably already guessed empty stands for menu type now the thing is sub menus don't show this see you don't know what the mesh is but we can look around in the python console and see if we can find it anyway it is a type so it is part of b pi dot types dot view 3d empty now let's see if we can find something there you go mesh add so this is very likely to be our menu calling it here we'll just give an error so let's not do that but at least we have the name now view 3d empty mesh add in b pi dot types let's copy this and then go to the code as you would have guessed it we have to do something in the register and in the unregistered functions and this time is quite different than what we've seen so far first we have to make our own function that will draw whatever we want to add to the menu so that looks something like this a name that you can think of yourself and again self comma context for now let's put pass in there to make python happy and this function basically will be called after the draw function of that panel so where our own panel has this draw function with self and context this function will be called with the same self and the same context so self will refer to that menu in our case now let's register and unregister this function this is done by taking that b pi types view 3d yadda yadda yadda and saying dot append mesh add menu draw the opposite is remove so in the unregister we remove mesh add menu draw now what to put in here well that's the same as with a panel we can just call self dot layout dot operator copy the idea of the operator and this should work let's save and go back to blender reload the script add mesh and there we have a monkey grid it doesn't have an icon yet but now you know how to add one so i'll leave that for you now you've seen how to add your own panel how to add your own operators to existing menus but most importantly you've seen how to look at other code so when you see something that's interesting when you're wondering how did they do that you can just right click edit source and most of the user interface elements in blender are written in python and then this just works so this is it for this episode of scripting for artist if you have any questions or remarks please leave them in the comment below and i will see you soon