Introduction to Python Programming for Maya Animators | Anim TD | Skillshare

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Introduction to Python Programming for Maya Animators

teacher avatar Anim TD, Technical Education for VFX & Animation

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Introduction

      0:44

    • 2.

      Script Editor Overview

      7:47

    • 3.

      Your First Python Program: Hello Maya

      11:28

    • 4.

      Intro to cmds library and imports

      6:56

    • 5.

      Project 1 - Prop Rigging Tool: Intro

      1:02

    • 6.

      Tool Breakdown

      3:47

    • 7.

      Creating the Tool

      7:25

    • 8.

      Modifying the Control

      11:38

    • 9.

      Deep Dive - Help & Documentation

      9:03

    • 10.

      Working With Node Names and Lists

      9:54

    • 11.

      Extending the Tool with "if" Statements

      18:49

    • 12.

      Deep Dive - "if" Statements and booleans

      8:46

    • 13.

      Introduction to Functions

      29:00

    • 14.

      Turn Code Into a Function

      14:23

    • 15.

      PyCharm - Install and Setup

      10:15

    • 16.

      Creating a Module

      10:11

    • 17.

      Deep Dive - Functions

      31:51

    • 18.

      Project 2 - Pose Transfer Tool: Intro and Breakdown

      11:17

    • 19.

      Namespaces and Lists

      6:12

    • 20.

      Selection and "for" loops

      10:12

    • 21.

      "for" Loops Overview

      3:09

    • 22.

      Getting Attributes

      20:03

    • 23.

      Storing the Pose - Intro to Dictionaries

      10:19

    • 24.

      Storing the Pose - Building the Pose Dictionary

      23:07

    • 25.

      Applying the Pose

      33:14

    • 26.

      Building the Main "Transfer" Function

      22:50

    • 27.

      Intro to UIs

      21:06

    • 28.

      Creating the UI

      11:59

    • 29.

      Project Recap

      1:40

    • 30.

      Deep Dive - Strings

      23:43

    • 31.

      Deep Dive - Loops

      15:05

    • 32.

      Deep Dive - Lists, Tuples, Sets

      32:23

    • 33.

      Deep Dive - Dictionaries

      23:33

    • 34.

      Final Project - Pose Library: Intro

      1:54

    • 35.

      Creating the Python Package

      5:16

    • 36.

      Working With Files

      10:51

    • 37.

      Saving Poses to JSON Files

      9:38

    • 38.

      Reading Poses From JSON Files

      13:57

    • 39.

      Creating the UI

      30:07

    • 40.

      Connecting the Functionality and Sharing Your Tools

      45:00

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

154

Students

3

Projects

About This Class

Welcome to this Introduction to Python Programming for Maya Animators!

Learn how to create your own tools, automate tedious and boring parts of your workflow and help yourself and your team work a lot more efficiently.

As the title implies, the course will be more relatable to animators, as well as layout and rigging artists, mainly due to the nature of the projects, however anyone working in Maya could benefit from the lectures if they are looking to learn Python programming.

Whether you're just looking to improve your daily work or considering switching to a technical role, I'm sure this course will bring you a step closer to your goal!

What is covered?

  • Foundational programming concepts in Python: Loops, if statements, functions, lists, dictionaries, etc.
  • Fundamental techniques and best practices: Even if it's a beginners course you will learn some best practices that professional TDs and developers use.
  • Maya cmds library: The skills you'll acquire are broad programming skills, but the projects use the cmds library a lot since the tools being created are for Maya.
  • Production tools: You will finish the course with actual tools that you can use in your work.
  • UIs: You will learn how to create UIs using the built-in libraries in Maya.

What is NOT covered?

  • Python classes: Classes are an essential part of Python, and they might be included in the future, however you can create some very powerful tools and scripts without them which is why they are not taught in this course. With that said, once you learn the concepts covered in this course you will be able to understand classes easily.
  • PyQt: Qt requires a good understanding of classes, and it's a more advanced UI framework that might even be worth being in a separate course. For this introductory course the focus is on the UI features of the cmds library which are still very useful and easier to master for someone who just started learning.

Who is this course for?

  • Maya artists: This course will NOT teach you how to use Maya, you might learn some new Maya tricks, but it is assumed that you have some basic knowledge of Maya (although you do not need to be advanced at all).
  • Complete beginners or people with some basic knowledge of Python.
  • Programmers looking to become familiar with Python for Maya. The contents are geared towards beginners, but the projects might help someone become familiar with Maya and its libraries.

Who is this course NOT for?

  • Intermediate to advanced programmers: If you already have a good understanding of concepts such as functions, basic data structures, mutability, for loops, etc. you might benefit from a more advanced course.

Best way to approach the course:

  • Follow the projects in order: The projects are designed to introduce new concepts naturally as they are needed, from the most basic to more complex, and each project will reuse previous knowledge.
  • Deep Dives: The deep dives give you a break from the project to delve deeper into a specific topic. They are placed after a concept has been introduced, you might want to watch them immediately after learning the concept but you can also treat them as a reference library to go back to in the future.
  • Source Code: Keep a look out for the resources in each lecture, you will have be able to download the files with the source code for a portion of the project.
  • Video Timestamps: Unfortunately this platform doesn't offer timestamps on videos but you will be able to download text files (in resources) with time codes for important points in the lecture.
  • Quizzes and Exercises: You will find quizzes and coding exercises after some lectures, these are optional, but a great way to practice.

FAQs

  • Can I become a TD with this course?
    • This is a beginner course which might not be enough to get a job as a professional TD, but it will definitely give you a strong foundation to continue learning what you need.
  • Will this help my career?
    • It could, but it depends on your goals, it will definitely make you an asset as someone who has both artistic and technical skills. If you are leaning towards a technical career this will give you a good foundation to continue on that path, which can lead to a very rewarding, stable and well paid career.
  • Can I include this course on my LinkedIn?
    • This is not a certification (yet), however it could definitely benefit you to mention that you complete this Python course.
  • What if I'm a student and don't know animation or Maya very well?
    • That's ok, you don't need to be a professional not an advanced user. Knowing the very basics such as navigating the viewport and creating a cube will allow you to get started, there might be things you're not familiar with such as rig references, but you can easily learn them from a free tutorial online.
  • Can I use an older Maya version?
    • You can, most of the Maya commands covered will work, however there's not guarantee that all of them will. If that is the case you can use the Q&A section (or a quick google search) to get help.
  • Is this just for animators?
    • Not necessarily. The projects are animation related, which will be a lot more familiar with someone working with animation, layout or rigging than to someone focusing on lighting for example, but the actual programming concepts covered are applicable to all departments.

Meet Your Teacher

Teacher Profile Image

Anim TD

Technical Education for VFX & Animation

Teacher

Hi! I'm Fernando, a technical director with a passion for helping artists work better and more efficiently.

 

As a TD I've worked on films and tv shows such as Once Upon a Time, Arrow, The Lego Movie 2, Peter Rabbit 2, DC League of Super-Pets and more.

 

Before becoming a TD I was a 3D animator, I loved animation but I also fell in love with the power of programming which launched me into the long and tedious process of figuring out how to create things with Python as an artist. That, along with the many times I was asked by other animators "how do I learn coding?", is why I decided to create this course.

 

I've been through that challenging learning journey, now I'm here to help you with it!

<... See full profile

Level: Beginner

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction: Welcome to this introduction to Python programming. My name is Fernando Ortega. I'm a Technical Director in the animation and visual effects industry, as well as a former 3D animator. In this course, I'm going to teach you the basics of coding in Python by creating a series of fun. You mentioned tools inside of Maya. But even though the projects we're going to create our focused on animation in Maya, the skills you will learn, our programming skills that are highly applicable to other departments, other software, and even other programming languages. By the end of this course, you will have the skills and the ability to create your own tools and make your own workflow a lot more efficient. Thank you for joining us. Let's get started. 2. Script Editor Overview: Let's get familiar with the script editor inside of Maya. First, how do we find it? By the way, this is my add 2020. But what I'm going to show you is virtually the same in all of my aversions. To open the script editor, we can do it through this button in the bottom right corner, or from the Windows menu. General editors and scrape it or what is this descriptive error is Maya's tool for writing, running. Or I'm showing the output of code that you write or code or commands that are being run when you're using Maya. So there's two main parts to it. The first one is the Script window, which is this one down here where my cursor is. This is where you write code number up here. This is the output window that will show the result of that code. So let's just write something. Let's do a little bit of math. Two plus two, something very simple. Now this is a little bit small. We can press the Control key and the wheel and the mass to make it a little bit bigger, That's the same appear in the output window I will show you in a second. So to run your code, let's say we want to do this bit of math here. There's a few options, few ways to run the code. One of them is through this Play button. It's called execute. Now you see, once again, I'm going to make this a little bit bigger with the Control key and the mouse and the wheel on the mouse. You will see that Maya always shows you the code that you run. And then the result of that code. This might be a little bit confusing at first, but you'll get used to it very quickly. So you notice that your code disappeared down here. If that happens to you, don't worry, just press Control Z or Control Z to get it back. Now, the easiest way to avoid that is just to highlight your code. I usually use the hotkeys Control a to select everything and then click that Execute button. To execute without clicking that button. You can use the Enter key on the numpad or click Control and Enter that combination of keys will run that code. Another way to run the code without having any disappear is through these Double Play button. It looks like to play icons. It will do the same, but it will, it will not remove your code. With that said, since you're usually with your hands on the keyboard, I find it easy to just hit Control a Control Enter to run the code. Now, since we're highlighting, let me write another line of code here. Just another piece of math. When Maya, you can either run everything, as you can see. Once again, it gave me the decoder and running up here. And then the result for each line code. But you can also just highlight a single line, e.g. here, Control Enter to run that line. And it's only running executing that line. So the output window, as you're seeing, it shows the code that you're running, but it also shows what's happening inside of Maya. So e.g. if I were to create a cube and then select the cube e.g. you'll see that everything I'm doing is being outputted, are being displayed in this output window. Which is great. It's one way to know to find out the commands that Maya needs to create a cube, e.g. so at some point, this output window will get a little bit cluttered. As you can see. Clear that with this button here, clear history. And before we move on, I want to show you that. As I said, What do you do inside of Maya is being displayed here, but not everything is being shown. E.g. if I change my shading mode through this menu here, you see that it changes in the screen. Me do that again. Changes in the viewport, sorry. But it doesn't really tell you what happened. There is a way to show more information from Maya. It's called Echo all commands. It's this button here. And what that will do is show even more things which can become a little bit verbose. As you can see, everything I'm doing is being outputted here. It can be a little bit verbose, as I said, but sometimes it's useful to know what's happening inside the hood. So I'm going to turn it off for now. Clear my output window again with this button. Down here, you probably noticed these tabs, these are MEL tab. Once again, we're focusing on Python. So we're going to stick to Python tab. You can create new tabs with this plus button here. Let's create another Python tab. Let's just sprayed a function. If you don't know what a function is, don't worry, we'll get to that very soon. So let's make this a little bit bigger again. You can see that my gifts you colors for different things. E.g. this is yellow, this is a string, this is a comment. This is called syntax highlighting. This is my S way to help you read your code a little bit better, which is very nice. So we have this tab. This is a nice way to organize your code. And you can name those steps from the command menu. You can rename this tab. Let's call this tab number two, e.g. let's call this one tab number one. So now you can recognize easily what each tab has. And you can also delete the type that the tabs through here. And what's really nice about this is that when you close Maya and open it again, it will remember the tabs and the code that you had in it. So you don't lose any of that. Sometimes you do, but usually you don't? Usually my yes. Very good at keeping that code for you. The last thing I want to show you is how to how to put code in a shelf. So e.g. I. Know that to create a cube, I had to use this command. We will get familiar with these commands very soon. This is just for, we're at the example. So I know that, um, that if I run this command once again, I'm hitting Control a and Enter or Control Enter to run the code. I'm creating these cubes. That's great. So what if I wanted to put this in a shelf? You can highlight your code, click on it, and then simply drag it onto the shelf. And Maya will give you this option to create a button. We will click on the Python option. When we click on the button, it will create the cube for us. That's great. So if when we go to edit that button, you see that your code is here and it's in the Python mode. And also don't forget to save your shelves every time you make a change in the shelf. Um, and that's that's generally, there's a lot more options. As you can see, there's many more buttons, there's many more menus in here. There's a lot of things you can do any scripted or what I showed you will be what you will need. 95, 99% of the time. Whether you're doing some simple scripting or more complex, let's say development. These are the main options you will be needing. So let's go ahead and write your first piece of code. 3. Your First Python Program: Hello Maya: Okay, Let's create your first Python program. So one of the first things we can do, one of the simplest things we can do is display something otherwise known as sprinting. So type with me, print open parentheses, open a quotation mark, and let's write our message. Hello world. Now closing quotation mark and close the parentheses. Now let's run this code. Once again, I can, I'm going to press Control a to highlight everything. And I'm going to click the Execute button. And as I mentioned before, the Maya will print or output the code that you're running. And then the actual results or output of that code. And by the way, if the font is too small, remember you can click the Control key and the wheel on the mouse to make it smaller or bigger. So let's break this down a little bit. First, we have the print statements. Now. You will hear me say statement and functions. They mean different things. But for now let's think of a print as a function. This is a special word in Python that will simply display something in the console or the output window up here. Then the parenthesis, we use a parenthesis to execute something, to call functions to action or execute functions. So always remember to open and close parenthesis. And then in the middle here in yellow. And remember that Maya will highlight things for you to make it easier for you to say, to know at a glance what's in your code. This is a string. A string is basically a piece of text under the hood. It's a little bit more complicated than just a piece of text. But you can think of it as just a string of characters. It could be a sequence, sorry, a sentence. It could be a ward or a symbol, or a single character, single digit. In this case. A string will always be made or created by using quotation marks. You can either use a single quotation mark or double quotation mark. It doesn't matter as long as you use the same type at the beginning and at the end. There's another special type of string that uses three quotation marks. But we will reserve that for documentation and we will talk about that later on. So if you have done this before, you might have seen printing done without the parenthesis. So let's try that out. I'm going to click Control a. And now this time I'm going to click Control Enter to execute my code. So it is the same result. So what's happening here? What's happening is that print inversions to Maya 2020, which is what we're using here. Maya uses a version of Python that is 2.7. And in that version of Python, print is a special type of function that is called the statements that you can use with or without the parenthesis, in this case. Now starting in Maya 2022, which is the next version, because Autodesk decided to skip Tony, tony one, starting with my attorney, 22 Python. The Python version will be Python three. And in that case, printing will only be able to be executed with the parenthesis. So that is why you will see me using parenthesis. Our convention. I think it's a good habit. But just know that for most current version of Maya, you'll have the option to do it with or without it. As long as you leave a space in-between the print statement and the string. Now, let me just clarify. The difference between statements and functions is a little bit technical and you don't really need to worry about it. At this point. You don't have to care about it, so don't think too much about that distinction. So let's make this a little bit better. Let's store this inside of a variable. So let's make some space here. Now, variables, I'm going to call this variable my message. Variables are containers of value and a variable name is up to you. You can name your variables whatever you want to assign value to them is through the equals sign, single equal sign. And then I'm going to assign this string that we have here. So HelloWorld in a variable called by message. And now instead of printing that string directly, I am going to print my variable controlled way to highlight everything, Control Enter to execute. And there you go. So what's happening here? Once again, as I said, a variable is a container of value, which means you can assign any value you want as long as it's valid for Python, of course, to any variable that you want. So e.g. the value ten, the number ten, that works, you can assign the result of something. So let's do a little bit of math again. Three plus three. Six, There you go. Now, why am I using an underscore? This is simply a convention that I like to use is called Snake casing. When you separate words by using underscores. Some people like to use camel casing, which means that they use a capital letter to separate words, which one to use. It doesn't matter. It's up to you or up to the team that you're working with. I tend to use this and I just stick to that. I would say there's only two rules about variables. One of them is that they should make sense. What do I mean by that? I mean that you could name your variable x. And Python doesn't care. It will still work just the same. But when you come back to your code, let's say six months from now, x will not really make a lot of sense to you. But I will name variable like my message e.g. we'll make a little bit more sense or something if you just call it MSG, even that is a better name than than x or simply message. And the other rule is that you keep your naming consistent. E.g. if you have this, sorry, for the typo, you are mixing conventions. You are using underscore here on, you're using capital letters here. This is not a problem for Python, but it's a good practice for the sake of keeping your code clean and readable to stick to the same conventions that you have established. But we'll talk about that a little bit later. So this is great, but it's not very exciting. All we're doing is printing. Even though printing is very powerful and you will be using it a lot when you're coding. But so now, now that we have my responding to our commands in a way, Let's take advantage of that. Let's use my a little bit more directly. So I'm going to type from Maya Important CMBS. We will talk about this a little bit more in more detail in the next few lessons. But know that this is the CMBS library, otherwise known as a Commands library or the Maya commands library. And what this does is gives us access to a lot of commands that Maya will understand. One of them is called in view message. Now notice that my other screwed dated or just highlighted these in blue. And there's a beautiful and metallics rendering in the font. That is because Maya recognizes it as a command that is available in the command library. So this, this command is also known as a function, and I will be calling it function because that is the proper term in, in Python. So this is a function. And notice that we're using this dot notation. We will see this in more detail like I already mentioned. But the simple explanation is that we're accessing this function from this library that we're importing up here. So the Commands library has this function and we access functions from that through the dot notation. Now, we use the parentheses to call that function. And functions can and usually do, have a special options that we can use to modify the output of the function. And those options are called arguments. In this case, this function has an argument called assist message. Now, I know I said you should keep things consistent when it comes to naming your variables. And you are saying that this has camel casing or the capital letters. This is because Maya decided that is Mayas convention. You can stick to that if you want or create your own. But when it comes to using Maya's functions, you have to use what my air developers have decided to use. So this is meso assist message will accept a string. In this case, I would like you to use the variable that I have already created. And let me bring this back to saying Hello world. Then there's another argument called paid. I will say that is true and we will see in a second why I'm doing that. And there's another argument called position that takes another string. And I'm going to say I want this to be in the top center. Spring this a little bit more to the center and make it a little bit bigger. So let's run this code and see what happens. You see these little message here and it's going to fade in a little bit because we have that option fade equals true. Now, as you saw, we're using these arguments to modify the behavior of the function. If I want it to appear in the right side, e.g. I. Just say top-right. If I want to modify the message, I simply modify my variable. And that variable is being used here. And now we have the different message. You have seen this before. Well, I don't know if you have there's very likely you have seen it. E.g. when you toggle the shading mode in this menu, you will see this message under the hood, my eyes using exactly the same command that we're using. With some different arguments. Of course. We will talk about functions in detail very soon. We will learn how we can find out the arguments that we have available for each of the functions that exist in the same ds library. For now I know this from memory just for the sake of example. But we will show you how to find that in their recommendation. But now you're ready to start your first project in Maya. So let's go ahead and do that. 4. Intro to cmds library and imports: Before we jump into the first project, we need to talk about the Maya CMBS library. So what is a library? A library is a collection of code, a collection of functions. Perfect example we have already used it. Is this in view message. Let's just refresh your memory about how it works. There you go, you get a little message in the viewport. So a library, in this case the MIS him, the library, otherwise known as the malloc command library, has a collection of functions. And functions. You can think of them as, as black boxes in that you don't know how they work under the hood, but you know how you can get a result from it. So in this case, we know that the imbue message can be accessed via the dot notation from the scene, this library. And we can modify the output with these arguments. We don't know how it displays a message under the hood. But quite honestly, we don't care. Are we care about is the result. So that is a library, basically a collection of code. So now to get access to a library, first of all, you need to make sure that Python, maya have access to the code that is inside of the library. Because libraries at the end of the day are basically just text files with Python code inside of them. The second thing you need to do is important, which is what we're doing here. So what I was doing, what I'm doing here is from Maya, this happens to be another library. So I am saying from the Meyer library, import the CMBS library. And then down here I'm saying from the C&DH library, access the interview message function. So you will see this import done differently. Something like import Maya, the obscene dot ds as CMBS. This will give you exactly the same result as you can see here. Now, you don't even need to input C and D as you can access libraries from other libraries through the dot notation. We could simply say important maya, like so. Now we have to say from the Meyer library, access the C&DH library through that dot notation. And from this in this library, accessing your message function, this is a lot of typing. So that's why we prefer to do this from my input CMBS. Now, you will see this done in a different way as well. Something like this. This is very common if you see scripts online, you will see these, this way of importing what's happening here. This is called namespaces. So we're still importing from the Meyer Library. We're importing this heme, this library, but we're calling it MC. That's why then these as MC. Now the AMC is kind of like a variable. You're basically storing the CMBS library into variable of your choosing. You could call these commands. It will work just the same. You can see up here the result. Many people choose to do MC because they think it's faster to type and more convenient. Whether or not that's the case for you. That is up to you. You can think of name spacing as just like when you namespace a character, when you import a rig in the scene, you're importing your referencing the rakefile. You're giving it a name space so that it is to work within the scene. Now, in this case, for me, cmd is simple enough to type, so I tend to stick to that. But it's up to you whether or not you want to use the name spacing. And I do use namespace and often, but it's usually when the library that I'm importing is too long to type than I do go for something more efficient or more convenient. So there you have it. That is my SEM, this library, just to drive the point home. Let's access another function that I know from memory, poly cube. It will simply create a cube. So once again, we're accessing the poly cube function from this library through this dot notation. And what happens just as an example, what happens if I run this? Remember you can run single lines of code by highlighting the code and clicking Control Enter. If I run this, Python is simply telling me this is a method otherwise known as a function. But nothing is happening, like I said before, to action or to execute a function. You have to call it through the parenthesis. And there you have it. This will create a cube for you. We just changed my shading mode. Perfect. So let's, let me give you before we move on one more example of a library. There is a built-in library in Python. This is not from, my advice is from Python itself. Will get pass. And get pass has a function that's very useful called getUser. What this will do, and let's actually let me assign these to my variable here. So in my message variable, instead of saying hello Maya, I want to get the result from these, from calling this function. What this will do is give me the username that is registering my computer. Let me clear my output window. I'm going to get rid of this poly cube function. Now let's run this. Once again, remembering that I am storing the output of getUser. Actually, let's make it more obvious first, let's print the output of getUser. First of all, I'm going to input that get passed library. And then I'm going to print the output of the getUser function from that library. There you have it. So this is the username inside of my machine. If you run these lines of code, it will be different for you, of course, because your computer has a different username. So now let's run this code that is assigning the result of getUser, this variable here. And there you go. Now they imbue message. Function is displaying my username in the viewport. There you go. So now we're actually ready to start on the first project. 5. Project 1 - Prop Rigging Tool: Intro: You are ready to create your first Python tool. We're going to start with something simple. This will be appropriate tool. Let me show you what I mean. This is the result. I just put it here in the shelf as an example. So it will give you a simple piece of geometry. In this case it's a cube, but it can be something else. It will give you a controller. So you can do animate your prop and imagine that you are blocking out a shot and you have, you need something for your character to interact with. Or imagine that you're creating a piece of previous and you need to set trace your shot in order to work out the timing of the sequence, or to work out the framing of the camera. There's many uses for these type of tool. So it's very useful. But most importantly, it will show you the principles of Python that you need to create your own tools. It will show you the basics of interacting with my app. And it will help you discover how very simple actions like creating a cube and a circle, can quickly add up to creating very powerful tools. 6. Tool Breakdown: So a breakdown of what's happening here. If we look here on the left, in the outliner, we have our curve for a circle called control. And under that, we have a piece of geometry which is a cubed. It's called prop. Prop is parenteral under control so that it can be animated. This is a very simple setup, and in fact, we can do it manually. And in fact, that is usually one of the best ways to start developing a tool when you don't know where to start. Let's, let me show you what I mean. Let's open the script editor. And you will remember that in the first couple of lessons we showed you that the output window not only shows you the output of your code, but also shows you what my eyes doing. So let's see that in action. Let me clear the output window. Let me delete this code that we don't need it anymore. Let's start by creating a circle. Now, let's start our training the cube. And then I want to select the cube and the circle. And I'm going to click the P key to parents. Now we get a very similar results. Of course it's not the same. We don't have the names and we want the circle is slightly smaller than done what the tool is giving us. But it's a good start and it got us most of the way by doing these steps manually. Now, if you notice here in the output window, Maya has been given us a lot of information. Now let's do this again and watch the output window. As I create my objects. Start with the circle, cube. Select, shift, select the circle, and parent. Great. So you see Maya has been given all the commands that we need to create this. Now this is Malcolm, these are MEL commands. This is Mel code. As I mentioned before, we're not going to be using MEL. But knowing how to read a little bit of mail will be very useful because you can translate that Python. So let's, let's try doing that. These code that Maya gives you is not always, not always clean. You see there's a lot of options here that we likely we don't need. And also you will notice it's creating two circles. Like if I run this code as it is, it created the setup that we want. As you can see here, there's the coupon printed on the record, but it also created extra objects. Why does my Aggie was repeated commands? Honestly, I don't know. But we know that we simply have to ignore the first one. And once again, this is just a guideline. This will not be your resulting piece of code, although it couldn't be. If you need to do something as simple as this, maybe this is all you need to do. But for our purposes, this is not enough. The main takeaway from this will be the commands that Maya is running. In these cases, the circle command, the poly cube command, and the parent command. And besides that, we're going to have to do a little bit of cleanup. As I mentioned, the naming is not quite what we need and the size of the circle is known as user-friendly as we would like it to be. So let's translate these commands into Python and start creating the tool ourselves. 7. Creating the Tool: Let's start by translating these commands into Python. So all we need is the names of the commands themselves. Circle, cube. We might or might not need the select command. So we're going to ignore it for now. And the print command. So let's, Let's copy this and bring it into a Python lab. It's going to make the font a little bit bigger. Control on the wheel, on the mouse. Let me delete this setup that we had from before. So we know that these commands, and usually when you see these blue color and the italics means that they are coming from the MIS him It's library. So once again, if you remember, we need to import that library and you will learn these. These by heart. You will type from Maya import, see MDS. Great. So now let's access these functions from the CMBS Library via that dot notation, TMDLs, the circle seemingly as a poly cube. Now notice the case in here, The capitalizing. This is important. If you don't have that capital C, it will not be the correct command. Cmd as a pardons. So let's start with circled first. Remember that to execute a function, I'm going to just clear the history of here, so that is less cluttered. To execute a function, you need to use the open and close parenthesis. Let's just go ahead and do that. And let's just run these first three lines of code. So we get, we get a circle. Excellent. Now, let's do the same, but with a cube, poly cube. Let's run that line of code. Actually, let's run the circle and the poly cube commands or functions. Excellent. That's a good start. We have a cube and we have a circle. Now notice that we're not passing any arguments. So my S is adding the name for us. I know you will find out very soon how to find out these arguments. But I know that there's a name argument in most of these functions that you can use to pass in a string that, that will be used to name the resulting object. So in this case, we had said that we want the circle to be called control. We had said that we want the cube or whatever geometry we're going to be using to be called prop. So let me do this. And let's run this once again. Excellent, That looks a little bit better. So now we have a circle called control, and we have a piece of geometry or cube called prop. Now, these are not parented yet. So do in order to print them, we're going to use the print command. Now I'm going to show you how to parent them. And then right after this, I'm going to show you how to find out what each command needs and that will be done using their documentation. But so now what we want to do is we want to parent the prop to their control. So now notice what I'm doing here. We know that this is a cube and the cubes node is called prop. And let's reinforce that by saying the name here in the channel box. So what a lot of these Maya commands, you're going to use the name of the nodes. And the names of those nodes will be strings. So remember once again, strings are created by using quotation marks. You can either use single quotation marks or double quotation marks. That choice is up to you. I'm going to use single quotation marks. So I'm going to say, I'm going to ask the parent node to police power and the prop, sorry, I went to as the parent function to police parent, the prop node to the control node. So if I were to run this line of code, it will actually do it for me. Let me, let me just show that these are no parented yet. And let's just run that. And there you go. This is the result that we want. And now here you can see the pro-business, they're under the control node. So let's run this whole piece of code from scratch. Excellent. So now it is parented. And it's kind of doing what do we want? There's still a few things we have to modify. You probably noticed that the circle is not in the same position that we had it before. It's vertical and we want it to be flat on the ground. And it's also not it's not the size that we want. So let's clean that up. But also, you might have noticed before we fix this little details, we should clean up our code. So you might have noticed that we are hard coding the names that we want in here by marketing, I mean, writing the strings directly and we are repeating those names in here. So one good way to know that you have to start using variables is if you start repeating yourself, either repeating yourself or copy pasting. If you find yourself, e.g. copy pasting this name, prop is a good indication that you can probably use a variable. So let's do that. Let's say, let's say the control name will be control. Now let's say the name of the geometry will be prop. Now also notice how I'm careful in a minimal variables so that they didn't make sense when you read them. And let's just replace those strings, this hard-coded strings with those variables. So this will be your name parented to the control name. Excel. Let's run this again just to make sure it's all good. Excellent, it is. So now once again, the power of variables. If you want to name this control instead of CTRL. And if you want to name these some prop e.g. or something else, it still works and you only have to do it in one place as opposed to it in multiple places. Excellent. So let's, let's keep cleaning this up. 8. Modifying the Control: We want to do a few adjustments to the circle. One of them is that we want it to be a little bit bigger. And we also want it to be facing up in the Y axis direction. So how can we do that? As we mentioned before, let's, let's delete this right now on, let's just run the first few lines to create the circle. As we mentioned before, we can ask Maya to tell us how to do it. I'm going to clear the history. We could simply rotate this roughly, hopefully pointing up. We could also make it a little bit bigger. So as we said, this could work, we could steal the rotate and scale command. We're going to do something a little bit more clean, if you may, a little bit more, more easy to control. For that. We need to get into the circle command. So let's have a look. Still, let's ask Maya to help us out a little bit. Let's create a circle from this button up here in the modeling menu and the circles and surfaces. And I'm going to create a nurbs circle. So what's happening here? Let me make this a little bit bigger. My eyes, of course, using the circle command that we already are already using. And it's using a few more arguments. Now we do not need all of those arguments, but we do need some of them. Now this could be, this might look a little bit confusing. But I know for a fact that r for radius. So r in this case is one who might want it to be a little bit more than one, but that's okay for now. And n are, takes three values. And when you're, when you're converting from male to Python, if you see three values, this means that this is a list. And you will need to separate them with commas. So let's just run this. Let's run this circle command by itself. That's great. Let's see what if we increase the, the, our argument. Let me delete this one. So again, run the code. And now that's a little bit bigger. So how do I know this and how do I know that our means radius? That you need to look into the documentation? The easiest way to get to that is here through the help menu in the script editor and click in Python command reference. Now, once you click that, it will bring it to this Help documentation. Now, this is the implementation for the my aberration 2020. And these are all of the commands that you have available in Maya. So the one that we care about right now is the circle command. You can use this search bar in here. And we found the circle command. Now, a couple of things to note. Up here in the top right of this section. You can have either the male version or the Python version. And we want to be in the Python version, Of course. This synopsis here, this is sort of a summary of the commands that are available to you, sorry, of the arguments that are available to you. Now, all of these are the options for arguments that you can apply to this, this command. Now if you scroll down a little bit, and I wish I could get rid of this right here, but I can't right now. She's scroll down a little bit. You see flux. Flux is another word for arguments. You can see all of them. So something to note as well is that every flower, every argument has a long name and a short name, which is why we saw are an NR, which is a little bit cryptic in my opinion. So if we scroll down a little bit, Let's try to find our curators are the long name for r is radius. Now, if you see here, this is telling you the type of value, argument, type, type of value that my eyes expecting for this specific argument. We will cover most of this. Boolean is basically a true or false or yes or no. In the case of the radius, linear. Linear means a number, either an integer, which is a whole number, or a float, which in Python terms of float is basically a number with decimal points. So in this case, if you like, it's actually telling you right here that the radius argument has a default value of 1.0. These because it has the decimal numbers, this is afloat. But for now you think of linear as, as a number. It's a little bit of unfortunate that Maya doesn't use the vocabulary that Python uses, but it's okay. You will get you through that in this case in sections. So it's expecting an int when it is linear, it actually means float or int an integer into short for integer. So that's the first one. Now the other one that we care about. As I mentioned, it's an R. Now, N-R stands for normal. Normal is essentially a vector. It is the line that, let me go back to my ad, explain this a little bit better. It is the line that is parallel to the plane that we are dealing with. In this case, the normal is basically going up this way. We're not going to get into the details of how to create vectors and all of those more specific details. But for now, just note that this takes three values. You can think of these values generally as your x, y, and z axis. And note that it has these brackets. Now these brackets means that it's a list. We're going to get into lists very soon. But at least is basically a list of values or collection of values. In this case, it's expecting three specific values. So now that we know that, Let's go back to Maya. Oh, sorry. Before I forget, if you scroll all the way down, you will usually have examples and this is really cool. This is very useful because my, my other recommendation gives you examples of how to use the command. So you can just very easily run this and see how things work. In this case, this is using the normal argument, which is what we care about right now. And if you switch over to the male version, you will see those same examples in Nelson textbook. We are using Python right now, so let's stick to buy them. Okay, so let's go back. Let's actually switch to the full name of these arguments. Now, in the case of Maya commands, you can use both the short name or the long name that is up to you. A lot of people prefer the long name. But when it's easy to type, I prefer to be a little bit more explicit and simply type the long game. I'm going to make the radius a little bit bigger than the default, which is 1.0. I'm going to make it too. I could type two or 2.0 or 2.5, anything you want. In this case, I'm going to leave it as true. Now for the normal. Let's, let's play around a little bit. Let's just say that we want it to be looking towards the x axis. Now, if you notice here, in case you're not familiar with this axis down here in the bottom left corner. This is telling you what, what acts is you're looking at. So this is x, this will be z, and then upwards, that will be the y-axis. What happens if we run this? Well, our circle will be pointing towards the x-axis. But once again, we want it to be pointing towards the y-axis. Now, as I mentioned, these values represent a vector, so it's not quite the axis. But for now, for this example, for our purposes, you can think of them as just the x, y, and z axis. And once again remember that minus expecting a list, and that is done through this bracket notation that we have here. But we will get into that very soon. So let's run this control eight, highlight everything. Control Enter to run my code. Excellent, This looks a little bit better. So this circle is a little bit bigger, easier to select. Now we can animate it. And of course, it's driving the cube. Excellent. So what else can we do? Let's see if there's any bugs that we can find. There is one and it's a bit of a major bug. What happens if we run the same code? Once again, we get an error. It's great. Errors are good because they tell you exactly what is wrong. So this is telling you more than one object matches the name prop. How is that possible? So you see here we have two geometries that have the name prop. This is this cube here. And it looks like we have two different curves. Now remember that we mentioned that whenever Maya, whenever we give, give the name argument, Maya will try to use the name that we gave it. But if it already exists, it will add a number, which is, which is the case here, it added a one. So that means that our, that when we're trying to power and control, to probe or sorry, productive control. Control. It's misleading because this is a previous one that we created. It's not really the newer one that has the number one in it. Now, let's see a clear example of this. What if I simply create a few different circles? Let me just highlight a piece of Kodak crazy circle. Let's run this code three times. Three. You see the first one has the name that I, that I, that I want to have or to give to the circle. But now the other ones will have this suffix, this extra number in them. Can we avoid this? No, we can't. Maya has a way of making sure that the node names are unique. In the previous case. I think I should clarify this. In the previous case, I'm just going to run the code twice, it's going to fail again. Prob has the same name. This is because as you notice, property is actually nested under another node. So in this case, my allows that to happen. But don't worry about that. That's a that's a detail that will not affect you too much for now. So how do we how do we deal with this case when my SID and extra extra numbers to our names. Let's get to that. 9. Deep Dive - Help & Documentation: Before we continue, let's take a step back and talk a little bit more about documentation. So let's go back to the documentation page here once again, to get to it, you can go from the script editor in the help menu. Click on Python command reference. And I will bring you to this page here. Now, all of these run other commands that are available to you in the Maya commands library, the CMBS library. They are organized by categories. So e.g. you have the animation ones here. You will be using these quite a bit. Or you have the search part here. So let's look at the other command that we're using. Oh, actually, maybe let's have a look at the cube. Now. Once again, a quick summary. You have over here in the top-right corner of this section, you can switch between male and Python versions. We will stay with the Python version. Synapsis. Synapsis has a summary of the arguments that you can have available in this command. There is a small description. And more importantly, at the bottom, there is, there will be examples. This will be the most useful for you when you're starting to use a new command. Sometimes you can get all the information you need from trying now these examples. Great. So then the more still part of this is the flag, the flux section, otherwise known as arguments. Every flag or argument has a long name. Like this, construction history or a short names he H. It's up to you whether you want to use a short or the long name. I usually recommend a long names because they are more explicit, but every person has preferences for this. You have the descriptions for the comments here. Now an argument types, like I said before, it's a little bit unfortunate that Maya doesn't always use the names that correspond to the Python type. In this case, linear actually means float, which is a number with decimal places in it. Ain't does exist in Python in this short for integer, which is basically a number without decimals, in this case, one by itself would be an integer, one point something would be a float. Whenever you see these brackets, this will mean a list or a tuple. We will get to that very soon. You will, you're probably noticing that there's, there's these letters here. What does this mean? In properties? This means that you can use these commands when you're creating, querying or editing the node or the object that you're using this for. In this case, construction of fixed heap construction history is only used for creating and querying. Most of the other arguments can be used for editing. What does this mean? This means that you can use the same argument in the same command, in this case a poly cube. You can either use it to create a new cube. You can use it to query information from an existing cube, or you can use to edit an existing cube, e.g. if you wanted to change the width of your cube, you will use the edit flag for that, but we will get to that as well in, in further projects. Now, there's other ways to get help from Maya. The main one, to be honest, is this page that we just saw. Probably the second best place to do it is Google itself. And Google in itself can be a skill that you will want to develop. But I do recommend usually typing whatever you want to do. And e.g. let's say you wanted to find the current command I parent my Python. This will usually give you a good example or a good result, either back to the documentation. Just note that sometimes you get an older version of Maya or it could send you to a few other websites. One of them that is very, very useful will be StackOverflow. This is community for developers. And there's a lot of help for very specific questions answered here. So I do recommend looking for help in StackOverflow, either directly Google than IT, or in the search bar for a StackOverflow. There's always a lot of forums. I cannot recommend any specific ones. There's a lot of technical artist forums that will give you some help. So definitely don't be afraid of googling your, your information or errors that you might have because Google will be your best friend in your development career. So how else can we get help? Let me open a new Python tab. One way to get help is literally type in health. Now help is a built-in function inside of Python that takes in a function. And it will, if the function has any documentation inside of it, it will give you that information. So let's important CMBS library. Let's actually run the CMBS help directly. To run this. Now, you see that it gave you all of this information. This is a lot. This is basically what the developers of the CMBS library put inside of that recommendation of the library, and all of this is available to you. It's not very thorough to be honest, but It's something and it actually tells you where you can find it in your computer. These cases, this path, and all of the functions that are inside of it. We could e.g. this check ABC important in Olympic important. Now, notice that for checking the help, I am not running the function. Sorry for that. I am not adding the parenthesis because I do not want to run the function. I just want to get the help from him. Okay? Well, this one does have a lot of help, which is unfortunate, but sometimes you will get some help from this help function. Let me, let me, let me find a function that might be useful. This one might have better help. Yes, there you go. He just cast a little bit of description, but it's useful enough. It even gives you a recommendation which is great, excellent. Another way to get help, especially with the Maya commands library, is by enabling the quick help. Now this is in the command menu. Show Quick help. You will get this little, this little section here. You can find information, find your commands. Let's say, let's find poly cube again. Now this doesn't have any details, but it will have the arguments that you have available. E.g. we have been using. We go back to this tab. We have been using the name argument. So you know that that's coming from there. 11. 11 tells you that it's a string, which is really nice. Another one is apparent going. That's great. So it's not very detailed, but it's enough so that you can at least know at a glance what arguments you have available to you. I don't usually use this, especially because it takes away space from the script editor, but it's useful from time to time. So I'm going to turn it off. Another way to get help is by this Show tooltip help. Now, the way this works is, let's say I start typing the circle command. When I open the parenthesis, you will see these Tooltip. Now this is similar as when you hover over like a shelf button. It will give you some sort of scroll up tooltip, tooltips help that happens when you hover over something. It will give you that it has a little bit more information. And it has the, the argument types. And also the arguments are flags that are available to that comment. This one, personally, it's a little bit intrusive because it covers my code and I don't really like that. But it's a nice option. A lot of people like it and it's a good option to know about. I generally have it off, but once again, it comes down to personal preference. So that's a good overview of the ways to get help. Let's get back to the code. 10. Working With Node Names and Lists: To find what name's Maya we'll use or is using once we create our notes. Let's see the result of this function right here. So let's start on result in a variable called control. Now, don't be confused between the name of this variable and this string control because they are, they are very different. They have nothing to do with each other. I in fact, avoid confusion. I'm going to switch these two controlling step. So let's, let's run that. And let's print the output of control. This, this variable that we were storing the output, the output of the circle command or the circle function into this variable. Let's just run that. Just to make it a little bit cleaner. Let's delete this. Let me clear my history. And there you go. So this is the output. Now notice that there's two, once again, we have these brackets. This means that it's a list. Now this cell is with two strings in them. Don't worry about the u. U means Unicode, but for these purposes is essentially the same as a string. So don't worry about the EU. You notice that we have control. We have something else called Make nerve circle. One's a little cryptic. Now, let's run this a couple more times. Now, you'll see that the second time we run it is here. We actually have the name of the node that Maya created. So what does this mean? Let's say let me copy paste this down here just to make it a little bit clearer. Now, the first item in the list, this is the actual name of the node that I created, which is what we want to know, right? Let me just get rid of the U. Once again, the EU doesn't matter. You can leave it in there if you want. But for now I'm going to get rid of it. And then there is this thing. This is the, this is the constructor of the node that Mayans using to create it. What does that mean? Let's, let's make another example. By doing the same thing with the cube. I think this might be a little bit more clear. So let's store the result of cube. This variable called prop. Let me delete this circle that I created. Now I'm going to highlight only these two lines of code. You're noticing that I'm not highlighting the other ones. This is because once you run these lines of code, once Maya will catch them, maya will basically store the last value of these variables, which can be very convenient. So in this case, my already once, once I run these three lines of code, my already stored the name of this variable or the value of this variable. So now I can simply run these two lines of code. And it will, it will work just as well. So let's do that. Let's do it three times as well. So once again, notice that my hands using the name that I suggested, which is stored in the geo name variable, which is prep, that's the value string. But since it's finding that either one already was created, It's adding those names and the result of each, each one of those commands. Once again, it's the actual node that was created. And then this constructor. Now, don't worry about these from now. Why do we care about is this first value? So how do we access that value? Well, like I said, let me paste again. Let me paste this again down here, just to make it a little bit easier to see and get rid of the US because they are not necessarily right now. This is a list. As I mentioned before, you know that it's Alice because of these brackets. And alist is basically a list of items, a collection of values. You can have integers, which are just numbers. You can have strings, you can have a mix of them. Now in this case, it's just a list of strings. How do I, how do I get the first item? We do that by using another bracket notation. Now, when it comes to counting in programming, and this will be true for most programming languages, but especially in Python, we start counting at zero. So the index or the position, or let's say the number of this item is zero, so this is position zero, and then this is position one. Now we're going to cover less than more detail very soon. But for now, know that to get the first item of this list, we're going to get it through the zeroth index. We start counting at zero for various reasons. It's a very common standard, a standard in programming languages. So let's run this line of code. Let's just print this. Now remember, I'm printing the first item, the item in the list, and there you go. That's what we're getting. So let's try to do this a little bit cleanly. So let's call this, let's change this variable. Let's call it control. Let's call it control. Now once again, we know that the result of that is the list with both the name of the node and then the constructor, which we don't care about right now. Let's call control node will be the first, the first item, the control list item. And now the node will be the first item in that, in that result that we're getting from Maya. Now if this is confusing, don't worry. We're going to explain lists again in more detail very soon. Now, as you can imagine, we were not going to be using these names anymore. These have become suggestions for Maya to use as much as it can be four, it has to add numbers to the name. So let's print these two variables here. The new variables that we created, so control node and then node. So now these two variables represent the actual nodes that were created. There you go. So we control and practicals is the first time we create them. If we run this again, control one probe, one, run this many more times. You see that the number grows as we run the code. This is exactly what we want. Now. Let's fix our power in command. So now we want to parent the creative control node. We wanted to parented to the probe. Note. Let me delete these two lines, remove a few lines, and let's run this code. Excellent. So this is the results that we're familiar with. Actually. It didn't it didn't work. Oh, I did it the wrong way around. We want to parent the prop node to the control node. We want the control to be the parent of the problem. Which makes sense. I'm sorry about that. Debugging as we go. From that again. Excellent. So now this is as we expect, if we move this to the side, Let's run our code again just to make sure that this works. Great. Now, this is controlled one and drop. Once again, like I said, because these are cube is parented under something, my allows the name to be repeated, but that's fine. We don't care about that because remember, we're getting the name directly from Maya. We're not guessing any names. My strlen, you telling us exactly the name that it used to create that poly cube? Just for sanity check, let's do this once again. Third time's the charm as they say. And there we go. Now we have all of these. Now we have multiple props working at the same time. Let's do a small adjustment before we move on to the next lesson. The small adjustment that I want you to do is essentially a workflow adjustment. Wouldn't it be nice if when you create your rig, we don't want to be animating the cube directly. Most likely we probably want to be animating the rig. So let's, let's have it select that control. We have, these are the select command is great. So let's see. We can simply do that. Cmbs Dot select that, that is working. And we want to select the control node that we created, right? So it's passing the control node to that function. Let me make this clear it up through my history. And let's run the code once again. And there you go. Now, as soon as you create your prep, you can start animating. 11. Extending the Tool with "if" Statements: We have our work into it essentially does what we need. But let's make it a little bit more interesting. I did say at the beginning that the prop didn't have to be just a cube. Let's add a little, Let's keep ourselves a few more options. And we're going to introduce also a new concept. And that is the if statement. If asked him if something happens, do this else or otherwise do something else, meaning that you can do a switch and choose a different option depending on a condition. That's how the iif statements work. So how do we do that? First, let's, let's identify where that condition would change. In this case would be here, which is like the cube. We know that the control, we don't want to change that where you just want it to be the simple circle. We only want to change the geometry. So let's start by changing the geometry. Instead of having it be a cube. Let's do a sphere, e.g. let's just, once again, let Maya tell us what we need. This case seems like poly spheres, all we need. So let's actually type that in before we add the statement that mean, clean this up. So let's type once again Pauli capital S. Now let's just run this code. Now notice that the code is exactly the same as we had. All we change one's cube or sphere. And there you go. Code works perfectly. Well, same as we had it before. Now, let's say we want, let's say a cone. Easy enough, just type cone instead of sphere. See the beauty of consistency that Maya provides, provides. Sorry, I meant to type cone. Let's write that out. Perfect. So let's, let's stick to those three options. Now we know that all we need is depending on the option that we want. We want to change that, that command or that function. Now, let's add a variable that will help us decide the option. Let's call it type. And this will be a string as well. It can be anything you want, it can, it can also be a number if you want to decide that way. But to make it more user-friendly, we're going to use a string. Let's say the default will be cubed. Now, notice that I am intentionally calling it prop type, not just type. The reason for this is because there's a built-in function in Python that is type. And it's usually not a good idea to create variables that are named the same as built-in functions. That is because if you do that, your code is not going to break, but you're going to basically override the function that is built into Python and you will not have access to it. In this case, we don't need that function, so it's okay to do it. But it's in general, It's a good practice not to do that. I'm going to say prototype. I'm going to say reset it as cube. For now. We're going to say if prop type equals, equals qb. Now pay attention here and I'm saying double equals. Instead of a single equals, when you use a single equals, you are assigning a value to a variable. You're using a double equals, you're doing a comparison. And now here comes something, something new called indentation for us to work inside of this if statement. We're going to have to indent this inside of it. The standard indentation in Python is four spaces or so that again, 1234. Or you could also use the tab, in this case, the top just above the caps lock, next to the Q key. In this case, Maya scripted or in Maya translated, translates to tap to four spaces. Just keep in mind that in general, it's good practice to have the force bases in Python 1234. Now I'm going to use a tab because it's equivalent, because we're inside of Maya. If we were inside of something like Notepad, e.g. this here, the tab. Let me make that a little bit. Let's say just to a, so this is a tab. These are four spaces, 1234. They are not the same. So that's a very important distinction. Indentation is something that makes Python very easy to read because you don't need a lot of extra syntax, but you do need to be aware of the indentation. If fewer indentation is off by one space, you're going to get an error. We can see that in a bit. So once again, if prop type equals, equals cube, then we're going to do that. Actually. I type cone. So let's say cube. Now let's say l. If this is the equivalent of saying else if. But in this case you have to type LF prop type equals, equals b or e.g. Poland. Now notice that at the end of the if statement, I'm writing a column. This is very important. If you don't do that, Python will give you an error. And also notice that after the colon when I type enter Maya, scripted or in Maya automatically gives me that spacing because it knows that I have to invent something on there that else if statement. So this case, let's, let's copy this. Now. Sphere instead of cube or sphere, perfect. And now the last option we talked about was a cone. So LA prototype equals, equals colon, colon. Let's copy paste this. And you might see already a redundancy in this code, which is great if you did good for you, Well done. If not, I will mention that in a second. So poly cone. Excellent. Now, what happens if, now remember, sorry, this, this variable, we're comparing this string to the value of this variable up here. Now what happens if there is no option that we accept? Let's have a default value, let's say. So for that you can say else, else. Let's assign. Let's say that the default value will be cube. Let's also tell the user that they didn't provide a value on, that we're using a default value. So there's a function called warning, same the boarding. And the warning allows us to just give a message invalid option using cube x. So this could be a better message, but actually invalid prop type option using cube as default. There you go. That's a little bit more clear. Excellent. So let's just run this code before we dive into it a little bit deeper. Let me clean up my spacing a little bit and maybe one more space, but we get a little bit more easy to read. There you go. Alright, so Let's just run this code and we get a cube. Now, let's add type another option. Let's say a sphere. There we go, we get a sphere. Now, what happens if we were to make a table, e.g. this? Now feel free to pause the video right now and try to guess what is going to happen. So let's run that. There you go. So you see we have that warning here. Invalid prop type option is in Cuba as a default and we got a cube. So what happened there is that since the, the value didn't match any of these options that we, that we put in there, then we're defaulting to the else. Now also notice that this is case sensitive. So if I type sphere with an S, capital S instead of lowercase S, We're going to get another cube. And these are upset because offset because I've moved the other one previously. So let's just test our last option which is a cone. Excellent. There you go. So now the total is a little bit more robust. We have more options. And this is great. You could even have made three shelf buttons and simply change this variable and it will give you the three different shapes. So let's dig into the code a little bit. Once I can notice that we're using the if statement to validate or condition. This is called a condition. You're, you're making a comparison. You're saying if this variable equals this value, then we're going to create a polygon. Otherwise, else, if this variable equals this other value, we're going to use this other function. And at the end we have, let's say a blank else statement. We don't have a specific condition we're checking like in this other else if statements. This is basically saying otherwise if anything else happens, though this, now this is not necessary. You can have an if statement just by itself and it works, it works, it works fine. This is just giving you more flexibility to have extra conditions or a bit of a catch-all type of situation which is the else in this case. Great. So I did mention that there was a redundancy in this code. And that is this line here. So we remember once again that if you run the poly cube command and let me just do that. If I simply select that function and run it, you'll see the result. Maya gives you the result. That's the name of the node. And then there's this other shape that is the thing that's constructing the cube in this case, but we don't care about it for these purposes. But that is how list. Once again, remember the list is with the brackets. And the thing is, the thing we care about is the first item in that list, in this case the name of the node that was created. And we access that through this indexing. So this notation, we start counting at zero when it comes to lists in Python. So we access the first index, the zeros index, and we assign it to this variable. Now there are redundancies that we're doing the same thing every single time. So it doesn't matter. It doesn't matter which command we run. We are still storing that result into the same, the same variable name, right? So we only need to do this once. So let's remove this indentation and get that out of the if else statements. And now we don't need these lines in here. Now, pay attention here to the indentation. Indentation once again, notice that this prop bar variable is indented under the if. Else, if statements. And the actual final node that we're getting is outside of those statements. That's important. Because once again, if we indent here, if we indent this, now this will be run only inside of the else statement. And if we, if we enter the if statement in it at the beginning, we will never get to that. And we might even get an error. Let's move that out of those statements. And also, if it wasn't obvious yet, they've, they've statement. The if-else statements work like a switch. The first time you, if you catch the condition the first time, it will stop there, it will not go to the next few conditions. If this condition is not true, it will try to move to the next one otherwise. So next one otherwise to the last 1 bar, if you can, if the condition is met in any of those previous steps, it will stop there. Great. So let's run this just to make sure that we haven't broken the code. There you go. Cone. Once again. Sphere. Excellent, this is working as we expected. Now, let's see what happens if you made any mistakes. And there's a very good chance you did. And if you did, it's amazing because mistakes allow you to learn a lot faster. So let's say that that your indentation here was a little bit, or actually this might be okay because it's the same if statement. To prove that point. Let's, let's add some print statements. Print aiding cube. Let me switch this up here to the a cubed, so we enter that IF statement. So now when we enter this, it should print grade in a cube there. Excellent. Now to prove my point about the indentation, Let's remove one space for this invitation. Now, notice that these two lines of code are not aligned. There you go, you get that error. This is the importance of indentation. Whenever you have multiple lines of code, one after the other, the indentation in them has to match. And in general, just stick to four spaces. Or if you're working within a coding text editor, which are going to be doing very soon. Using the tab will be more than enough. Let's do scale it back to normal. Excellent. I'm actually going to get rid of that line because we don't really need it. And the other thing that I mentioned was the column. If you don't have that column for some reason, let me clear the output window. It will give you a syntax error. Syntax means that you're not given the syntax or type in the characters that Python expects of you. What's great, and this is going to be very useful. Notice that you have all of these. This is called a stack trace. Stack traces the steps that lead to the error that you are encountered. And Maya very conveniently gives you the line where your code failed. In this case, it line by line 13. Now it says line 13, but it's actually the line just before. But it really tells you the, the place where it failed. It will not always do that in this case, it's very clear and it doesn't actually tell you what's missing, but it tells you the general area. So you can, you can come back and look over your code and try to find the error. So that will be very useful. Now before we end the video, Let's do something else that will make our code a little bit nicer. Let's add some comments. So e.g. in here on line seven, just before we create this circle, that is our control type, the pound key or the number sign or hashtag, however you want to call it. And just write a note. I'm just going to write create control for break, e.g. this is a comment. This is not code that will be executed. Python essentially ignores it. This is just for you to type a note for yourself or for whoever else is going to be running your code. Now, let's here, Let's say create geometry should say geo based on type would be clear enough and roll down down here. This is where we get the actual node that was created. Created node. And here aren't, GO TO troll. Then we have select. I will select this very self-explanatory. I would say parent is also very self-explanatory, so I don't mind on the other comment, but let's just leave it there. There's no general rule when it comes to comments. It's at your discretion. Too many comments can get on the wafer code. But no comments can also make it harder to understand what's happening in your code. So find your own balance when it comes to writing comments. Sometimes our well-named variable, e.g. prototype, might not need a comment cosine itself. It's very self-explanatory, so same as control name, GO name, a well-named function, and we're going to be creating our own functions very certain can also avoid the need for comments, e.g. parent is very self-explanatory. Select. There's no need to say what selects going to do because you know exactly what it's going to do. So yeah, we could maybe add a few more, but I think this is good enough for now. 12. Deep Dive - "if" Statements and booleans: Let's talk about if statements. If statements are very simple and to fully understand them, you have to be familiar with two concepts. One of them is conditions, otherwise known as conditional statements. And this is the basis of an if statement, because I'm IF statement is basically saying, if this condition is true, do something, otherwise, do something else. What this condition or, or do nothing if you, if you want. And what this condition statements boil down to is something called a Boolean data type, called Boolean, otherwise known as a bowl. It's a very simple data type that can have either one of two values, either true or false. It's called a binary value, can be like a zero or a one, essentially a yes or no value, true or false. True. Be aware of the capital T. That's the syntax for Python or false. So let's do an example. Let's create a variable, just a simple number, num equals ten. Just for the sake of an example. We're going to say if num equals, equals ten, we're going to print yes, e.g. otherwise, we're going to print. No. Excellent. Let's just run this code. Okay, sorry, that was my bad. I'm not actually printing anything. Let me fix that. There we go. There you go. Now we're printing. Yes. Now, if we change this over to be a nine or printing and no, Excellent. So let's break down what's happening here. This is your condition right here. Num equals equals ten. Now notice, well, first of all, let's note, is there the intention here? And in the case of this error, every time I press enter, it will give me that indentation automatically. But remember Python needs inundation and usually it's four spaces. Or in the case of my programming editor right now, is going to be a tab, which will be the equivalent of four spaces. That's very important. If we don't have that, Python will throw an error. Also notice the column at the end of the, the if and else statements. But that's very important. So now back to this. This is your condition. What we're doing is saying, basically asking, does the number equal? Now equal, equal, this is a comparison. It's called a comparison or per operator. And a single equals sign is an assignment operator. So basically means that a single equal sign will assign a value to something, in this case a variable. And a double equals sign will try to make a comparison between two different values. Excellent. So there's different types of comparison operators. We're not going to mention all of them because the focus of this video is going to be on If Statements and booleans. But let me show you a couple more. The opposite of the equals, equals not equals. This is an exclamation sign. And a single equals is called naught equals and it checks for the reverse. So it's asking does number or does num is variable not equal ten? So if you run this code, yes, nine does not equal ten, about 20. So true, 20 does not equal ten or ten. Well, now that is not true because ten equals ten, so it doesn't meet this condition. Excellent. So let's do one more example. Let's say we're checking a range of numbers. Let's say that it's asked if the number is less than zero. Let's say we want our range to go 0-100 if the number is less than zero. Too low, Let's bring too low. If this has specific syntax l, if it allows you to check for another condition, L, if the number is higher than 100, will say too high. Otherwise, we're not checking for any specific condition just otherwise. What we're going to say, number within rich, which is one that I have a syntax error because we're missing the parentheses here. Excellent. Gotten number within range. What happens if we go to 100? Number within range? 101? High, and just for the sake of completion, minus five is too low. Excellent. So remember that I said that conditional statements basically boil down to a Boolean, either true or false. So let's, let's see that. Let's see that example. Let me delete all of this. And let's assign this, this comparison to a variable. We'll call a print a. So what are we, what are we doing here? Actually non doesn't exist anymore, so I cannot do that because we deleted that variable. Let's just do something simple that we did at the beginning. So five equals, equals five. So we're assigning the result of this comparison to this variable. Now, try to think what's going to happen. It's playing that. And it's true. Five equals, equals five. Great. Now, let's say it's five or less, or equals to five. It is true. Five, less or equals six. Well, no. No, it is. It is true. Sorry. Yes, it is five, less or equals four. No, it's not, that it's false. So notice how we're getting this result. True or false when it comes to that condition. Now let me undo a little bit to go back to our if statements before. That is exactly what's happening here. So they've statement is basically evaluating that condition, boiling it down to either a true or false. So now you might be wondering, can we then simply say true or false? And you will be correct if we say if true, print, yes. Otherwise, print, no. There you go. So in this case, we don't need the num variable anymore. In this case, true will always be true. Great. Now how about false? False will always be false. So we're going to be printing in this case. Now let me show you one more thing. You can invert. You can do operations with Booleans as well. You can say, you can do the opposite so it not false. And let's actually run this in an isolated example. Let's print not balls. Now, once again, pause the video and just think for a couple of seconds by your thing this is going to print. Now let's run it true. Now if we do not true, what is it going to print false? Because not true is essentially false. Great. So we're going to say if not false, print, YES. Otherwise print know, this might be a little bit contrive, a little bit confusing. But once you think about it, it makes perfect sense. Not false is true. So we're going to print yes. What if I say if not true, not true, not true is false. So this can be the division will not be met. This will be false. So we're going to end up in this else. Then. There you go. Hopefully, if statements are a little bit more clear now, they are extremely powerful. They are very simple, but you will use them as switches to guide the flow of your code and to allow your code to be very versatile and very elegant in a way. 13. Introduction to Functions: Alright, so you have come a very long way in just your first project. And really in very few lines of code, you can see these are only 31 lines and there's a lot of empty space in between. You have acquired some very powerful and very useful skills, skills that are going to allow you to create some variables stuff. Getting to this point is a very good achievement. You should be proud of yourself. So congratulations on that. Even if some of these still a little bit confusing, please note though that is normal, that is expected in a way this is a lot of information and don't worry because we're going to be reusing all of this knowledge, all of this information in the next few examples and the next few lessons. Once again, this is very good progress and that means that you are ready to level up. So what do we need to level up our coding skills? One thing we need to do is get out of this group donated or in Maya. And I mean, this is great enough. It's good enough to type coordinate as we have been doing so far. But we should really be using a text editor or with programming abilities, programming functionality. It will not only allow you to write code outside of Maya, but also make your coding experience a little bit nicer, more enjoyable. And it will be the first step into learning how to create your own modules or your own libraries so that you can import your own code, just like we do with minus own CMBS library. But before we get to that, we need to learn one more concept. And this is one of the most key concepts in what is known as object-oriented programming. And it's, it will allow you to learn how to create scalable and organized code. And it will really unlock the power of programming for you. And that is the concept of functions. We're going to step outside of Maya for a second to talk about functions. And I hope you don't mind me using paint. I know it's not the best drawing tool, but it's good enough for what we need right now. So functions, there's two ways of thinking about functions. One of them we already mentioned, which is thinking of them as a black box. Let me just draw. Tried to make a box here. So it's a mysterious box in the sense that we don't know and we don't really care what's happening inside of it. All we know and we care about is that it takes an input. And it, well, inputs are functions. Not always take an input. Very often they do, but it's not mandatory, but they always, always return something. And this is a keyword. Return. Functions will, will always return a value which is the output of the function. And as we have seen already, we can store that value in a variable. Now, if the return value of a function is not defined, it will default to giving you something called a nun, which is basically a null value. We're going to learn about that very soon. So let's run through an example that should be familiar to us. The poly cube function. So once again, we don't really know and we don't care about how my eye is actually creating that cube, right? All we care about is that it takes some inputs, like we know that it takes a name so we can name the cube. Pardon my handwriting? And based on the documentation, we also know that they takes any other number of arguments that we use to modify the output. And then the result of that is of course a cube. But as we saw already, the returned value of that function is actually a list. And that list contains two items in it. One of them is the name of the node and the other one is another object, another shape that is the actual construction of the cube. But so far we have only really cared about the first item in that list. Again, that's how you think about a function when you're using it. Now the other way you're thinking about it is when you're actually creating the function. And in that case, you will be the one defining what goes inside of that blue box, inside of that mysterious box. And that is simply code. Very similar to the code you have been writing so far, with a couple of small exceptions, but it's basically a set of instructions. It basically is a collection of steps or collection of instructions that may or may not take an input, do something with the input, and then give the whoever is calling the function, return a value. Or now they can then store in a variable if they want to. E.g. let's run through there are very simple example. Let's say that we want to make a cake recipe function. Cake recipe. And let's say that we wanted to take in the number of people. Let's say n. N will mean that the number of people that we want the cake for, the function will take n, which is a number. In this case it's an integer. An integer is just a whole number, as we talked about before. And then it will work out the quantities and the ingredients and anything that it needs to do to give a recipe for that number of people. And then e.g. let's say that it returns a string, which is just the text, will be the texts but the recipe inside of it. Now when somebody is using this function, they will call cake. Cake recipe. We know that it's expecting n, which is an integer. Now n is you, you, you decide what to name. This will become a variable. You decide what to name it, but we know it's an integer, so it takes a number. So let's say we want a cake for four people. Now, whoever is calling this function, they can simply say, they can simply store the result of that in a variable. In this case, I'm calling that variable recipe. So what's happening here is we're going back to thinking of the function as a, as a box that we don't know what's happening inside you as a developer, of course, you know what's happening inside, but somebody else using the function, they don't need to know. You might even change the, the instructions inside of the function. And they will still simply call cake recipe with the arguments that you define the new function. So what's going to happen is your function will take this input. It will do some sort of coding inside of it to process the recipe and then give that value back to the user so that it can be either ignored or it can be stored, in this case in this variable called recipe. So hopefully that's clear enough. Now, let's go ahead and create some actual functions. So when you're making a function, it's actually called defining. A function. Type would mean death, scene define plus in definition, D, E, F space, and then the name of your function. Let's make something simple. Let's say, let's make a function that will say hello. So type, say underscore Hello, now open and close parentheses. And let's start simple. Let's have a function that takes in no arguments, no input at all. And then colon, press Enter. You will notice that my automatically gave me this indentation. Whenever you're creating a function, anything that goes into the function has to be indented. Remember that is four spaces. Or in the case of the script editor can be a tab on all of the code, all of instructions, as we mentioned before, that we want to have in the function will go indented this way. So let's have a message, and this message will be kept very simple. Let's just say hello. Now, let's not returning anything right now let's simply print this message. Excellent, So let's try this out. Let's run this code. No errors but nothing happened. We just got our code back constitutional. Let's say, let's write a function down here. Now, let's not call it yet. Let's just write that, see what happens. Nothing is happening. Now let's print the actual function just to inspect it a little bit. Interesting, we've got this weird thing. It says function say hello at some random, seemingly random number. So what's happening here is, as I mentioned before, I mentioned the idea of object oriented programming. A function, and most anything in Python is called an object. Now, don't worry too much about this definition of an object. But basically know that this function now exist in memory, which is why we got this number. Your function is basically saved in new rum. Again, don't worry too much about these technical details. But know that this function will exist, but it's not giving us its value yet until we call the function. So let's call that function and run the code. Excellent. Now it's actually saying hello. Now we talked about return values. So let's check what this function is returning. I'm going to type result equals, say hello, open and close parenthesis. Now we're going to store the result or the return value of this function into this variable called result. Let's print that. Print results. Excellent. So we're still saying hello, As you can see here. And we got this thing called none. As I mentioned before, unknown is it's kind of like a null value, sort of like a void or empty value. You can treat a non similar to a false, which in a way it's kind of like a false in a Boolean. But it's its own special type of value. It's actually very useful. It's not just empty is actually quite useful to check whether or not you're going to a value back from a function as we're going to see very soon. But if you remember, I said that functions always return a value. Now, since we're not explicitly returning anything, python will default to return unknown. So this is similar to saying explicitly return none. So let's change this a little bit. And instead of printing the message, let's return the message. Now notice there is no parenthesis here, it's just returned space message. Now, one thing to notice is that return means the end of the function. It will not only returned the value, but it will also end the function. Or we're going to see that in a second. Let's run this. I'm going to get rid of the print results for now. And let's run this code. Let me clear the output window first. Let's run this code. Nothing similarly happen. Why not? Well, the reason is because we're not printing anymore. We're simply returning the message instead of displaying it. Now, if we actually print the result that we have here, we assign again, once again, assign the return value of the function to this variable called result. Let's run that code. Now the word printing something excellent. Now we say hello. Great. So let's expand the function. But before we do that, I wanted to show you how the return means the end of the function. Let's print after the return, Let's print something. Let's just say after Return. And let's just run this code. You see how we're still printing hello, but we're not really, we're not getting to this point, once again is because returned means the end of the function and it will return that value. Just to make this even more clear, what happens if instead of returning message, I return a number. Let's return 100. Now. Take a pause and imagine what's going to happen here. But I think it's very clear. It's going to print 100 and we're completely ignoring this message up here. Great. So let's go back to returning message. And let's expand on this function a little bit. Now, let's make it a little bit more sophisticated. And let's add an argument through it. Let's say we wanted to say hello to somebody. Say, Well, you would take in a variable or an argument called name and we want to say hello to name. Now, we're going to expect name to be a string, just like this fellow here. So when you have two strings, you can actually put them together by simply saying plus, we can say hello plus name. Let's run this code again. Oh, great, We have, we have an error. Why he, let's see what's happening there. Error say hello, takes exactly one argument, zero given exactly, because now we're expecting an argument. So let's pass in a name. Let's say Adam. And I once again, as we said, the name is going to be a string. So now name. Once we run this, notice how name will take in the form of anime. We're basically saying, okay, then this variable called name will be Adam. So now we should be saying hello plus Adam. Now let's run that and see what happens. Excellent. We're missing a space. Let's just add that space after Hello and run that code once again. Beautiful. That's excellent. One thing to notice as well is that this variable here only exist within the scope of this function. Now, there's another specific word that I'm using. I'm using it intentionally. Scope. Scope means that this function, sorry, this variable, either the name variable or even this message variable. This will exist only within the scope of the function. They, they basically live as long as the function runs. So just to clarify that point, to get the point across, Let's print name into what happens. We have an error because name is not defined because named doesn't exist outside of the function. Once we remember again, anything that's inside of the function lives after this indentation. Once we go back outside of that indentation, we are done. We don't have access to that anymore. Now, if I were to put that print inside of the function, what happens? Now we're printing atom here, which is the name. And if we change the name here too, like George, e.g. now we're printing George. Excellent, so let's expand this a little bit more. And let's get a second argument. Let's say we wanted to say hello to the, to the person's full name. Let's add one more variable. Let's call this lastName. Let's run this. Now, we have another error because it takes two arguments. We're only passing in one. That makes perfect sense. Let's say George Smith, very common last name. So we're going to pass in that last name. And let's, let's run this one. The only saying hello to George, of course, because we haven't added the last name. Now, we could do this, we could do plus lastName. And this will work. But again, we don't have a space. We could I create another string with just a space in it and then keep adding a plus. That will work. But this is getting a little bit verbose. This is too much typing. So let's learn another way of putting strings together. Let me get rid of all of these plus variables. Now, type with me here, are going to say Hello, space, open and close curly brackets. To make this clear, these curly brackets, these are not parenthesis, these are curly brackets, type of space. And then curly brackets again. Now we're going to say at the end of the string, we're going to say dot format. Open, close parenthesis, we're going to say name, comma, last, last name. So take a second to pause and imagine what's going to happen here. It might be a little bit confusing or it might be very clear. But what's going to happen is that this curly bracket, so these curly braces are going to become placeholders when we're using this dot format function, python knows to expect something to replace these curly brackets width. In this case there's two curly brackets and there's two variables. So name will be, are these curly brackets will be replaced by name, and this second color brackets will be replaced by lastname. Let's just make our message a little bit better. Saying hello to name and last name. Excellent. Now our message or result is saying hello to judgment. Great. Hopefully it's clear so far, but just to make it even a little bit more clear, let's run through another example. I was thinking about something completely unrelated to animation to try to make it a little bit interesting, fun, and perhaps more clear, let's do something related to your finances. If you have heard of these payments movement called Fire, which means financial independence, retire early. This is of course we're not going to be talking about finances. I'm not a financial expert. This is by no means any sort of advice. This is just a bit of a fun example. There's this sort of calculation that these people in fire half that basically say, okay, if you want to know how much money you need to invest to retire early or retire at some point, they basically say, take whatever your annual expenses are and make that the four per cent of your investments or you're saving. The idea behind this is that the market in general, like the stock market or something like bonds or some type of investments will give you a 4% return every year. So if you can live off of that 4% return every year, you can basically live from those investments for a few years. So e.g. let's say that your your annual expenses are $40,000, e.g. well, $40,000 is 4% of $1 million. That's a lot of money. But again, this is not a financial class. This means that you need $1 million invested and every year you will get 4% of that, which is your 40, $40,000 that you need to cover your expenses like whatever your expenses are. So let's write these calculator just as an example. Once again, type with me def definition with the finding of function space. And let's name the function, let's say fire, underscore, underscore, calculator, open parenthesis, close parenthesis. Excellent. So now what do we want our input to be? We want expenses, right? We want to calculate how much investment is you need. Love, we're going to receive the expenses and we're going to return how much money you need to invest to get to live off of the 4% from those investments, right? So let's say amount will be equals and this will be the calculation expenses divided by 4/100. And let's return this amount. So let's simply print the result of this function. Now expenses, of course, we need to replace it with a number. This will be 40,000. And let's just run this code. We got 100. Now this is clearly wrong. Let's see why. This is actually part of why I wanted an example with numbers is to show you another peculiar aspect of this version of Python. Let me open a new tab and let's do it. Let's do this calculation together. So let's say we're dividing four by 100. We're going to say, just store these in a variable called R. Let's print that result. Oh sorry, this is not a division. A character that was my bad is the forward slash to divide. Great. So again, this is clearly wrong. So what are we expecting from this division if you divide four by 100, or we are expecting 0.04, right? So what's happening here is that we're dividing two integers. And now anytime for any version below any Python version below Python three, when you divide two integers, you get an integer back, which means that the value is getting truncated. What does that mean if I have 1.5 e.g. remember that I said before that an integer. Basically a whole number that doesn't have a decimal points. So because we're dividing two integers, this, if this was a result 1.5, this number will be truncated and we will only have the one. We were basically just removing the decimals. Now, when it's hard when you have a decimal point that is called a float. Now, once again, this is int, is just a whole number. And a float has decimals. So what do we need to do to get this result? Well, we need to divide an integer by afloat. There you go. This is more like what we're expecting. This works the other way around as well. By the way, we can also divide a float by an integer as long, as long as one of the numbers is afloat, Python will give you the correct value. But if it's not a float, once again, if it's just an integer, then it will truncate it, it's actually giving you is actually getting this result. And then it is truncating it because it thinks that you're asking for an integer grid. So that is an important peculiarity that you will find in Python. And if you're trying to do calculations and you run into this issue, it might be extremely confusing, but just know that usually it's a good idea to simply at a 0.0 to turn a number into a float, then you avoid this issue altogether. So another we know that we need. This is the actual value that we need. Let's just replace it here. Once again, this is just math and you don't really need to learn a lot of math or what do we need to do? So don't worry about the specifics of this. Just know that we're basically calculating what the, what this is. The four per cent of excellent expenses divided by 0.40, 0.04 will get us there the final amount that we need. And that is $1 million. And notice here we got a float, which is great. It doesn't matter if it's a float and integer in this case. Let's test it again. Let's say that you need $80,000 to survive a whole year. Then you would need, of course, $2 million. There you go. Once again, this is not a financial discussion. This is just an example. But I did want to show you that peculiar side of Python. We're loads and integer divisions can be a little bit tricky. Now before we move on, there's one last thing that I want to talk about, and this is extremely important. And there is a documentation I mentioned before that there is no other to create strings. You can do single quotes, double quotes, or you can use triple quotes. Now, I also mentioned that triple quotes are reserved for something called docstrings. Docstrings is basically documentation for a function. What do I mean by that? Type would mean triple quotes, 123. These are three double-quotes, by the way. And then let's type a message. Let's say this will calculate how much you need to calculate the retirement amount, e.g. retirement amount. Now type and other three quotes, 123, but three double-quotes. And let's run this code. Same result. So this is just a string and this is not really affecting your code in any way. But it's extremely useful for something that we have seen before. Now let's, let's remember that help command. We saw that help function. Let's run our function inside of that help function. So now, once again, pay attention, I'm not calling this function. I'm not opening and closing the parenthesis and simply writing the name of the function. So let's run this code. Help open parenthesis, far calculator, close parenthesis. And you see it says help them function for a calculator in Module main. That just means that you're in. Whenever you don't have a module which is basically a Python file, we're going to see that very soon. It just calls it mean, so don't worry about this part too much, but it's basically telling you the name of the function. And you can see here, it's displaying you that little bit of help. So if somebody were to be using your function, they can run help on it. And they can get a little bit of a message from you, the developer telling them what to do or what the function is doing. Now something else that is very important when it comes to docstrings. And we're going to be using this. We're going to be doing this from now on, is telling R are mentioning what is expected in this input. So now remember once again, because of scope, this or this variable only exists within the, within the function. And because it's Python, we could be passing in, fastening anything we want this function. So it's important to mention in the documentation what is expected to be, whatever this function is expecting to receive. To do that, you could simply say expenses or burn, close parentheses, and type in the body is expected to receiving this case, let's say it's an integer. Actually, there's different ways of writing this. I'm going to stick to one that is, it's a conventional standard by Google. And what they do is say args, which has been arguments colon. And then we're going to indent this on the args. We're going to write once again expenses. In parentheses. We're going to write the type of this variable, and this will be an integer because we're expecting a number, right? Then we're going to write a small comment about it. Let's say your annual expenses. Once again, all of this is just documentation, but it will help whoever is using this function, which could be yourself in a few months, know what is expected. Now something else that is useful is mentioned what the return value is. We're going to type return colon. And then I'm going to type the value that has been returned. In this case, we know that it's a float. Let's run this again. Great. Once again, the help function is giving us all of this information right here. Once you run the help of somebody that is using your function will know, okay, it has one variable that is expecting an integer. And I can be expecting a float in return as far as a return value. So hopefully this, this helps clarify what functions are a little bit more. This was just a brief introduction to them. Now, let's turn our code into a function. 14. Turn Code Into a Function: Okay, back to our code. Let's now turn all of this code into a function so that it's a little bit more re-usable. Now that we know what functions are, how to make them, we can very easily turn this piece of code into one of them. It might seem like a lot, but it's actually very easy to turn all of this into a function. But first let's think, let's think through what do we need the function to do? Let's start with the arguments are the input, what would you like the input to be? We probably want to let whoever is using the function to the side what type of PrEP they want to make, right? I imagine we might also want them to decide the name of the control and the new geometry. But let's keep it simple for now. Let's say that we make the executive decision to simply leave, give them control over the type of prop grip. Let's do that. Let's say we create a function once again to start defining a function you type def space. Now let's name the function I'm going to name it creates underscore, underscore, reg, open and close parenthesis. Let me make this a little bit bigger. Now, we need the, the input. Well, it's very easy. We can simply reuse this name here. Since we are, since we're already using these names throughout the code. Down here. What we're actually hearing this if statements. Why not just reuse that and make it easy for ourselves. Prop underscore. And then we'll close parentheses and then colon. And now we know that these instructions to be inside of the function, we have to indent them. How do we, how do we create that indentation? We can highlight all of that code and simply press the Tab key. There you go. That's all. That's all you need. We have just turned all of this into function. Great. What do we do with this up here? We really don't need it anymore so we can delete that. So now all of this code is inside of a function, so let's just run it, see what happens. Well, nothing is happening and I'm sorry, this is getting a little bit hard to read because it's, the code is growing, but it's great because you're writing more code. So nothing really happened. This is a, this is all just the definition of our function. Remember, we have to call this function to be able to use it. Well, let's, let's do that down here. Let's call it down here. Create appropriate, let me just type that creates rig open and close parenthesis. And now remember that we're expecting an input. In this case, it's called prop type. We're expecting a string and actually, let's write a little bit of documentation for our function via doc strings. Once I can remember triple quotes. And by the way, these can be single quotes as well. Triple single quotes, single quotes, or three double-quotes. I prefer the double ones. Let's just write a letter of recommendation. Go without control. So I'm going to just write, create a piece of geo with control in it. Now, we're going to write in the arguments that it takes args, colon. And then we're expecting prop type. We know that this is a string. And we know that it can be a cube, a sphere, or a cone. Once again, this is all just documentation. This is just for whoever needs to use the function to get a little bit of help. It's not necessarily for the code to work, but it's extremely useful. And I would argue is a very, very useful habit to get into because he will make your coating life so much easier. Once you have documentation, it's honestly set you apart from anybody, from many people that code is so useful and it's very appreciated by other programmers. I do recommend you get into the habit of this. Sometimes it can be frustrating, sometimes it's extra work. But in the long run is very worth it. And return, well, we haven't really return anything yet. So let's not add the return value right now. Let's just leave it as it is. Great. So we know we're expecting a string and we know we have these options, cubed, sphere or a cone. Let's type, actually, sorry, we know that we default to cube. So let's just add that in hearing the docstring. Let's type cube e.g. let's just run this code. There you go. Excellent. Excellent. Now let's, let's create something else. Now you see how beautiful it is to have a function. You don't need to copy paste all of this code. You simply can copy this function, paste it in the line below. Now let's say we want to have a cone as well. That's all we need to do. Just run that code. Now you have a cube, now you have a cone. Excellent. That's all you need to do. You have a function and now you can reuse your code very easily. Let's say just for the sake of it, Let's create a sphere as well. Because why not? Because we can do it. There you go, sphere, cube and count. Beautiful. Now, for the sake, before we go for the sake of completion and for the sake of curiosity, let me run this code again. It's getting a little bit messier. I like whenever you create the probe is just overlapping on top of the previous one. What if we could just organism a little bit? Now you see we have this move function. Let's, let's, let's move to the prompts as we create them. So let's take this and turn it into Python real quick. I know these are a lot of arguments. Once again, like Maya's given us get the malloc command. I'm going to go ahead improvise a little bit. I'm just turn this into Python command. But before we do that, we need to know what We're moving, right? What are we moving? The control? Well, we haven't, we're not really returning the control. E.g. if I store this as in a variable called cube, let me get rid of Conan sphere, that simple print cube. None, of course there is unknown because we haven't returned anything. What would be a good thing to return? I think they control the name of the controller was created would be good for it to return so that once the brick is created, the user knows what the controllers so they can manipulate it. This is adaptation that I'm just making right now as we go. So what we want to return is actually the node of the controller West created, right? So control node, return the control node. Before we forget, since we're returning that, let's add it to the documentation. Return and we're returning actually. So let's just add the type is being returned. We know that is the name of the control. So that is a string name of control node created. This is just a note to make it a little bit more obvious. Excellent. So let me delete these two rays in here. Let's just run this code one more time. Control. I feel running again, control one. There you go. So we're now getting the name of this, these controls. Now, let me be more explicit. Let me rename this variable called cube. Let me rename it kube, control. Great. Now we can use the move function. So let's, let's take this and paste it down here. If this gets confusing, please just try to follow along. I am improvising here, but I know we could move this object once it's created. So we, let's say, let's say assume the S dot move. That seems correct. It's highlighted here. Now what are we moving? Well, let's move the Kube control. So let's, let's give that as a first argument. Kube control, no space. Now they, the value that is moved by is this, this right here. Let's just use that argument and see what happens. We're going to get rid of these dash r. I'm going to get rid of this dash 0 S. I'm going to use this W, D. I'm not very sure what it is. But remember we have long names and short names, so I'm just going to assume this is the short name of an argument. I'm going to remove the dash in it. And remember in Python, we use equals, so we're going to say w d equals. And as we saw before, these are three values. What do we do when we see three values? Well, we assume that it's a list. And a list is a group of items separated by a comma as we already. So let's apply that and that number by a comma. And then the next number will be a comma. I imagined that these are, these are the axis, this is x, y, and z, or z, coma, coma. And then close the list. We do not need the semicolon. Now we need to close the function so we can call it. Excellent. So move by this amount. Just bear with me while, while I improvise this, but this is good yours. You're seeing me try things out, life in a way. I'm going to get rid of that print. Let's just create this lag w d must be passed a Boolean argument. Okay, Interesting. Let's say w d equals true. Actually, let me, let me remove this. As I said, I was improvising. Let's try the same function without any of these arguments. Let's just pass in three values to this function. Let's see what happens. I'm going to pass in those values without it being a list. It's just basically three arguments. As remove that. There you go. That works. Okay. So I was completely wrong. I apologize for that and I apologize if this was a little bit confusing. I did not try this in advance, but I think it was good for you to see me try all of these life because it will be part of your experience trying things out until they work. Sometimes something might be a little bit, might seem obvious, but as you saw in this case, it clearly wasn't obvious. Yeah. So this move function, the way it's working, it's working on the last thing that we selected. Now we know that we selected that that control node before it was created. I'm sorry, after it was created. So that was being selected. So all we're saying is move the selected object, whatever it is. And this move function is taken in those three values, which will be x, y, and z. Of course, as we saw it has, let me do this again just to show it. It has more, more arguments. This probably means relative. This, I'm not sure what it is, but it's not important right now. So let's just take, let's just stick with this. Let me make this a nice round six. We know that it works. So let's say we wanted to create a cube cone. Let's create a cone. And you're probably thinking if we're not using this control anymore, we don't need it anymore. That is correct. That is correct. We don't really need it anymore, so I'm going to get rid of this. But I think it's still a good idea to keep the return value to be there they control. But anyway, this is, this is your tool, this is your function. So feel free to modify this any way you want it. At the end of the day, you are in full control of all of this is create a cube. Let's create a cone. Unless it's actually not move the cube. We're going to get rid of them. Move on the cube. We're going to create a cone, are going to move the cone. This all we want is distance between them. And then let's create a sphere. And let's move this fear twice as much as the cone. So let's move it 12 units, negative 12th units. Okay, We're going to finish with this. Let's run this piece of code. And there you go. Cube, cone, sphere, all far apart. Nicely presented, ready to be animated by you or anybody else. Great, So thanks again for bearing with me through all of these experimentation. Hopefully that was useful for you, and I'll see you in the next lesson. Thank you. 15. PyCharm - Install and Setup: I'm going to show you how to install unsettled by charm because that's what you're going to see me use for the rest of this course. Of course, you can use any other IDE or text editor that you want. If you want to use Sublime, be VS code note, but if you want, It's all tools. They all provide this end result. I do recommend PyCharm. It's quite nice for Python and it was really made for Python developers. Their website is going to be jet, brains.com, dash pie, same PY, charm. I'm going to provide a link to this, of course. Or very easily as well. You can just Google PyCharm again, PY charm. And the first link that you get should be there. The same result on the front page. Click the Download button. You're gonna have two options. Choose the community version. There's a professional version that you can pay for. But quite honestly the community integration is more than enough. So let's download that. Once the file is downloaded, just click on it. It will open the installer. Now click Next. Next. I recommend creating a shortcut, and I do recommend creating these associations. Dot p-y is simply the extension for Python files. So I do recommend using that. You can leave everything else uncheck. Click Next. Install. Okay, and at the end of the installed you will have this window. Click run PyCharm Community Edition, and click Finish. If you have this window, I'm getting this because I had a previous version, but just going to click, do not import settings. Click okay. And we should have pycharm opening right now. Once PyCharm opens, you are going to be presented with these window. You can simply open files with it. But I recommend setting up a new project because by generally is made to work with, with projects. While you also recommend taking this mall tour if you want. For now we're going to do the basic setup. So let's click on new projects. Their location. I recommend you using the Maya scripts folder. So that is going to be under UC drive, your users folder. Once again, see users, your username and you can follow up here the path that I'm following and then go to Documents. Now instead of documents are going to have your Maya folder. And then inside of that folder there's a scripts folder. We're going to be using that quite a bit. So let's click Okay on that. Down here you have a few more options. Choose new environment using virtual, virtual ENV. This is a virtual environment, but don't worry too much about that. This is something that pycharm will create, but we're not really going to use right now. So all of this leave us a series. Sorry, I did not mean to check that. Leave everything else. The series at the bottom. Create main PY, welcome script. I'm going to check that box just so that you can see something once we start a project, click Create. And PyCharm is going to start getting a few things ready for you. It might take a few minutes. You just give it some time. Once it's done, you're going to see this quick tour of what you're going to see. This here is your project window. And this is really the folder where your project is going to live. Let me show you what, what's happening here. If you go in your File Explorer, if you go to your Documents folder, go to Maya, and then scripts. That is what you're seeing up here, that is the same folder pie chart and created this folder called idea dot idea. You can ignore that. It also created this v, v ENV folder. You can ignore that. You can actually delete it if you want, but for now it doesn't really bother us, so we can leave it there. And because we checked that last box, we have this main.py file. The only reason I check that is just to create a Python file for you to look at. But we're going to be deleting that very soon. Once again, this is inside of your documents Maya scripts folder. You can create this project anywhere you want. But because we are going to be working inside of these scripts folder very soon, I recommend using this location. Once again, this is basically your file tree. It's just to help you navigate. This is Python file that we're going to delete very soon because we don't really need. So let's set up a couple of settings before we start. Go to the File menu, then go down to settings. You can also press Control Alt, Alt S to open the Settings window. Now there's two settings we want to update. We're going to keep it simple. First one is under the earlier section. Expand that on their general. The first checkbox that you see change font size would control those modes. We'll just check that box. That is just to mimic the functionality where we can increase or decrease the font size and my out with control and the wheel on the mouse. Now the next setting, go down to the Tools section, expand that, and then scroll down to Python integrated tools. Now close to the bottom there's docstring section rumor that a docstring is a string that we can put in a function that acts as documentation, hence the name docstring. Under docstring format, I'm going to choose Google. This is a standard that I like to use. Once again, it's always your choice. You can choose another format if you want, but I'm going to be using this one for the rest of these examples. Once you choose that click, Apply, click Okay, or sending that we changed. Let's test it out. Control on the wheel and the mouse grades makes it bigger and smaller, which is quite nice. Let me delete all of this code because we don't really need it. It's just e.g. let's create a function, just an example function, def space example, open parentheses. Let's add two arguments. Just because I wanted to show you what the docstrings will do. So argued one, just for the example, or two, e.g. close parentheses. Now notice how as soon as I open a parenthesis, pycharm completed the second parentheses, which is great. It's doing a lot of that work for me. So let's do that again on one or two colon I presenter once again, notice that Python gives me this indentation automatically, which is also quite nice. So now for the docstrings has been mentioned before, I'm going to type three double-quotes, 123. Notice how PyCharm also autocomplete that for me. If I press Enter it, auto completed those arguments for me. It gives me a space for my arguments and a section for my returns, and also a little bit of space here for our description, which is great. So give me a description to this sample function. We need to add the types manually. Pycharm is not smart enough to know that right now. So once again, this is just a convention that I follow. I'm going to say that this is a string. Let's say example string. Now this one, let's say that it's an integer and sample integer. And let's just say that it will return a Boolean. Just, just an example. This is not really something we're going to use. Notice how there's syntax highlighting as well. The colors are different than in Maya, but it's still nice to see at a glance what's happening. In this case. Notice that these arguments are gray. They might even be a little bit hard to see. That is because PyCharm is telling you that you haven't used them. Pycharm does a lot of very smart things for you that help you inspect your code. As you see down here actually, day on the right you have this marker. It's like a warning. And up here as well, It's telling you that there's two warnings. For the most part, don't worry about this too much right now, but it's nice sometimes to see what's happening. It's simply telling you that you haven't used those arguments. So let's fix that. Let's just print one of them in order to use it. Now you see that it's no longer grayed out. And also if you notice that as soon as I start typing, PyCharm is offering me a suggestion. In this case, it is the other argument that I haven't used. So if I press Enter, there you go. It does it for me, which is great. It auto-completes form. There we go. Same as before. It's closing the parentheses for me. So typing in PyCharm will be a lot faster. Let's just return something here. As soon as I start typing F, I know I want a Boolean, so it will offer me the faults or if I start typing t, he will permit true as a suggestion. Excellent. I'm not going to get too much into detail about PyCharm. There's a lot of functionality that you can explore or there's a lot of tutorials, a lot of documentation on the internet. But for what we need right now, this should be more than enough. Now before we go, let's do a little bit of cleanup. As I mentioned before, this file main.py is just an example. We don't really need it, so let's get rid of it. You can delete it either from your file browser or simply go here. When this Explorer here on the side, click the Delete key, it will ask you if you're short to do it, you can say Yes, there you go. We have a clean slate. So let's go ahead and create some Python files. 16. Creating a Module: Let's talk about libraries and modules. As we said before, a module is a connection, a collection of functions, and a library is a collection of modules. A module really the core of it is just a file. It's a file that contains code just as we have been writing so far. We can create our own and imported just like we do with my own CMBS. So let's go ahead and do that. In a file browser. Navigate to documents, maya, and your scripts folder. We use the same to create the same location to create the PyCharm project. So you should have that already from the previous video. So inside of here we can create our modules. Maya knows to read code from this location. How does he know that? Let's find that out first to know where my data is getting code from, we can use a module called sits, SAS, as in system. And in system there is a list variable that's really a list that is called path. And this contains all of the directory or the folder pads for my end. Python in general, for this Maya session is getting the code from. So let's simply print that. Great. So you will get this list with a lot of strings, which are just Directory Paths to folders that exists in your machine. They will look different for you because I have some plugins that you might not have, but a lot of them will be the same. E.g. this one, Maya 2020. This is a folder that is just for the Maya, Maya version. So if you have code that is meant to be used only in a specific version of Maya. You can put it here, e.g. we're using a more generic location because our code is meant to be more generic. It's meant to be used by all my aversions really. So that's what we're using at the end here we have that path. Let's try to make this a little bit easier to read. We can use our module called pretty print. It's actually called pwd print. We're going to say, Sorry, let me undo this from Pip print, import p, print. It's a little redundant, but that's how they decided to call it bright star. It stands again for pretty print. All it does is it will give you a more human readable result. So let's run this code that's much nicer. Instead of having this long list that we have to scroll to see, we just get it. It's still a list is still the same result. It's just printed in a more human-readable way. Once again, you can create modules in anywhere here in any of these paths. But for now, we're going to be sticking to documents, Maya scripts. You might be wondering what if I wanted to use a separate folder so that other people, let's say in your pipeline or in your team, you want them to access your code. We can do that. We can edit this list and add more parts to it. It's called appending paths. But we will get to that in another lesson. Let's go back here. Scripts, Maya documents, Maya scripts. Let's create a file. Right-click. Go to New, and create a new text document. Now let's give it a name that makes sense. In this case, we want e.g. prop underscore reg. Now the format for this file is TXT by default, but we want it to be dot p-y, as in python, dot p-y. Windows is going to ask if we wanted to change the extension, we want to say yes, excellent. So now you could open this file if I right-click and do open with Notepad, we can write code directly. Note that this is just a text file with a different extension. But we have already set up PyCharm, so we're going to use that. Now when we navigate into pi term because we're using that same location for our project. The file shows up here. So you should see that file which is empty, of course, Let's go ahead and create a function just to test it. So type would meet Deaf. Space, test, open, close parenthesis, colon. I'm going to skip the docstrings for now because this is just a quick test. Let's simply return a string. It's just say this is a test. Capitalize this. Excellent. So how do we use this? Let's delete this code. Let me empty the output window. Let's simply input this important rig. Let's run that. Let's see what happens. No errors. That's great. That is good news. Let's simply print that. See what happens. Print, prop Break. Excellent. So my selling you appropriate use a module coming from documents, manuscripts, proper egg problem there is called rig dot p-y. Excellent, That is our module. How do we use it? For? Well, we know we have a function in there that is called test. So let's just do dot test. And let's simply call the function. We know that it's returning a value. So let's store that value in a variable for now and just call it t. Let's print. There you go. This is a test which is the value that we're getting from our function in our module. Excellent, congratulations, you have made your very first module. As you can see, it's very easy. There is more to modules. What we're going to get to that very soon. The next thing we need to do is create something that we actually need, which is converting our proper code into module, which says you are going to say it's going to be as easy as basically copy pasting this into our module. So let's, let's actually go ahead and do that. Let me take everything from the import statement at the top up to the end of my function. I'm going to press control C. Let's go to our fabric module. Let's delete this code. And I am going to paste this here. Excellent. So we have bromide ion present. Yes, you notice that PyCharm is giving me an error. If I hover over it, I shall effect check the errors here. It's telling me unresolved reference to Maya, unresolved reference to CMBS. That's fine. That is because PyCharm doesn't know about the CMBS library. There is ways to get around this and actually acts as the code outside of Maya, but you don't really need that. So you can ignore that are ever for now. Everything else is exactly the same that we have before. We just literally copied, pasted it inside of this module. Let's go back to Maya. And let's go here. So let's say, let me delete this code. Let's simply create a rig, say prop, underscore rig, create crop rig. Let's actually print this directly and see what happens. We have an error, interesting attribute error module, meaning Propp Rick doesn't have the attribute create prop brick. Why is that? That is because we have already imported the module and we modified it after we imported, after we, we imported it, we added all of these. This code isn't your function after we had already imported into Maya. Maya, even though we're importing it again, maya tries to be smart and it sort of caches the module in its memory and just reuses that. So how do we tell Maya to refresh, update, or reload this module? Very easy. We can simply type, there's a function called reload. So type would meet reload, proper rig. But it's going to happen is it's going to force Maya to read the file again. And once I can cache, hit, Save it's its contents in memory. So we only really need to reload once whenever we make a change. So let's run this code. And now let me run this print statement again. Excellent. So it printed something a little cryptic function, create probabilistic, add some random number. This is good news. This means that it knows that there is a function. Let's actually use it. I know I'm kind of going over these a little bit slowly, but let's simply create a regular. We know that we need one of these three values. So let's go ahead and say, let's call this function. And passing the value that we already know it expects, let's create a cube. I'm just going to run that line of code. Excellent. This is all very familiar to you. Created a cube in, gave you the name of the control that was created. Let's create something else is create a cone. Same deal gave you the variable that was created, which is controlled wine and he created a cone. And now all of these lives outside of my yeah, we're not writing all of this code in the script editor. You have your own module backed up by your file system. So much easier to work with. And now all you need to do, we don't need to reload right now because we're not making any changes. All you need to do to call your functions is this four lines of code, clean, easy, and very, very scalable. Congratulations, you're done with your first project. Hopefully modules make sense. We're going to be using this much more moving forward. We're going to keep using modules, keep using PyCharm, you're going to keep creating functions. So all of these will become even more familiar to you. Once again, congratulations. Let's move to the next step. 17. Deep Dive - Functions: Let's take a bit of a deep dive into function. I've said before that you can consider a function as a container. A container of what? Let's just draw a container real quick. A container of instructions is being, instructions being code that is executed inside of this function. Now, this being a container, it also means that there's a scope. This is an important term. Scope basically means the limitations of where something can exist. And this will be especially important when it comes to variables. There are variables that will live outside of the scope of the function, and variables that will live inside of the scope of the function. This can be confusing sometimes for people, but we're going to see an example in just a bit. So the main thing that you want to know about functions is what they actually do. So, as we have mentioned before, a function can optionally take in an input. So imagine that this is just a container that is executing a set of instructions and it takes an input. Optionally, not every function takes an input, but you can make your functions that can input. Even more importantly, there is always, always an output and that output, if it's not explicitly defined, it will be a value called None. None is like a null object. But you can also use it as a way to check for the existence of a valid value, e.g. but always, always a function will return at the very least, none or something that has been explicitly set as its return value. So as an input like the proper vocabulary will be arguments. The arguments will be the input, and the output will be a return value. So when you're calling a function, and we're going to see this in code very soon. I'm just drawing this will have a bit of an overview like a top-down view of what functions do. Let's say you have a function called, let's call it my function. Just for the sake of example, in order to execute a function, it's calling coef function. That's the name of executing a function you say would call a function. We have to use parenthesis. And instead of this parenthesis is one of these arguments are going to go. If there's any arguments, if there's any input, then the parentheses will contain that input. Once you execute the function, you will get a return value. This is sometimes confusing to people because how the, how the functions return a value. Well, as soon as you call a function by providing this parenthesis, you are going to get a value in return. And usually you will want to store that into, into a variable unless you don't care about what it's been returned, unless you just want the function to execute. The instructions are a set of functions and then forget about it. But normally you will want to store that into a variable. Friend of mine told me that's easy to visualize these kinds of us getting a coin at the end of the execution. So imagine you're executing the function. And then you get a coin, kind of light when you play Mario Bros, get a little coin. We tried to throw coin. So very ugly coin, but I imagined that you get this coin as a result. And then you can deposit that coin into your variable. Once again, variables are simply containers of a value. So you can think of that That way if it helps you, you get this little coin once you execute the function. And that can be used however you want, normally as a value to store in this variable. So let's create a function. Let's create a function called example. Creating a function in Python is called defining a function. And the way to do that is by typing def D-E-F, define space. You can call the function whatever you want. And we're going to call mine example, open and close parenthesis. For now, I'm not going to have my function take in any arguments or any input, then a colon, and then press enter. Now notice how I got this indentation. This is basically four spaces. This is very important. This indentation is what determines what goes inside of the function. So for now I'm just going to write pass. Pass is basically saying skip any execution or do nothing. And I'm going to write a variable. I'm going to print something here. Actually, instead of writing that you say something. This is outside of the scope of the function. This path is inside of the scope of the function. Now, we talked about scope of four variables. Let's make that a little bit clearer with an example. Let's go, let's create a variable called bar. And I'm going to assign a number to it. Let's say ten. And let's create another variable. Let's call this inside, because it's inside of the function. And let's assign the number three, the absolute an arbitrary, but it's just for an example. Let's print var. Great. So we got the output of ten, which is exactly the value of bar. That's great. That is because that is outside of the scope of the function. Now what happens if we try to print this variable here? We get the name error. It says name inside is not defined. It's not defined because that is inside of this variable, this function, and let's print it inside of the function. Let's print this print inside. Let me delete this line. Now nothing happened. Course, that is because we haven't really cold the function. So once again, remember to execute a function you have to call it. Would they open and close parentheses? Let's do that. There you go. Now we're printing the value three, which is the variable from the variable inside of this function. Now, if you're curious, you want to see what happens. If you simply print the function without calling it. You can do that. And this is the same. Python will tell you that it's a function. It's called example, and it lives in these random, seemingly random number. This is called a pointer to memory. You say the function doesn't live in any file right now is just lives in memory for this session that you're working with. Do not worry too much about this. It's a slightly more technical detail. But know that you can, you can use this function as it is, but I'm calling it. But in order to get the value from it, you have to call it. Now I mentioned that functions always, always return a value. So let me store that value inside of this output variable. Let's bring that up. There you go. So this three is from this print statement down here. So let me, let me remove that print statement to make it a little bit less verbose. There you go. So we're printing the result of output. And that is none. That is because we're not defining any return values from the function. And none is the default value. As I mentioned before. Now, let's say that instead of returning nothing, we actually want to return this value here. Let's say we want to return that variable inside. Now we can either return three directly, which would be basically ignoring this, this variable here. Or you can return that variable. For now. Let's just keep it simple and return this value three. There you go. Now we're printing three from this output variable. Once again, that is, like I said, like my friends said, is it helps to imagine that you're getting that little coin and you're depositing that value or that coin into, into a variable. That's what happened in here. You get little value I'm putting in output. You don't necessarily have to put that barrier, store that value in a variable, as I mentioned before, you can simply print it directly. So let's do that. Notice how I am calling the function. And I'm just pointing that directly. And it's exactly the same result. That is because we're going to get that value. This is equivalent basically of doing this. Print three, because in this case, three is the value being returned by that function. So hopefully that's clear. Now, let's do something a little bit more complex. Let's, let's add arguments or inputs to this function. Let's add an argument called a and an argument called B. Let's return, let's call this function, add, the add function because we're going to add those values together. Let's say that the result will be a lesbian. Very simple and we're going to return their results. We can call the function. So what happens if we call these right now? You get an error of course, because it says the function add takes exactly two arguments, zero given. So now we have to give arguments. We have to give some values, let's say two and then two comma three. This is how you provide these arguments thesis in order. So argument and two will correspond to the argument a, and then three would correspond to argument be. Nothing happened because we are not storing that value. I'm going to say output once again. I'm going to print that out, but excellent. So now we get the result which is five, because it's two plus three. If we were to do ten plus three, of course would be 13. There is a different type of argument called keyword argument. What do we have seen right now? These are called positional arguments. That is because these arguments don't have a default value, they are basically variables. And once again, this is important. These are variables that exist inside of the scope of this function. Now, notice how aid doesn't really exist anywhere else outside of the function. A, it's kinda like an empty box. I think of it as an empty box and empty container. That is just being called a for the purposes of using it inside of our function. We're basically saying whenever you get a value, store that value inside of this box is container called a, and then use it inside of the function. And the scope of a variable or this argument that basically becomes a variable or is being treated as a variable, is just the scope of the, of this function. Once the function exits, exits, which is when we return or whenever we end the function or being there. They didn't patient, then that variable doesn't exist anymore basically. And also the name of this variable, of course, is up to us. We could call this one. And oh, e.g. we're adding numbers. So we can say, we could even say first and second. Then we would do first plus second. And the result will be exactly the same. So this is all up to us, are up to you as a developer to decide what to call this variables. So again, these are in order. Ten will belong to burst in this case, and three will belong to second. There's a different type of argument that is called a keyword argument. Is called a keyword argument because instead of passing the arguments in order, they're going to pass them by their name. So e.g. let's, let's create a new example. Let's say that we want the function to determine to know if somebody can drive or not, if somebody is old enough to drive or not. So once again, let's define a function, let's call it can drive. Make it a little bit descriptive. Now, we want to know the age of the person. We want to know what is the, what is the age limit to be able to like sort of a threshold, right, to be able to drive. So let's call this minimum age. And let's add a default value. Default value. Let's say that you need to be 16-years-old to drive e.g. then colon. Without the colon, you will get an error. So colon, press Enter, and now we're inside of this function. So let's say if h is less than minimum h, minimum h, We're going to say no. We're not going to say no, we're going to return false, which is a Boolean value in this case basically saying No, this person cannot drive bolts. Otherwise, we're going to return. Yes. Because it means that the person can try it. Excellent. So, so let's, let's call this function. Now, notice again, since I provided a default value for this number, for this argument, I don't need to provide necessarily a value for that. So let's say that somebody is 25. 25, yes, can drive. Somebody is three years old. They cannot drive. Excellent. Now, what if we wanted to change the minimum age? But you could change it here or you could. There's, there's two ways of doing this, since there's only two arguments. You could simply treat it as a positional argument, meaning in order. So you could say, okay, let's, let's make the new minimum age to be one, one-year-old. Now this person can drive, but we can also do this. We can say minimum h equals one. There you got the fact that we are adding, award it to the argument, makes it a keyword. Arguments. When you're adding a word, when you're trading arguments as a keyword argument, you can ignore the order, which might be a little bit confusing. But e.g. if I were to say H equals 20 and the minimum age to drive is, let's switch this to 22, e.g. now notice how h now is coming after second and second place and minimum age is coming in first place. But because we're using the keywords, keyword arguments, the functional will know how to assign it to its corresponding argument or variable. So in this case, it's false as expected. Now, if we switch this to 19, it's true as expected. But if we were to remove those keywords, simply leave them as positional arguments. Now they're flipped. Now, 19 will belong to H, and 20 will represent minimum H. Hopefully this is clear. If not, let's, let's try one more example to try to make this a little bit more clear. Let's make a different type of function. Let's make a function that takes all, that has all a fully optional arguments, meaning that all of the arguments have a default value. So let's define it. It's called Deaf. Let's say that it's a function that will greet people. I'm going to call it greet. Now. It will have a message. The message will have a default value called not cold, but we'll just say greetings, which is a string that says greetings. Now, it will take in a first name. The first name will have a default value of an empty string. So maybe we don't want to have a first name and last name argument that will also have an empty string as a value type colon. And we'd go inside of the function. Now we're going to say readings message. Let's just start a new variable. Message will be, will be a string. We're going to say, we're going to say open and close curly brackets space open and close curly brackets space open and close curly brackets. And then we're going to type at the end of the string dot format. Now what's going to happen here is each one of these curly brackets, it's going to be replaced by whatever we put inside of this dot format function. So we're going to say the first, the first curly bracket will be replaced by the message, the second one by the first name, and the third one by the last. And then simply returned message. Now, of course, we could, we could return these directly instead of having to do distort it in a variable that's completely valid. But I think it's a little bit cleaner to create a variable with our message. Let me make this window a little bit bigger. Create a variable that our message, and then return that value. So let's see what happens here. Now. Again, we have three arguments in this function. To make another variable called output. We're going to store the output of that function in that variable. I'm going to print it. So let's see what happens if we call this function without any arguments at all, but a passing in any arguments at all. We, it says greetings, excellent. So it does have some empty spaces because we added some empty spaces. But that's okay for now. This is just an example. What happens now if we pass in a single, a single argument? Well, that will be, that will take the place of the first argument here. Let's just say, say hello. Instead of greetings. There you go. Hello. What if we pass in a second argument? We're going to say firstName, let's say Josh. Hello, judge. Now what happens? Happens if we pass in a keyword arguments? So notice how this is the second argument. Barbaric actually colonnade lastName. We're basically saying, okay, use this value, this string in place or LastName. There you go. Now, it's hard to see because because there's only an empty space. So if we switch the default value of person's name to a simple dash. But we'll see what happens here. Notice how we're passing in our first name, our skipping straight into lastname. And then we have hello Josh. Great. Now if we pass in first name, even if we do it out of order, as long as we use the keyword for it. Say. Junior. I know. I know this is kinda backwards. The last name judge, is not a last name. But let's say that Junior will be his first name. Hello, january Josh. So notice how even though this is out of order, firstName, because we're using the keywords. It's taking the place of firstName and lastName is taking the place of last name. Now, if we get rid of these keywords by simply removing last name equals first name equals. We're back to positional arguments, meaning that the order will matter in this case. So let's run this. Now you see that the output is flipped. Excellent. I wanted to go back to return values because I mentioned that return marks the end of a function. And this is one of the most important aspects of a function and something that causes some confusion, confusion to people. So let me try to go back and do a lot and go back to my previous example. There you go, can drape. So right here we're using the if else statement to follow the decide to dictate the flow of this function, right? So just as a refresher, we were asking, can the person this h is the person under the minimum h. We will say, we'll say node cannot drive. Otherwise, yes, they can drive. So we can actually read this else statement is not fully necessary. The reason for that is because once you hit the return value, that is the end of the function. So let me run this again. Let's say that this person is 10-years-old and the minimum h. Let's just remove this argument and just stick to the, to the default value. So somebody who's 10-years-old cannot drive. It's excellent. So what happens if I return this else? David, and I simply remove the indentation in this return return statement as well. Well, the result is the same. This person cannot drive. What's happening here, we're basically saying, if this person is under the minimum age limit, returned false, once you hit this point, the function is done. I will not continue. Down here. Let's look at another example. Let me create a new function that simply says, let's just call it test for now. And they will take and number. I'm just going to say the argument num colon, enter the function. We're going to say if num equals, equals one, Let's, let's return. Let's return one. Just a string that says one. If num equals, equals two, that's returned. Otherwise, let's return. More. Like in this dysfunction is just an example, doesn't make a lot of sense. So let's run this. Let's say test. Let's first one and I'm going to print the results. I'm not going to store it into a variable, I'm going to print it directly. So 12, sorry about that. If I say 20, it will return more. Excellent. So this is the equivalent of saying if else, if else return more. But once again, because the return statement marks the end, basically exits the function. This last else is not necessary. This is very powerful because it means that you can, you can dictate the flow of your function this way. We can have a lot of conditions, a lot of instructions. And once you hit a certain condition, you can say, okay, that's all we need. Exit the function to return a value, any value that you want, which is e.g. here we're returning different values. Here. You don't have to return the same type of value. You could even return a NAN, e.g. and in this case the 20 is not either one or two. So returning none. Now that is, once again, that is your choice, that it's up to you. Hopefully this made sense. Functions can be a little bit confusing and especially return statements can be a little bit confusing to people. But the more you use it, the more familiar they will become. One last thing before we. We'll wrap this up. And that is a very important topic, which is documentation. It's docstrings. Once again, as I mentioned before, a docstring is basically a string that we can use inside of a function that acts as documentation, hence the name dark strip. Docstring is a triple quotes. They can either be three single quotes just to open them and close them. Or three double-quotes, I'm going to stick to double-quotes in this case. Now, what docstrings do is provide documentation, as I already mentioned, I'm going to provide a simple explanation. Here. Is a description, sorry, not an explanation. This function. And now it's something that is very important to include in your doc string. No matter what kind of convention you follow in docstrings is very important to provide. Their arguments. Or convention that I like to follow is by tapping args colon. And under that, we're going to provide the arguments that, that make the optics function in this case is this num argument. I know that num will be an integer. And I like to provide a simple description for the second year, the good side, e.g. and then if the function is returning, something I like to also add is returns. Just like a section on the right side, the type that is being returned in this case, we know that it's a string. So let's say it will return a string, string integer passed in. This is just a description of that return value. Now, writing the docstrings can be a little bit tedious. A lot of people don't do it. But believe me, it is extremely important and it will benefit you so much. And just the fact that you write docstrings pool. If you would like to continuing this scripting or programming career, it will help you immensely in your career. So what is the benefit of those docstrings? Well, one of them is that at a glance when you're looking at a function, you can actually just see the documentation when you comeback to read your code a year from now. You know what's happening when somebody else wants to use your function. They know what is expected as an input and an output. But also, even if you don't have access to the function itself directly, you can access, even if you don't have access to read the source code or what's inside of the function. Let's imagine that we're down here. We don't really know what's inside of test. There's a built-in function in Python is called Help. You can pass it in your function without calling it. Notice how I'm not putting the brand, this, I'm not calling it just passing in the name of the function. Let me clear my output window. Let's run this. And the result of this health function is exactly the docstring. So it says test. Here, it says Help one function tests in Module main. Don't worry about what this means. It just means that y actually just means that there is no module. We're just calling this in Maine, which is kind of the default place for executing code. Now the name of the function is tests. I takes in an argument called num. This is the description that we put in at the beginning, and these are the arguments and the return type that we provided. Now, you can see how useful that can be if we didn't have this put into, have the docstring and we run the tests were run help in test. Let's see what happens. It says Help on function tests and more domain gives you the name of the function, the argument that expects, and then nothing, which is not useful at all. It's way more useful to have that information for you or somebody else who might be using your function. So it's a great habit to get into a little bit more time-consuming, but it's not really that much more time. And yourself and many other people will thank you immensely for writing documentation. Hopefully, functions that are a lot more familiar to you. They are extremely useful, extremely important in programming. And they will make your life so much easier once you get more comfortable with them. 18. Project 2 - Pose Transfer Tool: Intro and Breakdown: Welcome to your next project for this one, and we're going to be creating a tool that will help us translate post is between characters. Let me give you a demo of that. By the way, for this demo, I'm using this rig called Morpheus. This is a free rig, is an amazing Rick I was created by Judge burden. You can get it from his website. Judge horton.com slash projects slash Morpheus asap. It's an awesome Reagan, highly recommend it. I will be using that for this demo. But you can use whatever you want. These tools should work with different types of breaks. We'll be using these because Jewish was kind enough to let me use it for these projects. As you can see, I have three characteristics, three rigs, they're all the same rig. They look a little bit different. Morpheus allows me to modify the rig, create different versions of the character, but it's the same rig. They have all been referenced into the scene. And this one here has already been posted. So let's transfer that pose to the other two arrays. Let's select, let's say these controllers and maybe the head as well. So let's transfer those values. Just supposed to these other characters. I'll select one of them and run the two. There you go. Now they both got the pose. Let's say I want to transfer now, let's make a change and this character here, just ahead. So I will select, I will say I want to transfer this into these two characters. It doesn't matter what I select, because I know I will transfer this, the controllers that I selected on the first character to the two other characters I have selected. And there you go. Excellent. And of course, it works for a single character as well. If you wanted to. Let's try that real quick. Excellent. So that's two. Now, we need to do a little bit of setup. Because once I mentioned before, these are all the same rig. The reason this works and this is a very common way of working with these type of tools is that the tools are made at or around the rig. And when you're working at a studio or a project with other people in a team. Usually the risks are the same. The geometry may be different, the character may look completely different. As you can see that between these two characters, they're very different. But the controllers in the rig are mostly the same. And that is what's, what's happening here. This is how the tool is able to transfer animation or a post which is just values from one week to the next. Because we're working in rent references, we have namespaces. As you can see, these controllers, every control has a namespace. This character here, and then being Severus. The next one here is Minerva, and the last one is Luna. So let's go ahead and create this setup that you're going to need for this, I am not going to go over too much on the concept of namespaces. If you're not familiar with references and namespaces, I recommend that you do a quick tutorial online because I will be very important and it's also very useful to know as an animator or how to work with references and namespaces. So let's go ahead and do the setup. I'm starting from a fresh scene in Maya. This is a completely new scene. When you go to the File menu and the reference editor. Once again, for this to work, we're going to be using references because the rates are all the same, so we need to reference them and put them behind that namespace. I'm going to play a click the plus button. And I already have these rates in a folder. I have the female version, the male version. So I'm going to be using those. It's already here. But let's say the first one, the male one first. The order doesn't matter. It's all the same. I'm just going to do this one first reference that interest. Now, Maya gives me the namespace automatically, which is more fusel underscore mail. You can leave that as it is if you want. I'm going to call it Severus. It's a little bit more fun. And then let's do the other one. Thinking a little bit of time. It is a heavy rig. But again, it's a really nice Rick. Now, the next one, let's call it Minerva. Once again, the namespace doesn't matter. That's up to you. I'm just choosing to do this to make it a little bit more interesting. Excellent. We have the two rigs, Severus and Minerva. Just going to do to right now. But this tool will work with two or more rates up to you. I don't have the texture is loaded, but that's okay. I'm not going to worry too much about the textures. We only care about the controllers in this case, for whatever you decide to use, you can load the textures or not. The tool doesn't care about that. I'm going to use the default material just to give the shading evenly. And for some reason there are black. So let's turn on double sided two-sided lighting. There you go. They don't look as pretty, but Tool. It's just going to be working on the controller, so that's okay. There you go. Let me move this up. And let's just do a quick test on the tool. Let's go from Minerva. Just separates. There you go. It's working. Excellent. This is the setup that you need. Once again, if you're not very familiar with namespaces and references inside of Maya, I highly recommend you do spend a few minutes just learning about that. It's not very complicated, but it's very useful. So let's go ahead and jump into the code. Actually, before we jump into the code, let's break down what's happening here. Let's see how, let's see the steps that we're trying to automate. So let me bring this restricts together. Let me zero these out. Okay. What does it mean to transfer posts? Let's take this chest control, e.g. so as you can see, they're both the same. So rig spine, zero skin, shoulder, shoulders, and it's a little bit of a long name, but the important thing is that they are both the same. They are just behind this namespace. So for these control, its servers and through this rake the namespaces Minerva. So let's, let's change the rotation on this character. Let's just say 90. Perfect. In order to transfer this pose, we basically want to apply the same value to this controller over here, which again is the same controller, is just under a different name space. How would we go about doing that? Let's open the script editor. Let's once again ask Maya to give us some valuable information. Once again, I'm going to change that value, which is rotate x and see what happens. Okay, Perfect. My eyes telling us that there is, this is the command that it's running, said adder. Then the name of the controller, which is the name of the object, dot the name of the attribute. Okay, So this is the full name of the attribute. That means that the rotation x and that controller has this name, which is a controller, which is a namespace, a colon. Sorry, I didn't mean to do that. A namespace colon, the name of the control, and then dot and then the name of the attribute. And there is this set R command. Let's translate this into Python. I shall, I need to import this library. So from my input CMBS library will do CMV as dots set adder. And then in-between parentheses. Remember that the sub here is smell code, so we only use it as a guide, as a blueprint. We don't need the semicolon in Python. We're going to open and close parenthesis. The first argument will be the name, the full name of that attribute. And the second argument will be the value that we want to set on that attribute. So there you go. This is the basis of our tool setting and getting attributes. Let's set this attribute something else, let's say 80. Let me do that again. So you can see the changing here. I'm sorry. I know this looks very painful. I'm sorry, I'm Nerva. Please forgive me. Let's go back to zero. Let's give her a relief on her back. There you go. Now, let's let's do the same by just changing the namespace. Now the other namespace is separate, and let's set it to 80. Let's run this code. There you go. As you can see, all we're changing is the namespace. Now, we need to know what the value from one controller is. We able to apply it to the next controller, right? Let me zero this out. Once again. Let's bring them back to, back to the default pose. Let me apply a value in this rotation. And so it happens, there's a way to get this value and it's very intuitive. It's called Get outer. We'd work similarly as I said, adder, but of course we're not setting an attribute, so we don't need to pass any extra arguments. All we do is get adder. Say we want to get the attribute, the value of this rotation attribute, Minerva. So we have to change this namespace Minerva. And we'll say rotation equals that. Let's just print this out. Let's see what happens here. Let's print rotation. Let me make this a little bit bigger over here and clear my output history. Okay, there you go. Good. Adder with the full name of the attribute will basically give you the value that we're looking for. As you're probably guessing, all we need to do is then set the attribute. I shall, I'm going to call this rotation x because that's what we're doing. Great. So we're storing that value in rotation x. So all we need to do is applied the same controller, same attribute on servers. There you go. That's, that's basically the basis of our tool. It's very simple, which is good, simple, It's always good. We just need to do all of these programmatically, meaning that we need to know what the character is we're selecting, what controllers we're selecting. We need to get the attributes from that, from those controllers and apply it to the next character. Hopefully that makes sense. Now, let's actually go ahead and have a look at the code. 19. Namespaces and Lists: We're going to be cutting outside of my mainly in PyCharm. If you haven't seen the pattern video, please do unless you are using your own choice of a text, cetera are, in which case it's fine. But in the PyCharm video, I go over how to set up the project. We set up the project in your documents Maya scripts folder. And the reason for this is because Maya knows how to read code from that folder already. So we're going to be writing our modules or code in here so that it's easy for Maya to access. Let's try with our module. I'm going to create a new file, that simple text file. We can do this inside of PyCharm of course, but I already showed you this manually first. This is what we're going to be importing. I'm going to call it posts Transfer dot instead of dx, dy dot p-y or Python. Windows will ask me to confirm. I will say yes. Now if I go to PyCharm and that's not pie chart, that is pi term. You will see that file show up in here. And of course it's empty. But now we can start coding. What do we need to do first? Well, the essence of the tool is basically, like I mentioned, we're working a lot with namespaces. Little is basically saying transfer opposed from a namespace to another namespace. So we need to be able to get the namespaces that we want to work with. That is one of the first steps. To be able to do that. We need to work with selection because we're getting the namespace is based on selection. I select one character and then the next one, and then transfer the selected posts from the character one to the character. So let's get, let's work on getting that namespace first. And then we will work in getting this election. How do we get the namespace? If you select something? You will see here in the channel box that are even here like my already outputted this. The name of the controller is a string. Or we can think of it as a string. At least. It contains the namespace, it has a colon, and then it contains the name of the controller. So let's put this in a variable. We basically need to get the first part of this. And to do that, we can split the string by that column. We can do control dot split. This is a function that comes with strings. We can say control that splits. We want to split it in the column. Let's just print this. Go ahead and directly print this. Great, There you go. As you can see, we've got the first part and then the second part. Now it used to drive the example home. What if we split in the underscores? Well then you get more parts. You get everything that is before and after each underscore. But for our purposes, all we need is to split it by the, by the colon. So we get a list, the result of this split function. It's a list with all of the parts that were split. And the one that we care about is the first part. As I've mentioned before. We will be learning about list in this project. Our list is just a collection of objects, of items, anything you can, you want. It can be strings, can be integers, can be functions themselves, can be anything you want. And we start counting these items at zero. So this first item, so let me actually go into paint for this. You can see a list. You can, you can kind of imagine a list as this. It's also called an array. By the way, some people will call them array, but we will be calling them lists. So imagine this is a list. Let's make it simpler. This is a list with four items. Too. 3.4. We start counting at zero. This is zero, the element, this is one, this is two. This is true. So what if you want to get the second element of a list while you use index number one, which one of these are called indexes? If you want to get the first element of a list, you use index zero. If you want to get the second element different list, you use index one, and so on and so forth. So let's do that. The syntax for getting an element of the list, it's another open and closed square brackets. So let's say we want to get first one, which is one. Let's run this and we'll get one. Excellent. Let's say we want to get the third one. Once again, we're starting at zero. So we will get the second index. Excellent. Let's, let's save this split into a variable. Let's call it control parts. And now let's print, let me delete this. Let's print that variable. Excellent months again is a list. Now let's get the first item, which once again, score brackets, zeros, index. And that's what we're looking for. This means that our namespace main space will be that first index. We can combine all of these. This way. We can say the namespace will be first item of that split. Great, so that's how we get namespace. Now, what we need to get before we are able to get namespace is these controllers based on the selection? So let's go ahead and do that. 20. Selection and "for" loops: Selection. We need this CMBS library again. So let me put that on my impulse him, yes, there is a function called ls that stands for list. Notice in a Python lists like we have been seeing so far. But I've seen a function that will give you a list of objects in the scene, e.g. if you want to get all of the cameras in the scene, you can import library. Let's run this. You see you get the cameras in the scene from camera respective outcomes. With some cameras, you can get lights, e.g. which I don't have any lights at the moment. So that's an empty list. Let's expect it. You can get selection. Since I have nothing selected, I have an empty list. But as soon as I select something, let me clear my output window. As soon as I select something, I get a list with this electric selected objects. And as you can see, this is the name that we're looking for. So if I select more than one, say this arm controls me, just print. There you go. So I get a list of all of those controllers with their namespace and the controlling. So now we can start applying this technique to extract the namespace from all of the selection. So let's go ahead and move into pi term to start doing this. In our post-transplant module, the part that we just created, we're going to create a function def space defined the function, get selected. Namespaces, open, close parenthesis, column. But first of all, let's start by getting the selection. So we can store that selection as I showed you before in a variable called selection. Selection will be equals. And actually I haven't imported my library from Maya, important CMBS. These will be shown in red. That is because Python doesn't know where to get the CMB as library from. But it doesn't matter because PyCharm is not running the code. In this case, maya will be running this code. So as long as Maya has access to this, that's fine. So selection equals Cmd S dot ls. Selection equals true. This will give us the selected controls. So what happens if nothing is selected? We can check the length of a list. So once again, this will be a list. We can check how long I listed with a functional callback function called Len L-E-N. Len selection will basically tell you how many items are in a list. Now, I mentioned before that you start counting at zero. But in the case of length, it's actually telling you the number of things inside of the list. So it will not be counting at zero if the list has three elements in it. Land, we'll give you three. We want to say if len equals, equals zero, meaning that is there's nothing inside of the list. Well, we cannot get any namespaces from nothing so we can't continue, so we can return here. But we're making a function that will return namespaces. So let's keep consistent and return an empty list. Before we continue, let's set the expectations of this function in the docstring. Once again, I'm going to press 33 double-quotes. We'd say I'm going to write a small description for the function. Get list of selected. Now get list of namespaces for selected race. And it will return a list. Excellent, So if there is nothing selected, we return an empty list. Otherwise, we're going to have to iterate through that selection to be able to get the namespaces we just saw. How do we do that? We need to use a for loop for this. The syntax for this is for, let's call it item. In selection. We're going to get our code that we just had. We had up here. Let me paste it here. Actually, let's call these control. Let's call this control instead of item. In which case we don't need this here because this is hard-coded. We don't want to work with that single control. Work in anything that was selected. For controlling selection. Get the namespace, which once again we do by splitting the control by the colon and then getting the first item. And now to store our namespaces, we're going to start an empty list. Namespaces equals an empty list. So how do we add this namespace to that listing namespaces, let's call this actually in space list. A little bit more explicit. You can say namespace, list, dot append, and the namespace. Now something that you're probably noticing is that lists can be modified. This is called mutability. List is immutable, that means that it can be modified. There's another type of data, that data type called a tuple that acts like a list, but it cannot be modified. It's immutable. But we're going to touch on that a little bit later. For now, it's important to know that at least can be modified in place. So every time we're appending a namespace here, we're actually modifying this list up here. So once that is done, Let's return that namespace list. Let me add some comments. Let me say here we're going to extract namespace from control. Here we're going to loop over selection. Now this is very redundant, but it's the first time we're looking at for loops. So we're going to add some comments and then add namespace to list off namespaces. Once again, very redundant. You don't have to add this comments, but just to be a little bit more explicit. Excellent. So let's see what this function is giving us. Let's import these posts transfer module and simply use our function. So let's go back to Maya. Let's open a new Python tab. And now let's import pose, transfer. Me, make the font a little bigger. Let's simply run that. Now if I highlight the name of the module and I run it, you know that it's, it's, it tells you that it's coming from that location documents, Maya script, suppose transfer, excellent or on the right track. Let's simply use our function. Let's store the result fit into this variable. It's going to call it post transfer. One, I'm going to call it, we're going to call the function from post transfer and it's called get selected namespaces. So let's first test the case where nothing is selected. Sorry, I didn't print it. So let's print name spaces. Great, an empty list as expected. Now, if I select one control, we get that namespace. If I select two characters, let's select the first one and the second REG. Okay, We are getting all of the namespaces, but they are repeated. So this is, this is not necessarily this is redundant. We have a bug. Let's see what's happening there. So let's say I select one control on each character. Now this is the result that we want. What's happening now is that we are getting the, the namespace multiple times per character. So let's go ahead and fix that. Let's go back to PyCharm. That's true. So we are appending the namespace every single time. But what we want is to only appended to that list, the namespace list if it's not already in the list. So we can simply say if not namespace in namespace list than you intended, and then you are actually working. This is correct, but this usually more, more common to write it this way. If not, it's namespace, not in specialist. This is a very simple check to see if something is inside the whole list. If it's not, then we added, excellent. Let's go back to Maya. Let me reload this module because we made some changes. Let's reload that. Let's run this code once again. Excellent. So now what happens if we select multiple controllers? You still only get a single namespace per character. Amazing, That's exactly what we want. It's selecting some other things. So I'm going to turn off everything but the nerves curves for selection. Great, that's exactly what we want. So our first function is ready. Docstring is ready. Great, Let's move on to the next function. 21. "for" Loops Overview: Before we move on to the next function, I do want to clarify what the for loop is doing because that is a new concept and we're going to be using it a lot more. So it's going to be very familiar to you. But let's have a quick look at what is actually happening. Once again, we had our list. Let's create an example list. I'm going to call it test list. Now, I am avoiding calling this list. It's valid. You can call this variable list. But at least it's actually a built-in type in Python or a function, which means you can turn something into a list. By doing this, e.g. you can turn a string in tallest, bypassing it into that function. That's why I'm avoiding overriding this, because if I call my variable list, I will overwrite it and it's usually a bad idea. Maybe not a bad idea, but it's not a good idea to override built-in types in Python. So I'm going to call it this list, and it's just going to be a simple list of strings. We're going to say same example as before, 123. Now, the for loop, this will loop over these items or iterate over each one of these items. We're going to say for number in the list, list, print number. Let's run this code. Excellent. So as you see, it's simply printing each one of those items inside of that list. Now, number is a variable that I chose, that you choose the name of this variable. And you're basically saying for each one of these elements, so this list store that in this variable number. And then inside of that for loop, you can use that number. E.g. we can, we can do some integers, Let's say 235. Go bring that up. But we could also say print number times two, e.g. these should print or 6.10, right? There you go. So that is what, what's happening. We're basically saying store, go through each one of these items stored in a variable, then that variable is accessible for you inside of that for loop. And we're basically doing that from the selection functioning the CMBS library. To complete this example, let's go ahead and use that preceded the S wave. Let's just leave the variable number. It's not a great name for what we're using here, but it's valid. There you go. So as you can see, where it simply iterating over each element that will select. Hopefully that's clear, but don't worry, we're going to keep using for-loops throughout this course. So let's, let's move on to the next function then. 22. Getting Attributes: The next thing we need to do is get the attributes that we want to transfer, right? So this tool is basically saying get the selected controllers, get the attributes. We also are going to have to store the values for each one of those attributes. We're going to get to that next. But first we need to get the attributes for it so that we can transfer that to the next week. So how do we transfer attributes? Okay, well, let's, let's work with this control again. Let's put it here. Let's say we're working with that control. Now, the name of the node includes the namespace. That's important because that's how Maya knows what character it is. Control belongs to, what is controllable lungs to by itself. This is the name of the base, the base name of the control. But inside of the scene, it lives under that namespace. So it's important to keep that in there. There's a function called list adder. And let's have a look at what it does in the documentation. I have it right here. List adder, you can simply look for. It says this command lists the attributes of a node. If no flux or a specified, all attributes are listed. Okay, let's go down to the examples. It looks like if you don't give it the name of any node, it will list the attributes of the selected notes. But if you give it the name of a node, it will list the attributes on that node. So let's try that out. It's called the control node. Node. Let's call it store this in a variable called attributes, and let's print that variable. Okay, great. So this is a lot of attributes, a really long list. Some of them are what we see here breadth course. We probably are going to see rotation, translation. But we also see a lot of things that are probably inherited from something else. Now, there's actually limitations, of course are a few constraints we should worry about e.g. we don't want to get attributes that are locked. E.g. we only want to get attributes is that we're able to set. So if you're going to the documentation, you will actually see that there's a few options you can pass into this command. You can get rid only act attributes. You can get attributes that are kibble, that are locked. And you can get the ones that are. So here, settable. Let's have a look at what these will give us. Settable equals true. Okay, that doesn't seem too different. I showed, I know it's different. Starts with cashing instead of message. That's still a lot. Smaller list as you can see, still a lot and there's things that we might not care about. So how about we focus on the attributes in the channel box. For that, There's another function. It's a function called CMBS, totally just made a bowl. Now let's look for that in the documentation list animatable, how I found this bonus to look into forums on the internet and looking for the best way to get attributes from the channel box. I did that work upfront for you and now I'm just giving you the answer. Otherwise he would take a long time. So this command lists the animatable attributes of a node, allows filtering by the current manipulator or node. Okay? We probably can use this asset is without any arguments. We don't really care about the shapes. We don't care. Type. These flies off solid, no longer supported. We don't need to worry about that. Only affected by the current manipulator. Definitely don't want to do that. This means that if you're manipulator is in translation mode, you're only going to get the translation attributes. We definitely don't want to do that. Let's simply passing this node and let's replace our list adder with our list animatable function. Let's print that out. Okay, this looks very different. To make it a little bit easy to read. Let's actually look over this list. So let's say for otter in attributes, print hotter. Now this is just for visualization purposes. I just wanted to see the list instead of having the list flat. Yeah, there you go. There's presented as an actual list. What we're seeing here, we're actually seeing the full path of these attributes or the full path of the, of these controllers. So as you see, it's starting these all anime node. And then it goes into rigs group. And then there's another group called butter regroup all the way down. At the end of each of these, there will be the name of the controller. And at the end will be the name of the attribute. This is what we actually care about. And this is what we see in the channel box, translation x, y and z, rotation and breadth or breadth. That's great. That's what we actually want. We don't really care about all of these. This is honestly very verbose and slightly annoying if I'm being honest. So let's clean this up. If you look at this path carefully, we can do a few things. But something we have already learned how to do is how to split strings. So we can simply split by the dot. Notice that there is no other dots in here. So that will be saved to do if there's a door in your rig. That might be a slightly different case. In that case, we can still get the last item of the string. Okay, let's, let's imagine that that is the case. So let's work with this example. Let's say we want to get the name of this attribute from this really long list. Alright, so normally work on a separate that Python tab and paste it here. This is all outer. Name will be equals to these really long string. So what happens if we split by the dots? Let's go ahead and do that whole. Let me make this variable a little bit shorter, just poor. To make it easy to type. Full. Adder dot split. That instead of splitting by the colon, we're going to split by the dot. We're going to call this parts. We're going to print those parts, which as we know, it will be a list of the parts before and after any doubts that it finds in the great parts. And let's actually see how many parts we got. We can print the length of that list. Len parts. Alright, there's two of them. So there's a really long first part here, which is the name of the controller. Then at the end, this is the name of the attribute that we care about. Now, I did mention what happens if there's another.in this name for some reason. Let's put a.in here just for the sake of example. Alright, let's run it. Now. The length of our list is three. That is because of course we find another dot and other period. And now we have three parts. But still the one that we care about is the one at the very end. If we assume that there's no, there's only one.in the name, we can simply say, okay, well give the name of the attribute will be the second. What happens if there's more than one? Well, we need to get the last index, right? How do we do that? There's two ways of doing that. One way, we already know how many items are in the list. So we can simply say, okay, there's three items we want the third item because we start counting at zero. We can actually just say, okay, we want the number of items minus one. Because the third item, if you're counting at zero, will be number 20 will be the first, 11 will be the second, 12 will be the third one, right? So we can use this to our advantage. Parts. Let's say parts number will be that the length of the parts string list. And then the attribute name. Let's just call it adder. The adder will be arts and the index number will be the last index possible, which is the number of, number of parts minus one. So let's print utter. There you go. That's it. That is what we care about. Now, there's an easier way of getting this. And that is by simply saying minus one. Without even knowing how many items are in your list. We can simply say, give me the index minus one, which will be the last index. How is this working? Let's go ahead and break that down before we continue. As I showed you before. Let's imagine again with four items as I showed you before. The first item is index zero, the second item is index one. Third one will be two. Forward one will be three, etc, etc, etc. There's indices as well in the negative. So the very last index in the list will be index minus one. Sorry, that is how backwards index minus one. The second last one will be minus two. The third last one, of course minus three, and so on and so forth. In this way, without knowing how many items are in the list, you can simply say, okay, give me the last one. Or if you want me the second last one. So let me just show you an example of that. Let's go back to Maya. Let's open a new Python tab. Let's make a new example list. Let's call it example. Let's say 123. As usual, we are used to this example already. So if I say example, give me the zeros index. Well, let me find that. It's print. The zeroth index of example will be one. Let's bring the third. The second index will be, which is equivalent to the third item. That is three. Now if I say, give me the last one, index minus one, that is the same thing. Now give me an index minus two. That's equivalent, the same one. So that is another way for you to get the last item in this list, no matter how many dots you will have here. It's a little bit more failed proof in a way because we know that the last part of that will be our attributes. There you go. Let's very quickly put all of these together. I'm going to copy this piece of code. We already know how to get those long attribute names from a control. And I'm going to paste the rest of our code here. Make this bigger. Okay? We know that attributes will contain these really long names. And we already know how to get the simple name or the short name of that attribute by splitting its parts and getting the last split part. Okay, let's, let's, let's combine these. As we said, we don't really need to know how many items are in the list of the sprint. We can do all of that inside of this for loop. Since we already have this code here. Let's put this code and paste it inside of this for loop. Now we know that the attributes list will have these long names of attributes. So Let's call this full adder. And then verb are basically looping over each one of those really long names that we got from the list animatable function or splitting it into parts. We're getting the short name, which is the last part that we split, and that is the name of our attributes. So let's be more explicit. Let's call that name. Remove this. Okay, there you go. That's a little bit more clean. We can turn this on name, I don't names. We can actually create a list of these other names same on the same way with the namespaces. We can say adder names, dot append. If you're wondering, do we need to check if we already have the attribute in other names? We could. But these, this function will give you a list of unique attributes, attributes, and a controller, usually unique. So I wouldn't worry about checking if they are already in the list or not because there's a very good chance they will be unique. Okay, let's, let's, let's, let's see the flow of this. Once again, we started with a controller. In this case we're hard-coding the name of the controller. We get those long names of attributes from the listening measurable function. We start an empty list to store our short names. Are they just the simple names of the attributes? Now we iterate, we loop over all of those long names, and then we start splitting. Then we simply, once we get to the short name that we care about, we added here, we appended to that list. And then for now we can simply print that list. There you go. That is the list that we care about. So let's go ahead and turn this into function that doesn't hard-coding the names, but we'll take in any arbitrary control node and gives you the attributes from it. Let me copy this and let's put that into our module in pie chart. But still Clara, the final function, get outers from node. Okay, let's paste that inside of this function. Since we want to be able to do this for any given node, we can actually use a pass that in as an input. Then that means that we don't need this anymore. We will pass in the control node. We will get the attributes. Now, what happens if there are no attributes? Well, that's a good question. So just like we're doing here, if there's nothing selected, we return an empty list. Let's go ahead and do that. If len attributes equals equals zero, we will return an empty list, same as we did with the namespaces. Sometimes these functions in Maya, they, if there's nothing to return the return a non-zero value. Which isn't great. I, to be honest, it should be consistent. Return value should be an empty list. But sometimes that happens. So if that is the case, and none will not really will fail if you try to do this. So we need another alternative. How do we check for something that is, that is not there, something that is empty is something that is false. Let's say what we saw before. We can say if not attributes, return empty list, this will work for both if this is a non. So if, if this is, if attributes returns a non, this will work. But also if attribute returns an empty list, this will also work, which is great. And coincidentally, this is an empty string, e.g. this will work. So these are, the syntax is actually very useful because you don't have to know exactly what type you're expecting. You can simply say, if it's an empty type, then do something. What I'm saying, if not empty, then do something. That means that we could also do the same here. We could be saying, If not selection, then return an empty list. Both, both ways on a pilot, if you know for a fact that it will be a list, you can use Len. Otherwise you can use not the variable. We know that selection is indeed a list, so we're not going to worry about that. We're going to leave that as it is. Once again, we get the attributes from the control node. If there are no attributes, will return an empty list. Otherwise, we initialize an empty list for storing our attributes. We go ahead and split the names of those, those attributes and we simply get the name that we care about. You're probably thinking, could we combine these two lines into one? Yes, we could. We could simply say, get the last item directly from that split and say the actual attribute name will be that. And remove this line altogether. There you go. Now down here, instead of printing, we want to return that list of attributes. And before we go, let's add our docstring. Get attribute names from node. Actually, sorry, this is from a single node, not know it's plural control node. We're expecting a string. Open node. We will return a string, no, sorry, a list. Or return a list based simple attribute names. Let's actually say short attribute moons. That's what it is. Alright, perfect. Let's, let's quickly test our function. We imported that module here. So let's reload post transfer once again because we have made some changes. And then hold it. Get errors from node, get arrows from node. Let's passing this node. For now. Once again, we're expecting a string and it's called the adders. Let's print. And there you go. That's what we were expecting. Excellent. 23. Storing the Pose - Intro to Dictionaries: Now that we're getting the attributes that we care about, the next step is to get a mapping of those attributes with the values that we want to set on them. Because once again, the goal of this tool, and let me just set some values and this controller, the goal of this tool is to get the attributes on the controllers. We selected the values of those attributes and apply those attributes and those values to the equivalent controller in another rig. So how do we generate that mapping? Based on what we have seen? One way of doing it, we can simply use two lists. Let's say we have a list of attributes. Let's call this otters translate X e.g. slate. Why? Let's just use two right now. Then we can have a list of values. We could say, this will be, I'm just copying this value here to translate x will be that value minus ten point something. Then translate Y will be -3.90, 54. So we could, this could work. We can say, okay, well the first item in the values list will belong to the first item in the address list. Same for the second. And that's one way of creating a mapping, right? However, there's a better way of doing this. There is a data type in Python called dictionaries. A datatype is simply a type that allows you to store data, at least right here. This is a datatype. String is a datatype. The dictionary or a **** for short, is a datatype kind of like a list, but every item in the dictionary will contain a mapping between a key and a value. Now the way we create a dictionary is with the curly braces. You can see a dictionary kind of like a, like a phone book. So let me, let me delete this. And before we create an actual dictionary, let's say we wanted to create greater phone book. Or Ashley, I mean, like a dictionary itself. Dictionary has a word and then a description. Description of the world. Or once again, the phone book example. Let's say we have Rachel and we have her number. Let's say we have rows and we have his number. Now to translate this into an actual Python dictionary, let's call it phones. Once again, we're going to use the curly brackets, the list use the square brackets. We're going to be using the curly braces here. Like I said, every item of the dictionary will have a key. In this case, the key will be Rachel, then colon, and then the value, which will be her number, then comma, to separate the items. So this here, this is the first item in the dictionary. The second item will of course be Ross colon to separate that. And then 9876. There you go. So this is the equivalent This phone book example we created here. How do we access Rachel's phone number? Let's call this. Let's draw this in a variable called number. It's a similar syntax is with the list we open and close square brackets. But instead of using an index, you're going to use the key that you want to get the value from. This case will be Rachel. Let's bring that. There you go. We get the number. Now. We get Ross. We get Ross is number. Excellent. So this is how you can create mappings in Python. Before we create our actual attribute dictionary, which would look something like this. This case will be Translate. X would be this value here I'm creating in manually, but of course we're going to be doing it based on selection. Y will be this value here. And then let's call this attributes are otters dictionary. And let me get the value from that. I'm just changing the names of the variables. It doesn't matter. I'm just trying to make it a little bit more obvious. Alright, This is great. I made a mistake. Ross is not a key in that dictionary, of course, because we changed the keys. If you try to get a value from a kinda doesn't exist, you will get that error. So let me get one that I should exist. There you go. Before we create our actual dictionary, a few things to point out about dictionaries is that keys have to be unique. What do I mean by this? Let's say that I want to add a new Translate Y key. And the value will be, let's just call it 100. Now, if I get keen by you only get one of them and the first one is ignored. Once again, that is because if you add the same key more than once, it will be overwritten because keys have to be unique. Excellent. So I guess we should look over some of the functionality inside of dictionaries. Once again, to get a value, you use the square brackets. Let me remove this key, that key, core bracket, and then you use the key that you want to get the value from. If you want to get all of the keys in a dictionary, you can simply say dictionary dot keys. This will give you list of the keys. Now, if you want to get a list of the values, then you do the dictionary dot values. Then you get a list of values. But more often than not, you will simply be using the name of the key here to get the values. Now, how do you add items to a dictionary? Is the same, the same syntax, the same way you get a value. But instead of just wrote cosine value, we will be adding a value kind of like how we store values in a variable. You will be storing a value to a key in a dictionary. The same way, you will specify a key. Let me say, let me call it a new key. And we'll say the value will be 123, e.g. if I were to print the dictionary cell, it's actually printed before. Then let's add a new Cuba new value and then let's print it after. As you see, we have our new key, but the new value, and then the two older kids and we already had. You're also noticing that this is not in-order or new key was seemingly added at the beginning. It wasn't really added that beginning. Dictionaries don't have any order. They don't keep track of the order lists. Do keep track of the order in which you add items to them. Dictionaries don't. That is because you access items via key. There's other reasons for that. It's because dictionaries are actually very efficient when it comes to accessing the data inside of them through something called a HashMap. But don't worry about that. That's technical details and don't really matter right now. Just know that dictionaries are not in order, but it doesn't matter because you always get the values through the keys. Now, what happens if you override it here? If you want to override, I keep, you simply give the name of that key. So instead of -3.90, 54, we're going to overwrite that value to be 123. As you can see here. Now in this case, These two are printed in the same order, but not necessarily there's no ordering the dictionary. So here we add and we overrode this, this value for that key. Okay, hopefully that makes sense. Dictionaries are very fun and very useful. Let's create one with our attributes. Let's create a dictionary that we actually want that will look something like this attribute and then the value. But remember that we are going to be using, and let me remove this lines of code. We're going to be using the set outer and the outer functions. Let me set an attribute so that my leg ipsos, that very useful information. Remember that Maya will be expecting or Masaryk been set out. Our function will be expecting the full name of the attribute and then the value. So it looks like this is what we need to be setting, right? So our dictionary might look more like this, but there's something we need to consider is that we want to store the controller and its attributes and its values regardless of namespace because we wanted to transfer values from one rig under our namespace to another rig. The dictionary itself doesn't actually want to know or need to know about the namespace. That means that our dictionary will look more like this, which will be the name of the controller, then the name of the attribute. Somebody could argue that this is redundant, so we don't need to do this. We could simply say, we could simply store the attributes that we want and then add the controller name later. That might work as well, but I think it's simpler to do it this way. Name of the control, name of the attributes without the namespace. So once we're setting the attributes, we will provide ourselves the full name of the attributes, including the namespace. So we can, we can always put the namespace by kin, provide the full name of the attribute, and then simply use this to get the value from our dictionary, which will look something like this. So very long line of code, but it will look something like this. Basically, I think you get the idea. We'll just get that value and apply it right there. Okay, this is the dictionary that we actually want. Let's start constructing that. 24. Storing the Pose - Building the Pose Dictionary: In order to get the dictionary that we want, we need to consider a few things. We know what that we want to get it based on selection. But we have to be careful because we know that we want to get the dictionary of the attributes and the controllers only from the first character that we selected. Because we decided that the workflow of the tool is going to be selected controllers you want to transfer, and then select any control in your target rig or ribs. So we need to somehow filter out the controllers that are only part of the first week that we select. How do we do that? There's a few ways to do it. I'm going to jump into PyCharm and start blocking out my function. So let me create a function, let's call it get pose dictionary or just the deck. Let's do some quick blocking first before we write any code. We know we need to get the selection. Either it will be provided to us or we can get ourselves inside of this function. We know we need to get the attributes that we have already worked that out up here. So that is, that is done. We need to put together the selection. Together, a selection with the attributes that we got. And then we need to build a dictionary and then return the dictionary. So we need to get the selection, but we also need to filter out selection. By the way, just a quick pass. We, this is a good way to start blocking out. Your function is deciding what you want the flow of your function to be. Something else that you might be noticing is that we're keeping functions very specialized, very separated from each other. And that is usually a good way to organize your code. Make your functions as simple, almost as necessarily specialized as you can. I shouldn't say unnecessarily, but make them a specialized as you can. So if you notice here, this function up here, all it's doing is getting namespace. It's nothing else. This function here, all it's doing is giving you a list of attributes. Now, you could argue that my first function, the namespace is function is not just getting in spaces, it's also getting a selection. And I would say a good way to make this even simpler is by Ashley providing the selection to the function so that the function doesn't need to work with any selection at all. I think that's actually a good way to improve this function. But don't worry about that right now we can come back and refactor. This refactor means rewriting the function. So reorganizing or code, this is something important to know. It's very good to plan ahead how you want to structure your code and your functions. And usually try to block out your code or organize it in a way that your functions will be very specialized, as I already said, but also you don't have to worry or make yourself try to get them perfect the first time. Because you can always come back and reorganize them and improve them. Teacher of mine told me in university that one of the great things about working with 3D is that the paint never dries, meaning that when you're working with traditional media, e.g. painting, you cannot come back a month later and fix something very small because the paint is already dry and it's harder to fix that. But with 3D, the paint never dries. You can always make changes. And it's the same case with code, the paint and never dries. You can always come back and improve your code. Especially when you're learning at the beginning, your goal should be to get something done, even if it's not great, even if it's not perfect, just get it done, get it working, and then come back and fix it. That doesn't mean you shouldn't do planning. You should plan ahead steel. But don't limit yourself by making something perfect. So with that said, like I said, I can improve this by providing selection, but I'm going to leave it like that for now. And later on maybe, maybe we can come back and improve it. Anyway. Going back to our dictionary function, how do we do the filtering? We could get the selection here, already filtered and simply trust that is filtered out. Or we could get the selection and the namespaces and simply use the first name namespace on our selection. So we can basically get the namespaces and use the first one as a filter. But I think we could make this even a little bit more simple by receiving a namespace. So this function will be expecting a namespace. We're going to be getting the selection. And then we're going to filter out the selection by the namespace provided. Let's try this out. Might not be the best design, but let's try it out. First step, let's get this election. We already know how to get the selection. We have done it up here. So let's type that out again. Cmbs, ls selection equals true. Now we know that we want to do a check. If nothing was selected, then there's nothing for us to do. So we're going to type, if not selection. We're doing something slightly different before we were checking the length of the selection. You can do that if you want or you can do the same thing we did here, simply saying, If not something, then return. But since we're expecting a dictionary, let's keep our return type is consistent and return an empty dictionary. Empty dictionary is just two curly braces. And that's it. Great. So if there's nothing selected, there's nothing for us to do. Let's continue. We could say else and then continue your code inside of the else Bob, because we know that the return is the end of the function. We don't really need the else statement. We can simply continue here. Because if we enter the if statement, we will return an exit function. So it will not access who will not reach anything after that return. That means we can simply continue at this level of indentation. So the next thing we need to do is we're going to need to work with this election. We're going to use another for loop for control in selection. Now here is where we're going to do the rest of our steps. We know we need to do filter the selection, get attributes, and build a dictionary. So we're going to do all of that inside of our for loop. How do we filter out by, by the namespace provided? Since the controllers in the selection or strings, strings have a special function that checks if something starts with a substring, which is another string, or ends with another string. Let me give you an example here inside of Maya. I'm going to take this string e.g. delete this. E.g. this here I want to check if this starts, starts with Minerva, which is the namespace we have here. I'm just going to print that. This function will return a Boolean, either true or false, starts with Minerva. Yes, exactly. We could also say it starts with MI, e.g. and that will be true. But if we wanted to say, does it start with LA Example, It won't say false. We're going to use this to filter our namespaces. If control starts with the namespace that we were provided. We are going to do our rest of our logic inside of this. That means that everything building the dictionary will need to happen inside of this if statement. Now that's not a problem, but I usually prefer to have less indentation. I think that's cleaner and it's usually easier to read. So how do we avoid this indentation? There's a keyword that works for for-loops or loops in general, that is called continue. What this means is that if you were basically saying, if you enter this for-loop, continue to the next item in the list, in the selected list, meaning that anything that's after the continue will be completely ignored. And that also means that we need to reverse this logic. We're basically saying, if not, control starts with the namespace, then continue to the next item in this election. And don't worry about what's happening below. And that means that we can we can simply continue without having to indent inside of this if statement. So we have already filtered out the selection. And let me, let me put some comments here. Selection, space. Good selection, That's great. The next step is getting the attributes. This one should be easy. We already have a function for that. So we can simply copy that function, the name of that function. And we can say animate, animatable. Attributes are otters will be get address from node. And then know that we're working with is the control. To keep track of this in case this is a little bit confusing. Remember that the selection is giving you, the selection command, is giving you the exactly this, the namespace plus the name of the control. Let me just group that quick. We're going to print, going to select one controller. When to print the ls command. This is what we're working with inside of the for loop. Let me bring that into pie charts so we can keep track of what's happening here. We know that control, each control will be something like this namespace, plaza name of the controller. Once again, that's why they starts with check, it's going to work. And now we're getting the attributes from this controller node that basically looks like this. Excellent. Now we need another check. Well, let me come in here and get attributes. We need another check to say if there is no attributes, because we know that if there are no attributes at all, we're going to get an empty list. We could do this in two ways. We can say if the length of the animatable attributes, it's zero, we can continue again. So same, same idea as above. If there's no attributes at all, we're going to simply break the checks in the for loop and go to the next item in the selection. Now to keep it simple, I'm not going to use the length of the list. I am simply going to say, if not numerical attributes, if there's nothing in there, skip whatever comes after, and continue to the next item of the selection. Excellent, Well the next step is putting together the dictionary. And for that, we need to initialize an empty dictionary. Otherwise we have nothing to populate, right? I'm going to call it pose dict dictionary. And it will be an empty dictionary. Once again, an empty dictionary is just two curly brackets. Let me bring up the example. We had to know exactly what we're trying to build. What we're trying to build is something like this. Basically the key, let me put this behind a comment. The key will be the name of the controller without a namespace dot the name of the attribute. And then the value of that key will be the value of that attribute. And once again, we're working with controllers from the selection that looked like this. So let's keep, Let's keep this in here just to use as a reference. While we need to do is loop over the attributes that we got here. For adder in animatable others, it's a very long variable name, but it's okay. Any medical errors? The first thing we need to do is let's get this name. We need to strip out the name of the actual attribute, sorry, the name of the controller separated from the namespace. Let's call this troll name. Will be control dot split. As we have done before. We can do this plate on the, on this column. So again, we're going to split it. Remember, the split will basically give us the first item and the last item the same way we did it with the namespaces up here. But now instead of getting the first item we want, we want the last item. We're going to say, give us the last item from that split. That will be the control name. Then we want to put together the full attribute name, which is this here. And that will be, let's call it full adder. Now will basically be the name of the control dot, the name of the attribute. So once again, we can use this, this format function to use the replace these placeholders with something else. We're going to replace those with controlled name. And the second one will be the attributes or the other from the animatable attributes we already got. Excellent. So the next part will be getting the value from the attribute. Now for this, we're going to use as we already. So we're going to use get outer. Bud. Remember, if I go back to my app or a second, get adder actually expects where he's getting better. Okay, well let's just write it out. Get out or expects the full name of the control with the, with the attributes. Of course, because it needs to know what namespace the attribute belongs to. So we need to construct this whole name of the control plus the attribute. So we need to pass that into get adder. Let's go ahead and do that. That we do a similar pattern as with format. But now in this case we need to pass in the full control that came from the selection, which is under this variable called control. And then we're going to say dot. And then the attributes, right? Same idea, dot format. Now we're going to use the full control, not the control name that we separated. We're going to use a full control. And we're going to use the attribute editor, which is the attribute that we got. The value will be basically that if this is a little bit confusing, let me separate it. Let me put it in a new variable. Let's call it control with utter. And let's get the name. That's it. Let's get the value of that width, gets out of control. Way. There you go. Now we have the value. Now the only thing left to do is populate the actual dictionary. Remember our dictionary is called post decked. So let's say pose. Now the key, remember, will be the default attribute named without the namespace. So that would be this here. If this is a little confusing, we can make it even more explicit. Without NS, without namespace. This is the bread long variable name. But it's, it's okay for these purposes. Full Adder without actually let me name it. Full adder know namespace. There you go. It's a little bit shorter. Know namespace. And the value for that key will be the value of the attribute. Let's make this a little bit more explicit as well. And let's call it out or underscore, underscore value. That will be the value of our t value. Once again, we're building this right here. That is what this line is doing. Let me add a comment here. We'll basically say this will be the control width, the attributes. And the key will be the attribute value. Excellent. We have are populating our dictionary. Let me delete this here because we don't need it anymore. I'm going to say, I'm going to add a comment here just saying populate dictionary or build dictionary. And at the end, the only thing left to do is to return the dictionary, which is called pose score, that let's go back to Maya and test it out. Before we go back and add our docstrings, let me copy the name of this function. Let's go back to Maya. And I have here post transfer. So let's do this from scratch. Important Posts underscore transfer. Now, if you haven't restarted Maya, you have, remember that my kind of caches or keeps your the latest input that you did. So if we want to refresh that or reload that, we can say Reload post transfer. Since we did, we did some changes to it. Then we can say pose, but this is the dictionary. So we're going to say pose decked equals pose, underscore, transfer, transfer, dot, GET, post. Once again, the name of the variable is up to you. You could call it pauses that. You could simply call it posts if you want to. That is up to you. I'll be more explicit and call it pose. Let's, let's print this. Both underscore. Let's just run it and see what happens. Oh, of course it takes exactly one argument. So we're going to say that we want to filter by me nervous name. So we're going to say Nerva. And I'm just going to select one control on that rig. And there you go. This is exactly what we were expecting. As you see, that there's only three attributes in that control that I selected translation and all of them are at zero. So we have controlled name, translate z, x, and y, and all of them are at zero. Now let's test out our filtering. So once again, I'm going to select that controller and I'm going to select a few controllers in the other rig. So now we have selected one controller in Minerva and a few in Severus. But remember we're filtering by Nerva, so the result should be the same. So we're only getting the controllers that belong to that namespace. And of course, this is agnostic, like the result is a dictionary with the controllers and the attributes from that controller, sorry, from that character, but it's agnostic of that character's name space. Because now we can use this and apply it to any other character that has the same attributes. If you want to see this a little bit nicer, actually won't. Let me select a few more controllers to get a bigger dictionary. There you go. So this is a much larger force. I showed you before. The print function, we can say from Pip print, print, p, print means pretty print. We can say print, post. All of these will do is printed in a human-readable way. Which is, oh sorry, I haven't imported it. Which will just make it easier, nicer to read. So this is the dictionary we're expecting. Now we can use this and apply all of these values to another character. Now, just to make sure that this is actually getting the values that we care about. I'm going to change it so that it's not just zero. There you go. So there's a few different values. Now, if I run that piece of code again, there you go. These are the values that belong to that control. Excellent. The next step will be to apply this post to the other character. But before we do that, let's not forget the doc strings. Once again, triple, double codes, small description, pose to pose, dictionary without namespaces. The namespace we know we're expecting a string. So I'm going to type string and a small description of what this argument is actually doing. Filter selection by this namespace. Excellent. And then the return value, of course, will be a dictionary. Let's just leave it at that. Well, I did add a description above, so let's be consistent unless that dictionary of controls with attributes and their values. Excellent. 25. Applying the Pose: Okay, So we're almost done with the function only for your two. Now that we're able to save a pose in the form of a dictionary. The next step is to apply that pose. Let's quickly see what that would entail. If we were to do that manually, let me clear my output window. I'm going to select the controller, this chest control. Once again, I'll only see what the pose dictionary it looks like. Once again, we have the attributes with the controllers, with the attributes and the values. So what would we need to do to apply that to this character? Well, really, all we need to do if we were doing this manually would be set attribute. So Cmd S, Let's set adder. We know that we want. Let's say that we are setting the rotation value and we need to set the actual values. So that is this value we have right here. I normally say one thing which is telling the command which namespaced apply this true. So all we would need to do, what's the new namespace? It target namespace. And let me make this a little bit smaller and run this line of code. And there you go. We apply that value to the rotation. Let me apply something a little bit more extreme, a little bit of a bigger change which will be rotated y. There we go. Now we're applying that volume. Excellent. So that is basically what we're going to do. Take the attributes from that dictionary of the pose. We saved the namespace to it and then apply the value. Let's go into PyCharm and start mapping out, blocking out our next function. Let's call it applied posts. We know that in order to play a pose, we need to know the actual pose values which come in the form of a dictionary. So let's call it post dict to keep consistency with the names. And we know when it didn't need to know the target namespace. You can name this target namespace or simply targets. But I think Namespace by itself makes sense. And then in the docstring we can specify a little bit more about that, what that is doing. Excellent. So let's start blocking out in function. We know we need to get the, the attribute names. We know we need to add the namespace to it. And then we simply need to set the attribute. But now there might be a problem here. A second and try to see if you can think of what might go wrong here. What could be a potential bug? What happens if the characters are not exactly the same or the race are not the same. In my example, I know these two are exactly the same because I used the same rate. But now imagine that, let's say my, my source character has an extra prop. Let's say that she has a hat, and we select the hat. And we tried to apply that value to the other character. And the other characters who had the hat that would fail, that would cause an error because my, I would say, hey, this, this controller or this object doesn't exist. Why did they attribute is locked? E.g. if it's locked, we can set it. So Maya will also give us an error. That means we need to do a little bit of error checking. Let's just write that in, just error checking or error checks. And we will think about the checks in a second. Let's start writing the base functionality first. The first thing we want to do is get the attribute names. And that means we need to loop over the keys in the dictionary that basically represent the, the name of those attributes. So let's say, let's call them out her name, each one of them for acronym in post underscore dict. Now, dictionaries have a function. Cold Keys. Keys will give you a list of the keys in the dictionary. Let me show you that real quick. Inside of my, me, delete this right here, and let's get a new dictionary to play with. I'm going to select this controller. We know that that controller only has three attributes. So it's going to be as small dictionary. And I'm going to pretty print that dictionary. There you go. So if I were to print post underscore dict keys, as you can see, I get a list of those keys, which is basically just the controller. The controller names with the attributes. There's another function called values, which as you can probably guess, will simply give me a list of those values. Now there's another one called items, which gives me, will give me at least the pupils. Now, as I've said before, a pole to pole is like a list that you cannot modify and it's made using the regular parenthesis. And dictionary items will basically give you a list with the pairs of key and value pair for each item in the dictionary. But for now we want to use keys to loop over all of those control names with their attributes. Slip over that keys list. Let's get the value for that key. Remember to get a value, you use a dictionary and you get the value from a key. So open and close square brackets. And we will use the other name, which is the key that we're looping over. So that will be the value of the attribute. So let's actually call that outer value. There you go. We're getting the key and we're getting the value. Now remember, we need to put together the namespace and the attribute name. Let's call this full adder name. And this will be the namespace followed by a colon, because that is the convention in Maya. And we will do dot format. It's going to be namespace. And then the second place holder will be replaced by the name. There you go. The next thing to do, we have already done this. We need to add namespace. So let me, let me move this up here. Um, by the way, real quick, if you wonder how I'm moving so quickly between lines, there's a few called keys that will work anywhere in Windows. If you press the Control key, you can jump between words. It should press Control and Shift. You can do that but, but highlighting at the same time. And if you do tab, you will indent. But if you do Shift Tab, you will do the opposite. It will go in reverse. So that is how I'm able to do to move around so quickly. But those are just hotkeys. Don't worry too much about them. It's very handy though, so it does allow you to type a little bit faster. But anyway, going back to our code, we have nicknames. We added the name space, and now we need to set the attribute. Let's put this, let's set the attribute. The attribute we know we have to do CMBS dot set, adder. And then we pass in their full name of the attribute, error name. And the value that we want to set, which is our value. And there you go. There you are basically applying your post to your new character. Now we're, we're missing the error checks. But for now let's, let's test this out to see what's happening, to see if it's actually working. Let's go back to Maya this out. Since I made some changes to the code, I'm going to reload the module. There you go. Let's use a new controller. We've been using that chess control too much. I'm going to use this arm, this elbow control right here. Let's just apply some values to it. Let's get up post dictionary. We're going to get posts for Minerva from the selected control. Let's just print it out to see what we got. There we go. This is the pose that we're trying to transfer. So let's apply that pose to the other character. Post underscore, transfer. Apply pose. And we know we're expecting a post dictionary and a namespace. Let's provided pose dictionary we already created. And the namespace is targeting space, which in this case will be Severus. There. Now this is something else we could check for. If the namespace is even valid. What if I were to provide run e.g. this doesn't exist in the scene, so these could fail. But maybe we can check that in the controls directly. So let's, let's just test this for now. Let me show arm of this other character so that we can apply that pose. All of this code. And there you go. As you can see, we apply the values. In this case it's only rotation, but that's fine because it's just an elbow control. So now this is working. Now you could stop here because it's already working. But like I said, imagine that these attribute was locked, or it didn't exist, or maybe the character didn't have that control. We need to do that kind of checking. So let's go ahead and simulate that type of error to see how we can prevent or how we can check, anticipate this type of issues. I have gone ahead and added a prop to this character. I added in the base file for the rig. So now what we need to do is reload that reference. Which as you already know, it's the beauty of references. You can make changes in the rig itself and anything that references that rig. We'll get those updates, which is quite nice. Let me reload that reference. And we should see a new prop, a new hat show up here in a second. There you go. We have the prop. So now clearly this, this other character doesn't have. That same control. There's a new controller called Hat under nervosa namespace. Now let's try to transfer these controllers pose to that other characters just like we did with the arm. And let's see what happens. Let me make more space here. So let's get a new post dictionary. And now this is the hat. Now let's try to apply that to Severus. There you go. That's the error we're expecting. The error is that no object matches name, several hours hat, dot rotate. That's because every doesn't have a head. So let's go ahead and try to deal with these types of errors. The first thing to check is to see if the controller itself exists, right? But also there's other types of checks that we could do. So let's go ahead and map that out. Let's just block it out here. So check if object, object, cysts. Check if the attribute is separable. We could check directly if it's not locked, we could check if it's animatable. But I think if we're checking to see if it's settable, that's more than enough for what we need to do here. But before we even check if an attribute can be set, we need to check if the attribute even exists. So let's say check attribute exists. Alright? So checking if an object exists is, it's very simple in Maya. Let's start with that. There's a function in this library called Cmd S dot OBJ exists. Very straightforward. And why you do is provide the name of that object. So let's see if Severus has had spoiler alert. We know it doesn't, it doesn't. But let's, let's print the result of that function bolts. Now if we do that with Minerva, well, the answer is yes, excellent. So we can do that. But for this we need the name of the object itself without the attributes, right? Well, it's not. The object is, more correctly is the node. Let's go ahead and get that node. Now we know that we're getting we have the full attribute name here. So we basically one this without the name of the actual attribute at the end, which is separated by a duct. Once again, just for reference, Let's bring this in. This is basically what we're working with. Pull out her name is basically this, which is the namespace plus a controller dot attribute. So we want to extract the first part of that. We can do this very simply with our friend dot split. As we have done multiple times already. We can say the node will be full outer name, split, split by the dot. And we know this will be splitting the list in two parts. So we want to get the first part, which is the namespace plus day. I shall control name and that is the node. So the first item in the list will be index number zero. So that is the name of the node. We could say if Cmd S dot OBJ exists, check if it exist, that know it exists. Ashley, sorry, we wanted to check the opposite, right? Well, while again, we could do it depends how you want the logic to work. If you want to only apply that logic or set the attribute if the object exists, we could simply in dentin and this will work. If the object exists, then set the attribute. Let's do that for now. Let me write a comment here. By the way, if you're also wondering how I get to the end of the line, I am using the N E, which you will find in your keyboard. And the Home key will bring you to the beginning of a line. If you use that along with the Shift key, then you can select the whole light. It's very healthy. So we're going to say that we're checking here. Excellent. We're checking if the object exists, if it does set the attribute, Let's test that out. I'm going to remove this line here. And I'm going to move these two extra checks down here because that's still our to-do. It's kind of like our to-do list for now. So let's, let's test this out first. Once again because I made some changes. I'm reloading post transfer. Let's make sure it's selected and it is. There you go. Nothing happened, which is exactly what we want because the only control we selected doesn't really exist. Now, let's, let's try with two controllers. It's a little bit more complicated. Let's move the arm. Let's select the hat, and let's apply the post once again. Now, only the arm was applied and the handle was ignored because it doesn't exist. Excellent. How do we check if an attribute actually exist? Because the object might exist as we're checking here, but it might not have the attribute we're trying to set. So as usual, the symbols library has a command or function that can help us with this. It's called attribute query. So let's see how to use it using the documentation. Let's go to the documentation style attribute query. There you go. So in the description it says returns information about the configuration of an attribute. Seems like that's what we want to do. Let's go ahead and look at the examples at the bottom. Determine the hidden status of the selector attribute. Determined this. Okay, So it looks like you're passing the attribute that you want and then determine if an attribute is curable. Though this might actually be something we want to do. Potentially. You're passing the name of the node and then you pass in the attributes that you want. So it's a little backwards from the way that Get out or works, which is unfortunate. I wish I would keep that a little bit more consistent. But so remember that get out or it gets the full name of the attribute, which is the controller dot the attribute. In this case, we need to break it up. We need to get the node or the controller and the attribute separately and then check for the whatever you want to test or check in the attribute. So in that case, I know there's a flag or an argument to see if an attribute exists. It should be exists. Yes, there you go. It's a boolean attribute, will return true if the attribute exists, okay, So we basically need to get the sole attribute name. Then passing the node that we want to get the check the attribute in. Great, So wherever the chicken denote by splitting this weekend, and now we need to get the just the name of the attribute. Let's call it the short name of the attribute as opposed to the full name of the attribute. We know that by splitting full adder name, we're actually getting both, right? We're splitting by the dot, which means working in this part. And we're also getting that part, which is the short name of the attribute that we want. So we could simply do this. Outer short name equals copy, this equals that split. But now instead of getting the first item, we want to get the second item. So this is the note, and this is the short name of the attribute. But there's a, there's a better way of doing this. Since we know we're getting a list with two items, you can actually assign those two items from the list to two variables at once in a single line in Python, you do that by doing this. Outer short name equals the list of that we got from the split. So let's see how this is working in Maya in case is a little bit confusing. So let's go here. Let's take, well, let's follow the example we're using. Let's take this full name of the attribute. Let's use a new Python tab. We know we want to split this dot split by the dot split name. Let's call this, let's actually, let's call it, it's split list. Let's print the cell split score list. There you go. So you get the two items. Like I said, you can assign that to variables by saying the first node, this first one is the node, and the second one is the actual attribute equals split list. And now you can print node. You complete print attribute C. There you go. Now, this works because we know that the list has two items. If the list had more items. And that's e.g. let's replace split list with a list of our own. They just say 12.3. If we run this now, the list has three items. We're trying to assign to a 22 variables thread into two variables. What we're going to have a narrower because there's too many values to unpack. This only works because we know the split is giving us two items. Great. Let's go ahead and check if the attribute exists. Let me bring this line down here, which is where we need it. Let's start querying that. That the existence of the attributes. So let's say if CMBS dot, let me type it out. Attribute, query, open and close parenthesis. Now remember we have the passing the short name of the attributes. And once again, we're following the documentation here. Following this example shows the name of the attribute and then node. Now, if we wanted to see what that note arguments doing, Let's, let me search for it. Let's go to it. Note people's name. Use all attributes from node named name. Okay, this is a little bit misleading. There is no node named name. And it's telling us that it needs a value of type name that doesn't exist. That actually means a string. So it's a little bit unfortunate that sometimes the documentation in my S not very consistent. There is some educated guessing that you have to do sometimes like this. In this case named just have to guess or assume that it means string. With that said though, the documentation is still extremely valuable, which is why I keep coming back to it. Let's say node equals name. No, sorry, sorry, node equals node, which is the node that we got from splitting. And then what we need to check is to see if they attribute exists, exists equals true, or querying the existence of the attribute. Right? There you go. Call long to finish that IF statement. And now we indent this in. Something you're probably noticing is that the indentation is getting too much. We have 12 levels of indentation and we're going to have one more because we need to check if the attribute is separable. Let's go ahead and clean this up before we do the last check. Something when you can do instead of indentation. We avoided in imitation of here by using the continue, continue keyword. We could do that. We could say f naught object exists. Continue. And then let me unindent this. Once again, same thing. If not attribute exist, then we can simply say Continue. And then because we're skipping, we're using continued to avoid indentation. We can simply add the same level as everything else said they attribute. And now, let's do our last check. We need to check if an attribute is settable. Now, you might think that you can know that information from attribute query. That is a very good guess. Unfortunately, that is not the case. To see if an attribute is set of all, we need to use the set, sorry, they get adder function. So let's have a look at that in the documentation. Get out or you can check if something is scalable. You also can check if something is settable. And once again, this will return a Boolean or a one which in this case we can trade as a Boolean. So let's go ahead and do that. Let's get out or you basically want this get adder attribute name. But now as opposed to the attribute query function, we actually need the full name of the attribute. Once again, sometimes these functions have no very consistent with each other. But that's okay. You just have to rely on the documentation or your experience. The more you do these functions, the more you know how to use them. And it stops being a problem. It's never really a problem. Great. So let's do this. Let's say Get out or name of the attribute and set double equals true to check if our attribute is separable. So if CMBS dot get adder. Now remember in this case we want the full name of the attribute. So it will say full name. Homer, settable equals true. So if not, attribute is saturable, so if the attribute is not separable, we want to continue. So we don't want to, we want to basically skip anything that comes before or after that and go on to the next value in the loop. Great, So let's, let's test this out and see if this is actually working. Once and let me go back to my tab. Let me, since we made some changes, I'm going to reload my module. It's great. And I'm going to apply some values to the arm again. Once again. So get the pose, the pose dictionary, and apply that pose to our target card. There you go. Only the arm was applied and had was ignored because of course it doesn't exist. That's great. Now this is fine, this works, but there's one more way we can condense this code and making it a little bit cleaner. Let me show you how to do that by using the OR operator. Operator. The operator basically allows you to chain these type of checks. So we can say if not object exists or not, attribute query exists or not, attribute is settable. Now, this became a very long line, which might also not be great. But let me show you how it's working. So we're basically saying if the object doesn't exist, continue. So if this condition is met, we basically enter the if statement. But let's say that the object is sets exist. So the object exist, great. Or if they attribute doesn't exist. Then do that. So let's say that the object exist and the attribute does exist. Then we will go, we will go into our next check in the next or which would be, or if the attribute is not separable, then continue. This is the same as our three-level checks, but all in one line. Now there's another version of this which is the opposite, which is, and so you can say, if we could reverses logic, I just wanted to show you these two so that you know your options. We could reverse lists logic. Object exists. I'm not going to, I'm actually, I'm going to type the proper code. So we can say if object two exists and the attribute exists, e.g. let's imagine that that's all the checks we want to do. We can say if the object exists and the attribute exists, then we can do our set adder, e.g. this is reversing the logic by using the AND operator. But we're trying to avoid indentation. So we're doing the opposite by using the OR operator. If the object doesn't exist, or if the attribute doesn't exist, or if the attribute is not separable, then we continue skip setting the attribute and going to the next item in the loop. Okay, to finish up this function, we need to clean it. Clean it up. We know we do not need this line. This one's just while we're developing to help us out. We're going to remove these comments here, but we're going to put a comment here saying check. Just call it attribute checks. And then we set the attribute. Excellent. Now before we forget our friends doc strings, very important. So let's set the docstring, say apply provided pose to provide it namespace, e.g. now we know that pose is expected to be a dictionary. Dictionary with pose values or pose pose data, let's say post data, which is the keys and the values and the namespace. We know it's a string. But let's actually be explicit here and say target namespace to apply to. Maybe a little bit wordy, but feel free to condense that sentence if you want. And in this case we're not returning anything. So I am going to remove this line. And that's enough. Perfect. Our function is nice and tidy. It's very small, which is great. I can, like I said, small and specialized functions are usually the way to go. Let's test this out. Before we move on to putting all of these together. I'm going to remove the print. Once again, I'm going to reload my sorry. It's not what I meant to do. We're going to reload my module. And I'm going to get opposed dictionary. Let's do a few more controllers this time. So the opposing, the arm. Let's do the hips, e.g. hips, chest, and the arm. And I selected a few more controllers, but that's okay. And the hat, just to check out the test are checks. And great, So we're going to get the post dictionary from that and then apply that the Cerberus. There we go. Excellent, that's working. So we're almost done. While your code is basically done. Now the next thing we need to do is put it altogether for the workflow that we want. Now at this point, you have all of the functionality separated into specialized and small functions. So now you are free to decide what workflow you want to, you want to do with this, what you want to use with this, with this tool. We're doing something that's very valid, which is just typing in the name of our target namespace. If that's how you want it to work, it's great. Let's say that you want to create a UI. You want the user to type in the namespace you have they relate to do that if you want. But what we want, as I mentioned at the beginning, is use selection. And that's it uses selection to get the namespaces and oppose at the same time. So let's, let's create a function that does that. And basically brings all of these together into a single function that will do all of this for us. 26. Building the Main "Transfer" Function: Alright, let's create a function that brings everything together. So the first question is, where do we put that function at the top of the file, in the middle of the file, at the end of the file. So far, we have just been doing it top to bottom. And that's fine. We can just put it at the end and it's not a problem. But there is something that I want to mention about Python. It's an interesting fact. Or if it's not interesting, it's at least good to know. So python is an interpreted language. Let me write it up here. Interpret it as opposed to being a compiled language. You have probably heard this term before compiled. What it means is that your source code, which is really the code, the text that you write. All of this source code is translated into something that the machine can actually execute. At the lowest levels of execution in my computer. The computer is really leading zeros and ones. Between those zeros and ones. And the source code that you write, there's a few steps of different types of code. It's called object code or machine code. And those are different levels of translation to get to those zeros and ones. So in languages like C or C plus plus, before you can run your code, you have to compile it and you do that step manually or sometimes you automate it, but you have to do the step before you can execute your code. But that means that the code that is being executed is closer to what the machine actually reads at the lowest level of execution, which means that it's faster. So that code that you execute when you compile something is faster. On the other hand, as I mentioned, Python is interpreted. That means that you don't have to compile it. You can just make a change and run it just like we have been doing so far. I have to say that it's not 100% true. The fact that Python is interpreted doesn't exactly mean that it's not compiled. There's still a level of completion happening, but there are differences that is done for you every time you execute the code. So that means that you can just make changes and run the code. But that also means that the code that you're running Python will usually be slower than the code that you run in C plus, plus the same operation in Python will be slower than the same operation in C plus plus. For the simple fact that C plus plus gets compiled in advance. And the code is being run more directly to what the computer understands. With that said, the difference for most of what we do in animation will not really matter too much. Which is why all of the pipelines in any studio that you can go to, we'll be using Python. Because since you don't have to do that step of compilation is very fast to develop with it. You can make changes very quickly. The syntax is very similar to English. So you have seen so far and you don't have to compile it. As I mentioned, the fact that it's slightly slower than other languages doesn't really make a difference. And it's agility in development. It's a benefit over the lack of speed. Which again, is something you will never notice. The Spirit is not very noticeable by, by humans unless you do something very, very computationally heavy. Why am I mentioning this? Is because the fact that Python is interpreted, it means that the code is being read and executed top to bottom. So a practice that a lot of people do is write the function stop the bottom. So if I'm going to need this function later on, I'm going to have it first and then call it down here, e.g. now, while that is logically true, with the case of functions, it's not necessarily, I could be if I have a function. So e.g. if I call this function up here, this is going to fail. Even by charm is telling me that the function doesn't exist. This is going to fail because the function hasn't been declared yet. But if I put it inside of another function, it's not the case anymore. That is because Python will go through the function names first, register all of them, and then you will have access to any function even if it's below the one that you're calling, because it's inside of another function. That is to say that you can put the function we're about to write anywhere you want. As a general practice, I, since this is a function that we're going to be interacting with, this is the entry point for our tool. I usually put those kind of functions at the beginning of the file so that the first thing you see is that function. And from there you start understanding what is happening in the rest of the pile. So, but that said, after all of that speech, hopefully it was interesting, useful, unclear. But let's go ahead and write the actual function. This is a function that acts as our button click for us, he will do the entire workflow on. Remember we said, we want to select two or more characters and apply the post from the first one to the other target characters. So I'm going to call it transfer selected. No inputs right now. Let's start blocking it out. The first thing we need to do is get selected namespaces. Then after that we need to validate the selection. Because like, like I said, we need to select at least two characters. So valid date selection. Then we need to get the pose dictionary from the first character, firstName space for the first character. Because Rick, let's say, then apply the pose to the target namespaces, target drugs. So let's call this dictionary from source and apply targets. Alright, I think that makes sense. I think that's flow we're going for. So let's start filling it out. Let's get the namespaces. This one is easy, we already have a function for it. Now. We actually have a function for everything we need to do in this function. Or maybe let's call this variable namespaces equals, get selected namespaces, which is our function down here. Excellent names. That's why it's failing. Namespaces. There you go. Now, we need to validate our selection. As we said, we want to operate on two or more select characters were selected namespaces. So let's say if I'm going to use the length function L-E-N, len namespaces is less than two. We can simply exit their function here and do nothing returned. But I would like to make it a little bit more user-friendly. I would like to tell the user why nothing happened, instead of leaving them in the dark. So we can use the C&DH library for that CMBS dot warning, very conveniently named, will give us the ability to give the user a warning. All it does is take a string as a message and displayed to the user. So we can say Please select two or more rates. Now let me show you this. I'm sure you have seen this type of warning before. It simply looks like that. Is that yellow warning that shows up at the bottom of Maya. Excellent. So we could improve on this a little bit. We can actually make it so that if there's nothing selected, you tell the user to please select something. If there's only one. Let me do that, please. So if the length of namespaces is zero, we can say Please select something. Otherwise. Else, if the length of namespaces is one, we can say morning. Please select more than one. Right? If that is the case, we can simply return and exit the function. So it's up to you. You can do the single if statement or the double if statement. The if and elif is Terminus. I have done here. I'm going to stick to this one. Then again, if my checks are not passed, then we return and do nothing. Otherwise, we want to get the source, the dictionary from the source and then applied to the targets. Since we have namespaces, this source namespace, source, let's just call it source. Actually, no, sorry, let's be more explicit. Source name space. We know that it will be the first selected namespace. So we can simply say namespaces, open and close brackets, square brackets. Index zero will be the first one. And then we want to apply that to the rest of the namespaces before we get to that, because that will actually introduce a new concept in enlist. Let me do it with just one target. So let's ignore anything, anything other than two selected rigs for now, we're going to fix that. Very soon. I'm going to call this target space, will be spaces. The index number one, which is the second item in the list. And we know we have enough because we're checking to make sure we have at least more than one, at least two in this case. Great. So let's get the post dictionary. Let's call it post dict equals GET post deck. As we, as we know, we are asking for a namespace for that function. And that will be our source namespace, source namespace, dictionary. And then all we need to do is apply pose. Provide, apply pose. We can provide the post dictionary and provide the target namespace. Alright, let's test this out. And then we can come back and do this for more than two rigs if this is working. All right, Let's go into Maya. Once again, because we made some changes. We're going to reload the module. Let me clean up my script editor because it's a little bit of a mess. And we don't need to store any of these variables anymore. So we're going to say transfer, no, sorry, posts transfer, both on those port transfer, transfer. Select it. And then let's call that function. So let's just test out our checks first. Let me run this piece of code. Indexes are arranged, Okay, I have a bug right now. Playing 13. Index is out of range. Okay, I see what's happening. You made a mistake, but this is great. So as you can see my warning, they'd go off. But then my code kept executing. And we're trying to get the first name space from the namespaces list. But Python is telling me that the list index is out of range. That means that there's nothing in namespaces. And I'm trying to get something from nothing essentially which of course is not going to work. So let's fix that. Go back to pi term. So this is a problem, is that we, we got to this point. The length of namespaces was zero. We throw a warning. And we still went here because we didn't return in this if statement. So let me show you that if I select only one namespace we're going to enter here. We're going to get this warning, and then we're going to exit without error. Let me just do that real quick. Select one of them and run this. There you go. We got the warning but no errors. So how do we fix that? Very simple, we can simply return here, exit the function here. That also means that the if statement is not really necessary. We can simply say if now that doesn't make it wrong, it will work both ways. But now because we're exiting the function anyway, we don't know, we don't need the else if in this case. Alright, so this should fix our bug. Let me de-select everything so we get the first warning. Please select something. Excellent. Now select only one thing. Say Please select more than one Rick. Excellent. And now let's do the actual transfer. Let me move the hips back. There you go. Let me select my target character. And there you go, The post is transferred. Now we said, well, let me test it with the prop that doesn't exist in the target. There you go, no error occurred. The pose is the same. Sounds. It's working. Great. Now, let's, let's make this work with multiple characters. And for that, I need to add one more character into the scene. I have gone ahead and duplicate our Minerva character. So there's another reference rig in the scene, and I'm going to change its namespace to be low. Now. Let's just give me a second to respond. There we go. So now we have a three characters referenced into the scene. Let me move this one over here. And let's apply a post again. But now let's try to apply it to two characters instead of just one. There you go. So as expected, it only worked in the second one we selected because we put that limitation in place. But now let's go ahead and make it more versatile so that we can apply the post to as many characters as we want. For this to work, we need to get a list of target namespaces. So I'm going to change this to target namespaces. Now instead of it being a single string, it has to be a list of strings. And we do that by doing something called list slicing. We have to slice a list and take everything except the first item in the list. And we can do that by using this syntax. So it's still inside of this score brackets. We will use this column. This will be on the left side. Let me make some space here. On the left side. This will be the first item or the first index at which you want to start the copy of the list you're making. So that will be indexed number one. And then here, this will be the last index at which you want to stop. Now the thing about this one is that is non-inclusive. That means that if you want to stop at index number two, you actually have to provide index three. That could be a little bit confusing. This means that we're going to start at index one and end at index two. Or in this case right now, we want to go all the way until the end of the function. So you can simply leave this empty. So for our purpose, we can use leave this empty. So this means that we're going to get everything from the second item, which is index number one, until the end of the list. Let me show you this in Maya before we continue. Because this can be a little bit confusing. So let's, let's actually get a list of namespaces. Namespaces equals post underscore, transfer, get selected. There's coordinate space says let me move this over here, name spaces. And let's go ahead and print that. Okay, there you go. So we have three, these three namespaces. So actually, let's just, let's just put that list in here so that it's more obvious. Okay? So we print that list. We have those three namespaces. If I print, if I do this, e.g. I am slicing the list. But if I don't include any indices, it will take from the very beginning of the list until the very end of the list. So basically making a full copy of the list. Now remember before that I told you that lists can be modified. That means that you can modify name's Britney spaces over and over again. When you do this, you are making a copy of it. So it's like a separate list. And if you're modifying the copy, you will not be affecting the original one. So in this case, we're basically printing a copy of that list. Now as I mentioned, we want everything but the first one. So we can start on index, on the second item, which is index number one because we start counting at zero. So let's see what this gives us. There you go. We get Severus and Linda. Now, what if we wanted only the last one? Well, for AC, we can start at index two and we only get that wonder. Now, I did mention that you can, you can tell the slicing of the list to work to stop. So in this case, what if we wanted to stop? Number two, let's say that we want everything from zero up until the second item. The second item logically should be index number one. But if I do that, I only get one. That is because as I said, the second place here after the colon is not inclusive. That means that you have to go one over. So if I do index number two among, I'm going to basically get index number zero and index number one up to one before number two. I know that can be a little bit confusing. Even for me. Sometimes I forget which one is inclusive, which one is exclusive? But it's, it's how Python works and there's, there's some reasons behind it, but we don't need to get into that right now. But once again, if I leave this empty, I'm going to get, actually I'm going to start at index one. I'm going to go all the way until the end. So I'm going to leave it empty. There you go. If you're curious, if you can use the minus one index, you can. It works the same way as, as if you were writing index number four in this case. So this is index 01.2, sorry, index number three. If you're writing the index number three here, you will get 12.3. Will it be ignored minus one? Because like we said, minus one equals is the same as the last index. Minus one basically is the same as three. So you will get one. And that's it. She'll get only servers anyway. I hope it wasn't very confusing. We will go over lists in more detail After this project. For now, all we need is, all we need to know is that we want a copy of the list, skipping the first item, so it's keeping index zero. So starting at index one all the way until the end of that list. Let's come back and do that. Well, we already, already didn't. We have a new list of target, of namespaces, which is the target namespaces. So what we can do now, we need to loop over this target namespaces. So I'm going to call this, I'm going to say for target. In target namespaces. We're simply going to loop over that and apply the post each one of the target namespaces. Instead of the argument space namespaces. There you go. So let's go ahead and test this out. As usual, I'm going to reload the module because I made some changes. We do not need this anymore, so I'm going to delete it. And let's, let's test out our function. Let's do, let's go over our test again. So if I have nothing selected, we get the warning. If I only have one selected, we get the warning. And now if we, let's make a change in the pose. Let's say this. Let's actually select the hat as well. We're going to apply all of those controllers that I selected. 21.2 rigs. Run the code. It's going over that tributes and perfect, there you go. So even the hat on the other reference while supply, because it did find that controller did find that attribute. Let's keep the hat on this character right here. So apologies for my horrible posts. I know this is not very appealing, not a very good silhouette. But as you can see, all of the poses are exactly the same, which is what we're looking for. So there you have it. You have created the back-end of your tool. Back-end means the functionality of the tool. Now, I did say we're going to create a UI that will be the front end of the tool because as much fun as it is to run code, it's even more fun to press a button. So let's go ahead and talk about how we're going to approach the UI. 27. Intro to UIs: Let's go back to paint and design or UI. So we need a window which is the main container for our tools. Let's just quickly draw this. This will be a window. Now for what we need. We don't really need too much for this tool. We'll need a button, but that is way too simple. So let's just add a little bit more than a button will have this button here will have small title. It's called a label that says suppose transfer, something like that. Does it need to be perfect? It's just a mock-up of the yarn. So let's try to replicate this in code. As I mentioned, the start are you, are you need a window. So there's a command from the same this library. Let me import that as usual. My inputs in the S, there is a window command that we can use to create a window, and then you have to show a window. So before we go ahead with this, Let's have a look at the documentation. This window layout for this window function, sorry. Okay, So this is a window function. Let's go down to the examples and see if we can get something from them. Okay, right here, make a new window. Seems like this, exciting. A few buttons. Let's, let's copy this code and see if we can start from here. And we paste the code here. Let's just go ahead and run it. Alright, there we go. We have a window, we have a couple of buttons. We have a title about them that's doing nothing, and a button that's closing the UI. Okay, so let's see what's happening here. Let's ignore the arguments for now. We'll get back to those in a second. First of all, we're creating a window and we're storing that window in this window variable. So let's see what that window variable, it's going to simply run the window command and print the variable. Okay? So that is a piece of string. That is because just like objects are notes in Maya, e.g. if you're working with a controller, if I select this controller, we interact with the controller through its name. That name represents whatever object is happening inside of Maya. That is similar with us. You guys have a name and you interact with those two eyes through their name in this case, without giving it a name, explicit name, man has given his creatinine for us. Then we start that name in the window variable, which is really just going to be a string. And at the end of this code, we show that window by using its name. We can, if you want, give it a name, e.g. my UI. And if I run this and print it, it will print the name that I gave. But for now, let's not worry about that. It's not necessary for this example. Okay, let's, let's keep going and see what else is happening here. Next up, we're creating a layout. Layout are very important. Layouts are basically the thing that the containers that manage the UI. And let me try to explain this concept. Here are independent. So there's two main types of layouts. There is column layouts and there's Row Layout. There's more types, but these are the main ones that you are going to be using. A column layout will flow downwards as a column from top to bottom. So if you add a widget with Jerry's anything like a button, like a TextField piece of text. So if you add a column layout, there will be flowing downwards. If you add widgets to a row layout, there will be flowing from left to right horizontally. So this concept is, is basically how a UIs are managed in Maya, but also how they work in other UI frameworks such as q, D, e.g. they will entity there will be called horizontal and vertical your eyes, but it's a similar concept as column and row layouts. And how it's important to know that you can put layout inside of all their other layouts. And that is how you can create more complex UIs. So imagine that you have a big column layout, e.g. you have a column layout. It's flowing again from top to bottom and you're adding buttons to it. And then suddenly, let me change the color. You add a row layout to it. And then you're adding buttons to that row layout. So once again, that will flow left to right. The buttons will be coming this way. And then you add another row layout. And now this is where it's getting complicated. And inside of that row layout, you add column layouts. You add three column layouts that are flowing from left to right. But when you add buttons or widgets inside of those column layouts. Those widgets will be flowing again top to bottom. So this is, this is how you create this flow on this more, more complex UIs inside of Maya. So keep this concept in mind as we work with layouts. Let's go back to Maya. So we're adding a column layout. Now inside of that column layout, we have two buttons. Then we're setting the parent. Let's, let's keep this for now. I'll get back to it in just a second. Then we're showing the window. So what happens if we run everything without showing the window? Your UI will be created. But it hasn't been shown. You can show it at a later time, but in this case you need to run show window to display that. So as you can see, as I mentioned, because we're in a column layout, the buttons are flowing top to the bottom. So if I add a few more buttons, they will flow up the bottom. Now, what if I were to insert a row layout here, cmd S dot row layout. Let's add that in. Too many children layout. Okay, I think we have to define the number of items for the row layout. Let's have a look at that in the documentation. Rolling out. There you go. Okay, let's see. The examples are usually a good indication of what to do. Row layout, number of columns. Okay, there we go. I think number of columns is what we're looking for. So we are adding, Let's just add two buttons. Just to prove. To show this example, let's say number of columns equals to. Show that. Okay, there you go. So we have the first layout, column layout top to bottom, then a row layout going from left to right. Now let's add another column layout right after this, and then two more buttons. Let me leave some space so that it's a little bit easier to see and add some comments. So this will be a row, this will be a column. And the first one up here, this will, this is also a column layer. Okay, let's maximize this. But see what happens here. Too many children layout. It's because we're adding the column layout to that row layout. So that column layout is another children to that row layout. So if we say we want three columns in this row layout, it should accommodate that extra children. Extra child. There you go. So this is what's happening here. Let me take a quick screenshot so that I can do, I can draw over it. What's happening here is that we have a column layout first, going from top to bottom, column layout. And then we have row layout, which is this one here, which is going from left to right. But now inside of that row layout for inserting another column layout, which again flows from top to bottom. That goes back to this idea that I talked about of layouts inside of other layouts. Now, what happens to that set parent function here? Whenever you create a layout, without layout will be the current parent. So whatever you add afterwards, right after that layout will be put into that last layer that you created. So this row layout, it's actually been added to this column layout. And this column layout is being added to that roller. Yeah, So the current part in that the moment is this column layout. So let's imagine, let me run this again. Let's say that we want this column layout. So these two buttons to keep flowing downwards this way to be the skip over the row layout. The way to do that is by using this set function. So first we create our column layout, we add two buttons, then we create a row layout. The current parent is at rolling out. Now before we add the next column layout, we want to change the current parent to be there already. The first column layout, which is where we want to add a new one. So that is what this is doing. If I, if I say set parent dot-dot, this is Maya's very specific syntax. To go back to the previous part. So you can say skip over the current parent and make the skip over the last layout and make the current parent to be the previous one. So we're going to point the current parent to be column layout. So now this new column layout should be added to that originally, original or first column layout. Let's see what that looks like. There you go. So let me take another screenshot. Perfect. So once again, just to make this as clear as I can. We have a column layout. Ashley. I'm exaggerating there. Sorry, I'm not exaggerating. I'm not quite right. Like this whole thing is a column layout. Then inside of that we add these buttons. And then inside of that column layout, we added this row layout. Then we, the current parent when we create this row layout is that layout, but the row layout because the current parent. But remember by using set parent over here, we change the current parent to be the main layout again. And that's when we create another column layout. And that's when we add the new buttons. So hopefully that makes sense. It can be a little bit confusing. But, but again, once you get into the idea of layouts, the way you create UIs will be much more efficient. So now let's go back and see what these arguments are doing. So in the window, we have a title, that's the name of the title of the window. Let's call this. Let's change this to window title that simply a string. Then we have icon name. See what that is in the documentation. Oh, interesting. It doesn't actually show up. That is very unfortunate, to be honest, I've never used that argument before, so I'm not really sure what it does. It doesn't seem to give us any sort of icons in the window. So I am simply going to remove it. Now with height, this is, I mentioned before, this is a tuple. This is simply a list that cannot be modified that has two items. This will be the width, this will be the height. That is just, these are pixels that is saying that the window will be 200 pixels long and 55 pixels tall. Which is why when we first created, it doesn't show all of the buttons because we're constraining that if I remove this argument, there's no constraints in size and it will simply show all of those buttons. Excellent. So I just have all column equals true. What happens if this is false? Let's simply set it to false and see what it's doing. Okay, Well, our buttons are not expanding with the window. They are not fixed size. So that is what I just doubled column is doing. It's making the buttons or the widgets inside of it be adjustable. Except for the row layout. Because the row layout needs to manage its own expandable widgets. So let's, let's keep going. Where is my window here? Close all of those copies. Okay, now we have the buttons. Label is simply the texts that is going to show up in the button. And command is a piece of the piece of code that will be run with when you press the button. Now a command can be either a string, and in the example they're using parenthesis, but that is not necessary. It can be a string, in this case is a string that is deleting the window. This is a command for deleting a UI. And we're passing in the name of the window we created. So we're basically diluting that window. Let's say, let's make this button up here, do something. Let's call it, do something. And I'm going to add a command to it. The command will be simply a print statement, but now it's going to be in the format of a string. So it's kind of a piece of code wrapped inside of a string. So we're going to say print doing something, e.g. let's just do that. Now. Notice that I'm using the two types of quotations, single quotes and double quotes. That is because if I were to use single quotes here, maya would think that this is the end of my string. So what I'm doing, remember to, remember that I mentioned to create a string, you have to match the beginning and the end of the string, the type that has to be the same. So I'm basically saying, okay, inside of this string, is this outer string. There is a string inside of it, which is what I'm basically passing into that print statement that I'm using a sacrament. So let's run this. Let me click on the button that says do something. And you got now is doing sprinting, doing something. Now to that command. You can also pass in a function. So let me show you what that, what that looks like. If I call my function, do something e.g. that it's not that let's just call it test, function test. And I'm going to make this print, just print, doing something from inside the function. Okay, So instead of passing in a string, I can actually pass in a function. But I can pass in the function without calling the function. Notice how I'm not using the parenthesis. If I use a parenthesis, I'm calling the function and I am giving the command argument, the result, the output, the return from that function, which in this case is a non, because I'm not returning anything. What I wanted to do is give that command arguments by actual function without colon it. That means that when I press the button, Maya will take my function and it will call it itself. So let's see what happens in that case. Before I do that, let me clean this up. I don't think we need this extra buttons as we have already gone through the example. Let's keep it simple. Do something. Okay, it is doing something but it's not exactly what we expected. Let's see what's happening here. It is saying that the test function takes no arguments, but one of them was given. Let me close the window. So that is true. I'm not taking any arguments. But somehow when I press the button, my eyes passing in an argument to my test function, only one. So let's inspect it and see what it is. I can simply catch that argument by passing in an argument to my function. Let's just call it x. Let's simply print, Let's print x. Let's see what's, what mine is doing. Is that it's printed false. So it seems like my yes, passing in a Boolean to the function. For some reason. I'm not really sure why. Now, the thing about it is that we don't really need it. We don't really care about it. So there's two ways you can get you can handle this situation. One of them, just like I did here, is accept an argument because you know, you will be expecting that from Maya and ignore it. Simply do nothing about it. You can accept it, but not use it inside of the function. As you see. There you go, my button will be working. Another way of doing it is using a special syntax that goes like this. You do an asterisks or a star, a star, a star, a new type, args. Now, these can be any word you want, but by convention you will see people do args. What this is doing. This is kind of like a catch-all for any arguments that you throw out the function. You have access to them if you want. A lot of the times people use this to ignore any arguments that might be expected but are not needed. So let me, let me show you how this star args that syntaxes is working. So if I say if I call my function test and I pass in 1234 arguments, it's going to work. So this syntax is basically catching anything I throw at it. And I'm not really using it. But now let's, let's see what, what's actually happening. Let's print those arguments. Okay, very good. So this is what's happening. So this star arc syntax, it's basically catching any arguments that I give to it. I'm putting them inside of a tuple. Once again, remember that a tuple is like a list at cyclist, but you cannot modified. That means that I can actually get e.g. the first argument that was passed, that was passed in, in this case will be the number one. I can simply print that. There you go. So for cases like this where the UI, for some reason Maya as giving you an argument and you don't need it. I recommend using this index. You can use that syntax along with other arguments, like if you're expecting an actual argument, sure, expecting a baby, and then anything that might be thrown at your function. This is also, also valid. Okay? So with that said, let's go back. Let's go back to simply using that sort of net catch-all star args and passing that to our command for the button. That is doing something. Okay, perfect, so let's use this as a start for our actual UI. 28. Creating the UI: Okay, we don't need this test function anymore. We don't need this comment. I think this is a good starting point. Let's, let's change the title of our window. Let's call it owes transfer tool. That will be the name of our window. And let's make the columns I just have all yes, because we want the buttons to be able to expand with the columns. We do not need two buttons, we only need one. And let's say the label should be transfer. Okay, and command, let's, let's leave the command out for now. We will add a function through it in a second. Actually. Let's call this transfer selected. Okay, there you go, a single button. We cannot see the name, the title of the window. So that would be nice to see. We can use the width, height argument we had before. Or I also know that the window has weight and height arguments individually. So we can simply add the width. We can say, let's make it 300 pixels, e.g. maybe a little bit bigger for 100. That was too much, let's say 350. Okay, that sounds great. So now we have our button. The button is a little bit thin. So let's also make the button a little bit bigger. We can have a height argument in the button. Let's make it 20 pixels tall. Not enough. Let's double it 40. Okay, that seems a little bit better. Now, I also said that we wanted to add a little bit of a label on top like a piece of text. There is a text UI components in this here in this library is simply called text. Let's say this. Let's go down to the examples. There you go. That's all we seem to need about it. So we can add a label and we can align the text. Let's, Let's get this example. Let's copy from this example. I'm going to add that piece of text. And the label will be, let's just say post transfer all caps. And let's align it to the left and two, what happens? That's not great. Let's keep it in the center. And it's also very small. The text is very small. Let's see if we can do something about that. Perhaps we can change the height of it as well. Let's say 60 pixels. Let's say 50. And let's make the button a little bit smaller. Let's make the button 30. Text is a little bit okay. I'm sorry. I'm going to take the baton back on at this point. I'm just messing with the numbers. This is not as important, but feel free to play with the sizes as you wish. Um, I think this is pretty happy with this. So we have our texts, we have our button. And before we move this into PyCharm, I want to show you something that will come in very handy. And that is tooltips. Now, a tooltip, also known as a tool clip, but the official name is tilted is the texts that happens when you hover over something in the UI. As you can see when I hover over these buttons, you get a little bit of help. And also down here in this status bar, you get a little bit of help from that is basically telling you what the button is going to do. So we can add that in all of our UI elements. And that is called an annotation. So the argument for that is called annotation, but the actual name for it is tooltip or tool clip is also called in Maya. So if you do not see it like I do, like when you hover on something, if you don't see that, you have to go to Windows, settings, preferences, preferences. Now in under UI elements, sorry on their health, There's interface help. On their pop-up help. You will see this display two clips. You want that box checked. If you don't have that checked, you will not see that. Okay, so there you go. So we can add those. Once again with the annotation. Argument. So annotation equals, I'm going to press Enter here. This is, this is valid by the way. If I press Enter, it's valid and Python will not complain. I'm going to say annotation, transfer between characters. This will be the annotation on the text. Actually I'm going to go back, go back to this single line. And then the annotation in the button. We'll be annotation equals select two or more characters. So this is the help. So in that case, since this is the sort of the instructions for the total, I'm going to write out something a little bit more detailed. Transfer posts from forest selected brick to the rest of the selection. Okay, this might not be a little bit long, but that's okay. So now when we hover over these buttons, you will see that the toll dig a little bit of help. And you also see it down here in the status bar. Okay, perfect, So our button is not doing anything. Let's move this into a function in PyCharm in our module so that it lives in the file. And then we can connect the button. Alright, let me copy this and create a function up here. At the top of the file. I'm going to call it Show UI. Which by the way, it's usually a good idea to keep your, your backend, as I mentioned before, which is your functionality separate from your front-end, which is your UI. Normally I would have this function in a separate module alongside of this post transfer module. In this case, because it's a very simple UI, I'm going to leave it here and it's just for this first project. But in the next one you're going to see the separation between the functionality and the UI, which is usually a good practice to do. So, you can call your functions directly and you can have your UI completely separate, completely agnostic from your actual functionality. But for now, let's like I said, let's keep it here. I'm simply going to paste my UI code in here. And let's test it out real quick. Let's say import, pose, transfer. Okay, and if you haven't restarted your Maya, once again, you want to reload the module. Let me reload that. Okay, there you go. And I can say Posts underscore transfer, dot show other score UI. And let's call that function. There you go. Okay. Now, final step is to connect the button. And the button here will take that command. I'm going to press Enter, as I said before, if you have something in multiple lines, it doesn't matter. Python will not complain. You can have each argument in a different line if you want. So let's just do that for the sake of the example. Command equals this will be my transfer selected function. There you go. Now let's test this out. Error transfer selected. Ok, OK. Of course we just talked about this. We need, I'm going to use that star args. That's sort of catch-all catch-all syntax to catch the argument. And while I'm at it, I'm going to add a docstring that I forgot to write. Transfer pose forest selected rig to the rest of the selection. Now star args is a special, we don't really need to add a description for this, but Ashley, Well, we do because there's a purpose to it. Let's just call this catch. All four arguments passed in by my we're not returning anything. So we do not let that slide. Yeah. Okay. And now poorer show you, I may simply let's just add a simple docstring. Show the UI for the tool based one, mxnet, less explanation, but let's still add it in there. Okay, Let's run this. Let me close this tool. There we go. Now it's working. Please select more than one rig. If we have nothing selected, it tells us to play select something. And lastly, let me go ahead and create a nicer post and this to properly test our tool. Okay, we have a bit of a better pose, not amazing neuro masterpiece. I'm sure I know you can do better than me, but it's nicer than the horrible thing we had before. So let's put the tool to a more useful than nicer test. So let's select all of these controllers. I do not want to select the basement role because that will just move the characters to this position. And there you go, select it all my controls, target, one, target too. Let's transfer. The process is going through a lot of controllers right now is a little bit slow. But there you go. Our poses, let me turn on the curves. Look exactly the same. So there you have it for stole your first-year, I share this with your friends, use it when your work. This is an actual working tool that you can benefit from. 29. Project Recap: Now, to close the project, I would like to do a small recap of what we learn. Let me write it here. So we'll learn a lot of things. Will learn about lists and tuples. We'll learn about dictionaries, we learned about for loops, how to iterate through elements in a list. We'll learn more about how to handle strings. We'll learn more about if statements will learn more about functions. We learned how to grade your eyes on how to handle, how to manage UI elements using layouts. We'll learn how to interact with objects and notes in the scene. How to set and get attributes, how to interact with rigs, and really how to split things apart so that we can get the information and the data that we need. Now, that is one of the main takeaways of this project. That now using these two data types and these are the main datatypes for storing data. Now you know how to query, store, and manipulate data for whatever purpose that you need. Now remember that we use list for querying and storing selections, for making lists of controls, making lists of namespaces, attributes, etc, etc. Can we use dictionaries to create a mapping between attributes and their values? So these are very simple concepts, but they are extremely powerful and they will translate into more complex use cases for you. So congratulations on completing the project, and now you're ready for the next one. 30. Deep Dive - Strings: We're going to learn more about strings. Strings in simple terms are a sequence of characters. You can think of them as text. So you can create strings with quotes. Can be single quotes, double quotes, or triple quotes. To in this case there's three triple quotes, either three single quotes or three double-quotes. So e.g. this is a string, and three single quotes are also a string. But triple quotes are usually reserved for either blocks of text or docstrings. Usually they are reserved just for docstrings. But you could create, create blocks of texts. So when you have single and double-quotes, string one, e.g. string two, you can only create this string as a single line. But when you have a doc strings, as long as they are opening and closing that string, you can write inside of it. And there the new lines will be preserved for you. So this is kind of like a block of text. And that will be the same case with the triple, single strings. So I sort of seeing here, as long as the starting and the ending quote is of the same time type, the string will be valid. And with the same, same logic. If you want to have quotes inside of your string. So let me, let me just print this. First. Let's print that string. So as you see, it prints without the quotes. Let's say that I want to print it with boots. I can put double-quotes inside of it. There you go. If I wanted with single quotes, there's two ways to do it. One, I can switch the quotes around. Or two, I can skip the quotes. Let me show you what that, what that means. What happens if I put a quote here? Well, Python will think that you are in the new string here. This will, this is basically an empty string and it will give you an error. So how can I make it so that I can have single quotes and still have single quotes inside. You can escape those single quotes by using a backslash. And that will work just the same. So you're basically telling Python, this is not the end of my string, and this is also not the end of my string. You're escaping bypassing those, those quotes. But as much as you can, It's easier to simplistic to mixing the types of quotes. This backslash serves another purpose. There's a special types of characters called escape characters. These backslash and just note it's not a forward slash, it is a backslash serves as the what defines escape character. Let me show you what I mean by that. The most common skip character is backslash n, and this will create a new line for you. So let me show you what I mean. Let's type line one, backslash n line to eliminate print this. So you see that we're not printing the backslash where we're getting a new line. We're if I didn't have the backup backslash, I would have a single line. So that is one of the most common escape characters. Another one is backslash d, which means tab. You're kind of indenting as if you were pressing the Tab key. You can mix them as well. We can have a new line, backslash n and a tab, backslash, backslash t. And then you'll have something like this. If she wanted to have more indentation, of course you can do more tea and other T. So what if you want to actually print backslash n? Let's say that you want to have these exact string printed. So right now if I run it, I'm going to get a new line between, between these two substrings. A substring is simply a piece of a string. So line two in this case is substring. Line one is a sub string, and the escape character n is also a substring. So let's say that we want to print exactly this. Well, you can actually escape, escape character and you do that using this same backslash. What is going to happen is this backslash. The first backslash, is going to tell Python not to read this as escape character is a special character simply as a, as a raw string. So if I printed, now, you get that. So that is how you can escape it. Now, there's another way to skip or escape or kind of ignore a special character. So if I print this again, I get a new line. If I type an R before this string, that will give me the same result. Now, they are is usually reserved for something called rejects. Which is a special type of expression for searching strings. But they are in this case, it means that this is a row string. It means that there will be no interpretation of special characters. He will be rho, simply whatever is inside of the string will be printed as it is. So that is another way to escape it. So this is especially useful when you're, when you're dealing with parts inside of inside of Windows, file path might look something like this. E.g. let me make a new variable. A file path might look something like C colon dash a path to a file, dot dx dy, e.g. There you go. Let's say this is a folder or file path. So a couple of folders or directories and then our file, if I were to print this, it printed exactly what I typed. And the reason is because I'm using the forward slash. So this was my mistake. Sorry about that. Let me quickly switch that the backslash. So as you can see, the forward slash is completely safe to use, but the backslash will be interpreted in a different way. So you see that my, my a was changed. Let's turn into these character. Then after path, we have the tab which is t. Then backslash F was turning to this other character. That's clearly not what we want. So you could simply either escape those characters, but you kinda have to guess or you have to know which ones. We need to skip. A, we don't need to escape P-I. We do need to escape t and we need to escape f. So this is the result that we want. Or we could use raw strings. Just put an RB far behind the string and we get the same result. So strings are used for a lot of things, for accessing file paths, for creating variables, for declaring names or labeling things. But the most common operation you will do with strings, or one of the most common operations is called concatenation, which basically means putting strings together. So let me show you what that might look like. Let's call this have a variable called S. We're going to start it as an empty string. And I'm going to say S plus equals hello. And I'm going to say it down here as plus equals space and the S plus equals world. Now let me print S. Now it says hello world. So what's happening here? This notation plus equals, this is basically equivalent to saying S lost, hello. This is what's happening here, is we get the previous value from S, which is an empty string, and we add hello to it. And then we store that. We basically override the S variable. Then down here, this will be the equivalent of saying the same thing again, take the previous value of S, which is what we just did. And again, storytelling, which is, which is basically overriding that same variable. So by using the plus sign, you can put strings together, but there's other ways of putting strings together. One of the most common, and I would say the most powerful ways of concatenating strings is using the dot format function. Let's, let's see how that works. So let's create a string. Let me do two curly braces, then an underscore, then tumor curly braces. So what's going to happen is these curly braces will become placeholders or replacing something. Now, if I were to print S right now, we're going to get exactly that, which is what we just typed. But if instead I do dot format, that format is a function that is built into strings. And I pass in two variables, let's say two strings, hello and world again. Now let's print it. So as you can see that format we'll use the curly braces as placeholders to replace with whatever I put in here. And this can be something, something else that is not a string. This could be a number, e.g. if I type the number 100, then that gets replaced format. It's smart enough to know that it can, it can do that with strings. If I were to add using the plus sign, a string and a number 100, I will get an error because Python cannot concatenate a string and an integer. But the dot format function is smart enough to know that. There's another way of using the dot format is by using variables. We can specify variables inside of these curly braces. I can say variable one will be high. Variable two will be, let's just say number. Let's say it's numb. So if I run this right now, we're going to get an error because it's going to complain about a key error because we're basically not telling the function which, which one of these two values belongs to which key. So let's fix that. I can say that hi, hello will be high, so high equals hello and num will be 100. There you go. So this means that since we're using this keyword arguments, it means that even if we flip the order, you'll go into work. Hello, one-hundred. Now, what's very cool about this is that you can reuse the same variables that you're, that you have here. So let's say that you want to say hi a few times in this string. You can simply say, it doesn't need to be underscores. Let's just put a space. And I will use height. So now we're replacing four different places. But we are using only two variables. So the variable hi, well basically we placed inside of this placeholders. If we didn't have these keywords, python would complain. So let me go back to how we had this originally. Say, hello. One-hundred. Python will complain because we haven't provided enough replacements for these placeholders. So in this case we would have to say hello and hello, which is a lot of typing. So you can see the power of using the keywords. Now, there is one more way to use the format function. And that is by using, by using indices. So let me, let me quickly go back to just two placeholders. So I could do this once again and it will just be replaced in order. But I could use, also use indices. And what these numbers will represent. This will represent the order in which you put your variables here. And we start counting at zero the same way we do with lists. You can see that as the same result. Now, if I were to flip this indices, now we're flipping the order. And now we can do this same thing as with the keywords. I can say that I want to say hello here. By place that put an index zero. Let's say Hello again. Let's put the number 100. There you go. So once again, simply using two variables, we can mix and match by using the index numbers. Now let me show you one more way of concatenating strings. And this is kind of the classic way of doing it. I'm going to write percentage sign s, going to leave a space and I'm going to simply type world. Now, this will be our placeholder. And to replace it, I'm going to leave a space, put a percentage sign, and then I'm going to type something else. Let's say Hello again. Now you can see whatever I type here to the right will be replaced by this percentage sign s. By type a number that will be replaced. Now, I could also do multiple. We're seeing the sine S. But in this case I need to provide a list or a tuple. So I'm going to use a tuple. I can say hello. Palmer planets, this case. And each one of those will be replaced by h, one of these items in the tuple. So they all kind of give you the same result. My preference is to use dot format because it allows you to do a lot of, a lot of different combinations and gives you a little bit more flexibility. But you can choose whatever works for you depending on the code that you're writing. Now, under the hood, a string is a little bit more than just a sequence of characters. It's an iterable objects. That means that you can iterate over it the same way we do with lists. So let me show you what I mean. Let me create another string. Let's call it Barcelona, e.g. now, similar or the same way as we did with lists, I can. Provide an index by using these square brackets. And I will get, if I provide index zero because we start counting at zero, I get the letter b. If I do index number two, I'll get the little r because it's the second index. If I asked for my index minus one, which is the last index in the list or in this case the string. Then I'll get the letter a. Now, by the same logic, we can iterate over the string using a for loop. So we can say for ins, print i, and it will print each one of the letters in that string. So under the hood, a string is more like an array or a list. Just so happens that every item inside of it, it's a character. And it's also special in that we can do specific types of operations, just like we saw with concatenation and others. Specific operations that are built in for strings. So let me show you some of those operations. One thing you can do with strings is check or sub-strings. The substring is basically a part of a string or something that you would expect to be inside of a string. Let me rename this variable string. Let me create a new variable called sub string. Let's call, let's make this substring, have the substring, it's a loaner or slumped in this case. Then I can use the in keyword to do a comparison. I can say sub-string in a string. So this operation will give me a Boolean. We will say either true if the substring is in this string or false if it isn't. So let me store that result in a variable called result and print that result. There you go. It's true because the substring is inside of Barcelona. Now Barcelona doesn't have an H. So let me look for that. There's no h in Barcelona, so I get bolts. Another way to check for substrings is with a built-in function in strings called starts with. So let me named myself stirring bar. And let me replace this operation with string that starts with substring. So this will check if the string Barcelona starts with bar. Once again, we get a boolean from this. That is true. If I replace it with high e.g. it will say false because it doesn't start with that. Now notice that this is also case-sensitive. So if I, if I do lowercase bar, you will say false because it doesn't install with that. And there's the opposite end of this equation which is ends with. Now let me run this. It's false because Barcelona doesn't end with bar, but Barcelona does end with NA, e.g. so that will be true. So these are two built-in functions for strings. You can also change the casing on a string. There are two other built-in point. There's many built-in functions for strings, but I'm just showing you the most common and useful ones. Another one is Opera. This will turn your string into an uppercase. So let's draw that in the same variable. Opera will be uppercase and lower will do the opposite, which is lowercase. So in this case there's only one uppercase letter. So it will turn that into lowercase, lower. And by the same convention, there is a function called title. This will, this will make your string in the form of a title. This is very useful for things like firstName is e.g. if we wanted to say John Smith, title that every capital letter or every first letter in the string. It's smart enough to know about spaces. E.g. will have a capital letter. As I mentioned, there's many built-in functions in strings, but let me just show you a couple more. Before we wrap this up. You can replace parts of a string. You can say string dot replace. This function will take in two arguments. The thing you want to replace, let's say we want to replace this last name. And the substring you want to replace this width. Let's say we'll replace this with Williams, e.g. replace that. This is very useful for removing things like spaces, e.g. if you want to replace a space, can type in a space. Just eliminate the space. You can replace it with an empty string, e.g. and I will simply eliminate the space. The last thing I want to show you is how to split the strings. So let's imagine we want to split the name by the way, this space here. Let's use this split function. And as its first argument, it will take the character you want to split by, which is a space. So let's just give it a space. Let's run the code. So I get a string, sorry, I get a list. I gotta list with everything that was before and after that space. Now, if I have more spaces, let's say I type something else here. Now we have three spaces, which means we get four sections. Now if there were no spaces at all, it will find nothing to split by. So it will simply give me the same string, the original string, but as a list. And this will work with any characters you choose to split with. Let's say that I have some periods and I want to split using those. Now I get the same four sections. Now, there's one more argument that this function takes, and that is the number of occurrences that you want to split. So by default, they will do all of them. Every single period that it finds in this case will be, will be split. But I can ask it to you simply do the first one, e.g. it will split the first one and then ignore the rest. I can ask it to only do two. And there you go. The first two will be split and the last one, we'll stay as it is. So this was just a short introduction to strings. There is a lot more you can do with them. I really encourage you to experiment and play with them. And I also highly recommend that you read the documentation online for Python, there's a lot of stuff in there. Once again, this was just a simple introduction, but I showed you what I think are some of the most common operations that you can do with strings that will help you with your day-to-day coding. So hopefully you are more familiar with strings and hopefully it was clear enough that you will be a lot more comfortable working with them. 31. Deep Dive - Loops: Alright, let's learn more about loops in Python. Looping basically means doing something multiple times over and over again. There is two main types of loops in Python. One of them is a for loop, another one is a while loop. So let's go through the for loop. First grader for-loop. It's very easy. You use the keyword for you. We'll say for something or for each item. So let's say for item in list, let's create a list. Let's just have a list of numbers, 12.3, and then you have a colon. And then inside of that loop you have this indentation. Inside of that loop, you will do some type of operation. So right now, let's just print the item e.g. let me run that. So as you can see, we're printing 123. So what's happening is we're basically saying that each item inside of that list will be stored inside of this variable called item. Now I chose that variable, you can call it something else. You will usually see something like I. So for I in the list, we can print I. There you go. So what this is doing is just going through each one of them and replacing it with the value of that item inside of the list. Let's change this up a little bit and say 12.3. Now, same result. So now let's say we want to print the numbers 1-100. So there's a function called range that basically gives you a range of numbers. Let's say we want 100 numbers. Let's print I in range 100. Let's print something happened, we're getting 99. Let's see why. Let's actually make this wrench a little bit smaller. Let's say five, e.g. we're getting the numbers 0-4. So that is because the range function starts counting at zero. We could fix this. We could say, we could pass in an actual wrench. If you, if you just passing a single number, it will start counting at zero up to the number before that, that number you provided. But if we provide 1-5, in this case, we're going to get one through four because this number, the second number, It's not, it's exclusive. That means you have to add one to get to the desired number of the range that you want. So if we say range one through six will actually give us one through five, it can be a little bit confusing, but that is how this function works. There's reasons for that, of course, but you really only have to know that that is how it works. Excellent. So branch will give us one to five. Now, if you wanted to see what wrenches actually doing, all it's doing is creating a list of those numbers, were still simply working with a list. Excellent. So we're storing this value of each one of those numbers in these variable I, but it doesn't mean we have to use it. Let's say that we want to print hello friend 100 times. We can simply say, give me a range of 100 numbers. I don't care what they are because I don't need them. I just need the list to be able to loop over it 100 times and print this string 100 times. Hello, print, there you go. But we could also do a little bit of math. We could say Hi times two, e.g. so every item in range will be multiplied by two. We print that. And there you go. So you get that result. So the for-loop will use an iterable object, something that it can iterate through. It can be LEDs usually list, it could be a tuple as well, e.g. instead of a list, let's make a tuple, which is like a list, but you cannot modify it. So it will print it three times. Or we could also use a string, e.g. we can say a, b, c, d. We can, we can iterate over a string. And what is going to happen? Let me print r in this case, is it will simply go through each one of those characters in the string. By the same logic, we could actually print anything we want and use that string as the, as the thing that will give us the number of loops we're going to do with our for-loop. So that is how the for loop works. Now, the while loop doesn't take something you can, an object that I can iterate over, it takes a condition. So what this is saying is, while a condition is true, while something is true. Do something inside of that loop. So in this case I could print once again, hello friend. Now, if I do that, if I run this code right now, we will enter something that is called an infinite loop. That is because true will always be true. So it will never stop something you need to take into account. And you have to be careful about this when you're using while loops, is that you have to make sure you update your condition at some point. So let's, let's do something that we can update. Let's say we want to start counting numbers. We will start the count at zero. And we're going to say, while count is less, sorry. While count, yes, while count is less than five, e.g. print hello friend. Now, as the series right now this code will run forever again. It will be another infinite loop because counting this case will always be less than five. Unless I update my variable. I can say count equals count plus one. I'm basically adding a one to the count. Every time we end, we will loop over, we enter this loop. So what's going to happen here is the while loop will check count, is count less than five. Yes. Then do these operations here. Now after printing hello friend, we're going to add one to count. That will make account be one, right? So we're going to enter the loop again, or the while loop will check again, is count less than five? Yes, print hello friend. Then we will add one more to count, and so on and so forth until we get to five. And then the while loop will check its count less than five. No. Then it will exit and continue on with whatever is after the loop. So let me print something after the loop. Just to make this example a little bit more clear. And let's simply run this code. Let me clear my output window here. Let's run this code. As you can see, nothing happened. Why is that? Well, because I forgot to reset the account because count is five. So the rollup is asking is count less than five? No, it actually doesn't do anything. And it goes straight to printing the while loop. So let's put the count back to zero and run the code again. There you go. So now we're printing hello friend 12345 times, and then we exit. So let's check that count variable. You can see it goes from 01234. The last time it runs will be five, which means the loop doesn't enter. Excellent. And then we print the, whatever comes after the loop. By the way, I can, I can make this line of code a little bit simpler. But saying by saying plus equals one, that is a shorthand way of saying simply add one to this variable and it works just the same. So those are while loops. Once again, they work with that condition. So you have to be careful to update that condition every time you're inside of the while loop. Otherwise you'll run into the danger of creating an infinite loop, which will basically crash your application. Because after a while it will run out of memory or it will enter something called segmentation fault, which means that it's trying to access memory that you shouldn't have access to, so it will simply crash. It's not the end of the world it happens to everybody is going to happen to you at some point. You just have to restart your work. So the way to stop the while loop is again by using the condition. But there's another way to stop the while loop. Let's say that for some reason we want to stop in the account. If the account equals three, we want to stop. So we could simply say if the count is less than four, stop. But we could also say if the count equals three break. Now let's run this. There you go. So now we're stopping at three. So you might be thinking that means that we don't need the condition. We could simply say, While true. Run all of this. If count equals 33, we simply break. Now, as long as we're still increment the count variable, this will work. Now this is weird. This is not really a nice while loop, but I'm just using it as an example to show you. Technically this should run forever. But the break, this break statement will basically exit the loop. Let's see that in action. There you go. We printed we printed hello friend three times. That is because I removed my print statement for the count variable. Let me add that in. So we don't want to 0123 and we simply break. So we don't, we don't print three after that. Now we can use the same break statement to stop the upward loop. So let me go ahead and loop over 100 numbers again for I in range 100. And let's print that. I let, let me do it 20, so that it doesn't take too long. So we're going to print from numbers 0-19. And then we'll print this string. It switch that over to say. So if we wanted to stop the for loop at a resort number, we can say if I equals equals ten, e.g. we can break. There you go. So once we get to nine, the next I will be ten, and then we break out of the loop. So that's another way to put a full stop to the loop. Another very useful keyword is the key, this statement, and it's continuous statement. Now this is going to work for both for loops and while loops. What the continue statement does is it will stop the execution of the code at that point and go back to the loop and go into the next iteration of the loop. So what this slope is supposed to be doing a sprint every number 0-19. But we're putting this check if I is the current iteration is ten, continue, which means we're not going to get into this print statement. So let's see how how that works. There we go. We go 01 234-567-8910, and we're going to 11. Now, let's make it a little bit more obvious. Let's say that we only want to print numbers that are divisible by two. So let's say if I modulo two not equal zero, this means that the number is not divisible by two. We are going to continue into the next iteration, not print that number. There you go. So we've got 0246810. You see that basically every other number is skipped because they are not divisible by two. Now, this is the equivalent of saying if the number is divisible by two, if I modulo two, the result of that is zero. Rent. I otherwise don't do anything. Yes, you can see that it's the same result. So it's the same result. We're just using the continue statement to reverse that logic. And you might be wondering why, why do we do that? Well, imagine that you want to just keep writing some code here. So more code, and then more and more. So this gives you the ability to not have to deal with the indentation and simply putting a quick check to stop the execution of the code at whatever point you want. And go back and get into the next item of that, whatever disease in this case it's a list of numbers. So there you have it. For loops and while loops, they are very simple concepts, just doing something over and over again. But they are extremely useful, extremely powerful, and you will use them quite a bit in your coding journey. 32. Deep Dive - Lists, Tuples, Sets: Let's talk about lists. Lists are a collection of objects. And what I mean by object is any type of Python object. It can be a string, it can be a function, an integer, float. It can even be another list. You can have lists nested within each other. I'm going to show you that. And I also wanted to talk about two other types that are tuples and sets. So we're going to talk about these three. All of these three can be seen as being similar in that all of them store a collection of objects. And what you can accomplish with tuples and sets. You can also accomplish with lists. For the most part, you can simply use lists. And they are really one of the most used data types in Python for dealing with collections of objects. So you can stick to less than you can accomplish. The same thing that you can accomplish with a bolt and sets. With that said, under the hood, they behave very differently and they have very unique use cases. So let me start with lists and then we're going to get into tuples and sets. Let's create our list. There is two ways of creating a list. I'm going to create a variable called mylist. Now, I'm purposely not calling my variable list because list is the actual list type in Python I, and I do not want to override that. I'm going to show you that in a second. So I'm going to call this my list. And I'm going to create the list. So to create an alias, you use square brackets and then you separate your items by a comma. So they suddenly supplied numbers. Ireland meter, sprint that MyList. There you go. Now, the other way to create a list is by casting something into a list. The same way you can do it with strings or with an integer, e.g. there is a list type and you can put something inside of a list. So as you might already know, under the hood, strings are behave kind of like lists. So we can actually cast a string into a list. We're going to store it in a variable called a. I'm just going to bring that. What's going to happen is that the string will be broken into its parts, into its characteristic, and the result will be a list. So these are two ways of creating a list. That is why I'm avoiding calling my variable list. Because if I were to do that, I would simply be overriding this and then it would essentially be broken. There's ways to fix that is by the way, let me show you that if I were to do this, I'm going to get an error because now I basically overrode the functionality of this list keyword. If that ever happens to you, you can say delete list. They'll list. And now they should be working again because we can reset that functionality. Great. So what can we do with lists? The first thing we can do with lists is a loop over them or iterate over their members. So we can say for I in list, print I, and there you go. We can access each one of their members simply by, by using a for-loop. This will be something very common. You will be doing, by the way, looping over lists. But you can also check if something is inside of our list. We can say, let's create a variable called result. And let's say, let's check if the number two is in the list. To enlist. We can print the result. There you go. So this check will give us a Boolean, either true or false. We can use that in an if statement as well. We can say if two in my list. Let's simply print the list. Now, if we check for something else, let's say number ten, that is not in the list. It will print nothing. So let's say, let's add an else statement and say print, not in list. Great. Now as I mentioned, your list can have any, any type of objects. You can have numbers, you can have a string, you can have floats. E.g. you can even have a list inside of that list. So let me create another list with simply two numbers. Let's actually loop over this list. Or in my list. Hi. There you go. So as you can see, we have this string and we even have the list inside of that other list. Checking if something is inside of your list will be very useful. But you can also get items from inside of your list. So let me create a simpler list because this one got a little bit messy. Let me add three strings, 1, s, and then just the number three, that makes it up. So for us to get an item from a list, we do it by using the index, and that is using the square brackets. Let me explain indices in a more visual way. So imagine you have your list here. Let's say we have a list with four items in it. Every item has an index, and it's all basically in order from left to right, and we start counting at zero. Now this might be confusing sometimes, but the first item in the list is the index number zero is the zero item. The second one will be one. The third one will be to afford one will be three. Now there's another way to get index indices by using negative numbers. So let me undo this. Let me put this indexes the bottom here. We can do it in the reverse order. We can say the last one in the list will be index minus one. Then the second last one will be minus two. The third last one minus three. Minus four. We don't start at zero because we cannot say minus zero, of course. So if you ever want to get the first index or the first item in the list, you simply say zero. If you want to get the last item of the list, you can either know what's the last index, which would be the number of items in the list in this case is four items minus one because we start counting at zero. And that will be your last index, which will be the last item. Or you can also simply type minus one without knowing how many items are in the list. So let's go back to our code to get the items using the indexes. We use the square bracket notation. And inside of that square bracket, we put the index and we want, so if we want our element, let me store that in a variable. Actually let me just print it directly. I'm going to print the first element in the list, which will be the string 10. If I want the second element, it will be the first index number one by one. The third one will be index number two. Now, if I go over the indices, let's say I want the fourth one, but there's no fourth one in the list, then I'm going to get an error saying that the index is iron-rich. Now by the same logic that I just showed you, you can get the last one, which will be minus one, that is three. We go on to the second last one will be minus two, and so on and so forth. There is a, there are two ways to know the limits of your, to get to the limit or to the end of your list. This is one, is of course, getting, getting just the last item using the minus one index. But you can also check how long your list is. I can say I can use the function called Len L-E-N. Pass my list into it. Let me print that result length. This will give me three, that is because there are three items in the list. If I add one more, now there will be four. So you can use this to get the last item as well or simply to know how many items are expecting. Because sometimes you have a list with hundreds or thousands of items and you cannot always keep track of that. So this is one way to always query that, that value. So what we can do now is say my list. To get the last item, I can get the length minus one because we're starting to count at zero. So this will give us four. There you go. You can also get not only one item from the list, Bora, a chunk of items from the list you can, it's called slicing. You can slice your list and get a portion of that list as a copy to let me show you how that works. For these, we also use the square bracket notation and we also use indices starting to count at zero. But now because we're using a range, we use the colon in a colon inside of square brackets. So let's request, Let's get a copy or let's get a list of lists are the first three items in our list. That means we want from index zero to index number two. So let's request from index zero as the first index and go onto index number two. Now let's store this in a variable called range. Now I'm purposely not calling this variable wrench because there is another function called range and it's built into Python. Let me show you really quick, but that does. This is called wrench. And I'm going to request a range of five numbers. So it will give me a list of five numbers from zero until four because we start counting at zero. So if I were to call this variable range, then I would be overriding it. And it's usually no, it's not a problem If I don't really need the range function in my code is not a problem, but it's usually recommended not to override. Built-in functions are built-in types in Python. So let's go back to our slicing. Let me print by orange. So once again, we're requesting from index zero up until index two. So we should be getting these three items that logically that makes sense, right? But we did it. We got the first two. The reason for that is in this slicing, the first index that you provide will be included. But the second index that you provide will not be inclusive, but it will be exclusive, which means that you will get everything up until the item before that index. So if we want to get the third item which is index two, we have to add one to this. And now we get what we want. This can be a little bit confusing sometimes. And there's of course, multiple reasons as to why this works this way. But with practice, this will become second nature to you. There is one more element we can add to the slicing, and that is the step. If we add another column here in the square brackets, by default is step will be one and the step is just how to return the copy of the list that you're requesting. E.g. let me do this. So by default, this step is one, which means we will get every single item in the range where requested. If I were to do step two, we will basically get every second item. So that is also how you can handle that slicing. Something I also wanted to show you is that you don't necessarily have to provide indices by default. If you don't know, if you do not provide an index number. The first place here, this will be the very first index. And by default this will be the end of the list. So if I were to simply do this, I would get a copy of the list with every single element in it. There you go. Now, if I were to say I want everything except the first one, I could start at index one and leave this one in the second one empty and simply get the light at the end of the list. And the reverse is the same. We can simply type three and we will get up and up until the second index of the list. Okay, Let's talk about one of the key properties of lists. Something that makes a list very unique is that they are immutable. Now what mutable means is that they can be modified in place. And there's something you have to be careful about this property and keep it in mind. So let me create an empty list, those square brackets, that is an empty list. And let me add something to that list. To add something to the list, you can do dot append. Let's add simpler string. I can, let's say one. And let's print this result. It's printed. So let's print a list. Before we add a number, are we before we add an element, and then after we add the element. Than me clear this, we run the code again. So as you can see, we get an empty list first. And then we have this lists with a single element. That makes sense right? Now. What happens if I add another variable and assign a to that variable? Now, let's print a and B. A and B. Let's add something to a, and let's print a and B again. So let me, let's print, Let's give it a little tag. So we know which one is which, say B, B. And let's do the same up here. Once again, I'm creating an empty list. I am assigning that interlaced a variable called a. And then I create a variable called B, and I assign a to B. So far so good, right, so both a and B should be essentially empty lists. Then I print those centralists. Let me clear my output window. Then I add something to a stop for a second and think about what you think is going to happen when we print a and B again. Now a has, has one element in it. But what's happening to be? Interesting. So a and B at first are empty, and then both a and B have an element. Now, let's make this even more clear. Now I'm going to append something to be. Just going to point to that, sorry, a string that says two. Let's print that again. So both a and B are the same. That is weird because you would think that a and B would be independent. Now this is something that you have to consider very carefully when you are using lists because these can catch you off guard. It's called a pointer. There's always a pointer in memory to the list that you created. So a becomes a pond pointer. And whatever you do to a, even if you assign it to b, b is basically is not a new list. B is pointing to a. So whatever happens to a will be reflected in B. Because B is simply going back to the original pointer a. That is very useful because it allows you to keep track of a lot of data, modify, modify your data anywhere you want, and still have the same list of elements in memory. But it can be confusing if you're expecting two different lists to be working with two different lists. Now, to fix that, if you wanted two different lists, well, you just have to create two empty lists. And then this is what you would expect. Now a and B are behaving separately. So that is, that is mutability. As you saw, we're starting a with an empty list and then we're modifying it in place. So that means that we can add things to it by appending things to it. And it also means we can remove things from the list. Let's go back to a single empty list called a. Let's, let's actually no, let's not make it empty. Let's give it some strings inside of it. So now a has two strings. We can remove things from me by using the Remove function. Let's remove two, and then let's print. Okay. There you go. So now a only has 11 string in it. Now if you try to remove something that is not in the list, e.g. removing this string three, it will give you an error because it says that x, which is what you're trying to remove, is not in the list symbol. Check. Or a simple way to avoid this error is to say if three in a list, then remove it, otherwise, do nothing. So let's say we want to remove one instead. There you go. Now one is gone. I want to show you two more functions that are very useful in a list. So let's go back to our a list and let's type. Let's add a few more strings to it. That's a zero. Add a name, Let's say John sample. You can find the index at which you're an element you're looking for is there's a function called index. And you pass in the element you're trying to find. E.g. if we're looking for the string zero, even if it's called zero, it's not going to be at index zero, right? Its index number two because it's the third element in the list. But if you didn't know this, you could query it. You could say, okay, index zero. Let's draw that result in a variable called result. And print. There you go. Number two, if I ask for John and we get index number three. Now if I ask for something else, something that doesn't exist, we get the same error because something is not in the list. Which case we would also do an if statement to check if something is in a, then do that similar, like the same way we have done it before. Now, the last function I want to show you is similar to append. A list less than this case, a dot append. We'll add a new item to the list at the end of the list. So if I do this and I print a, you see the new item will be at the end. What if I want to add it at the beginning? There is another function called Insert. And then you give it the index that you want to insert at and then the items. So these will insert the new item at the beginning. Now, if you wanted to in the second place, let's say we want it after one. Then we can say, okay, insert at index number one. And now it will be one and then the new item and then the rest of the list. That is how you can insert things, whatever you want in the list. And by the same method, something else that I could show you is that you can remove things from the list by index. So I showed you there, remove the remote function, which would remove an item by using its name. So in this case we'll remove the string called one. But if you want to remove by index, you can use the function called pop. By default, pop will remove the last element of the list. So in this case, the last element of the list is john. Pub will remove John. We do pop twice, removed John, and then we'll remove zero. So we should be left with these two elements. There you go. But now you can also pass in an argument to pop, and that will be the index. Let's say you want to remove the first element, which is this string. That says one thing, the index zero. And now work with that. So if you want to remove two of the first elements, the first two elements in the list. You can do pop twice with index zero because the first time it will remove number one, we're left with this, this list. Now the first item will be to write, so we pop zero again and two, that is now the first item will be gone. So now we're left with zero and John, on. There we go. And of course, by the same logic, you could simply remove, let's say we wanted to remove this string that is two. We pop index number one, and we're left with that result. Alright, before we wrap this up, I wanted to show you something that can be confusing to some people. Having a list inside of a list. Because as I mentioned, anything can be put inside of a list including another list. So let me go ahead and do that. Let me say we have a list with one element and then we have another list with another element. So what's happening here? Our main list. So you can see from these square brackets has two elements in it, and each one of those elements is a list. So what if I want to access the first list? Very simple. I simply say, okay, give me the first item of list a. And that item of course, will be the list that contains this string that says one. Now what if I want to get the first item from that, from that list? You can simply follow the same convention. You can get the first item from that list, and now that will be one. Now, if I were to modify, let me modify this list that its initial list. I've put three items in it, just three numbers, 12.3. What do you think there's just going to print? It? Prints one. Now what if I wanted to get number two? Then I get, I'm getting the, I have to get the first list on the mainList, bypassing index zero. And then once I get that list, I want to get the first item of that list, which, which will be this number two. And there you go. Now what if I wanted to get to this string over here? Well, I need to get the second item in the main list, which would be the list that I'm trying to get at. So that will be index number one. And from that list I want to get the first item, the one and only item, which would be high index number zero. Now, this works with as many levels as you want, and this is where it can get a little bit confusing. Now, imagine that this inner list has another list inside of it with another element, just a single number. Now to get to that, we need to get the first list and then the sort of the first item in our main list, and then the first item in that order list. This is just recursion, been going level after level. So now we do get the first item from the main list, which will be this list here. Then get the first item from that list, which we will be there, the next level of the list. And then we get the first item from that, which is the string that we want, which is this one over here. There you go. I wanted to show you this because it makes sense logically. But seeing this type of syntax can be a little bit confusing. And it will be confusing until you have more practice with it. So don't be afraid of it. Don't, don't be discouraged to try it. Just because it's a little bit cryptic. It makes perfect sense once you have done it a few times. So that is list. Now let me show you real quick what tuples and sets are going to do. Similar to lists. They store elements inside of them. So you can do very similar things but a tuple. The difference now is that a tuple will be using parentheses instead of square brackets. So let me print, let me get the first element of this tuple. Same convention as with the list square brackets will get the first element. The main difference is that tuples cannot be modified and they cannot, we don't have the same concept as, as pointers with top. So if I were to assign a variable b, the value of a, which is our tuple. They would basically be two different copies of the same tuple. Now as I said before, lists can be modified in place, but tuples cannot. So if you try to append something to a tuple, it will basically give you an error if fixed my typo, because tuples have no attribute, append. So use a tuple whenever you know you don't have to modify your list. Otherwise, simplistic duality analyst. Now, the other element I want to show you is something called a set. Now, you can initialize a set this way. Or with curly brackets. This can be confusing because curly brackets are also useful dictionaries. So I recommend you typing in set. Now this is an empty set. Let me print that out. That is an empty set. Now would says you can modify them, you can add something too. I said there is no method called append. If I try this, it's going to give you an or. There is another method called add. You can add something to a set. There you go. So I added one. Now the difference with sets, what makes him unique is that every value inside of them has to be unique. If I were to have a list, let's make an empty list. Let me append the same value twice in a list. Let's print that out. So my list is called B and should have two strings, let's say one in them. There you go. That is valid in lists. If I tried to do the same with sets, you'll see that I only get 11 item in it. And this is very useful to keep a unique set of elements. Now you can replicate this width with lists, e.g. let's say that you are adding something to a list. Let's say you have your list and you have some value that you're trying to add. Let's say the value is the string with these numbers inside of it. You can mimic what I said is doing by saying if value not in the list, which is list a, then you can add it. You will only add that value if it's not in the list. So that's kind of a similar behavior as what sets do. But something else that you can do with a set is you can give it a list. And it will narrow down the list until there is only unique values in it. So let me show you that. Let's say I give it a list. And I will say, I'll give it a list with some duplicate values, one to two. There you go. So once I printed, let me get rid of these petitions. Once I print that said, you will narrow down my search tree will narrow down my list to only having unique values. So once again, this can be very useful when you, when you don't want to have to repeat values inside of your collection of elements. And you can also, actually, no, sorry, you can't. Something else that is unique about sets is that you cannot get elements from a set the same way you do with lists and tuples. You can iterate through a set that is valid. You can check every single element in a set by using a for-loop, but you cannot get elements in a set by using indices. The reason for this is that similar to dictionaries, sets are not ordered. There's a technical reason behind this is actually called HashSets. And they use pointers. But there are basically very efficient. You can find elements in a set much faster than in a list. But that also means that you don't keep, you don't keep track of the indices. So you cannot do this, this type of operation. So if you need to access something by an index, then use a list. If you don't. But you want to keep a really large collection of elements and you want them all to be unique, then you can use a set for that purpose. There you have it, lists, tuples, and sets. These are very powerful data types that will help you a lot when you're coding. And hopefully I was able to help you get more familiar with them. 33. Deep Dive - Dictionaries: Let's talk about Python dictionaries. Now you can think of a dictionary, some kind of like as a mapping between two things, between a key and a value. So similar to an actual dictionary where you have award and you have a definition for that word. The key would be the ward, the definition would be the value of that key. Or like a phone book, e.g. let's say we have a name. We have Rachel and we have Rachel's phone number. We have rows, we have roses. Phone number, e.g. Rachel would be the key, and Ross would be the rows would be another key and the phone numbers or the values for each one of those keys. So let's translate this into an actual Python dictionary. Syntax for creating dictionaries is using curly brackets. So let me translate this. Rachel Rose is phone numbers into a dictionary. So you have to use the curly brackets or curly braces. And then inside of that you will have each item in a dictionary, which will be a pair of key and a value. And that is separated by a colon. You have a key colon value. This will be the first item in the dictionary. And then you separate each item, item by a comma. Go. And if we wanted to add one more phone number where we don't have to do it in your lines. By the way, these can all be in a single line. So let's do it that way. Let's add a new phone number and a sad comment, Monica, and then one more phone number. So these would be a dictionary. And let's, let's store it in a variable called phones. I, let me just print it, see what that looks like. It looks exactly the same as what we just typed. So what's special about dictionaries? The first thing is that as we said, we can have a mapping of them, offer of keys and values. And you can access those keys and values. So you can access those values simply by accessing the key. So let's say that we want Rachel's phone number. Let's just say Rich and we want her phone number. You can access the value of a phone number by using the key. So to do that, we use the square bracket notation, similar to how we access indices in the list. But instead of a number, instead of an index, we provide a key. So let's provide Rachel e.g. let me call this Rachel number e.g. let's just print that number. And there you go. That's how we answer the phone number. We can do the same thing with Ross. And this is case-sensitive. By the way. This is Ross fake phone number. If I don't follow the casing, if this is lowercase e.g. I'm going to get an error saying that there's no key, key named Ross in that dictionary. Once again, because of the case sensitivity, that's how you read values from a key. How do we write a new key? Let's say we want to add a new phone number to this. Let's say we want to, let's print the dictionary right now. First, let me clear my output window. Let's add a new phone number. So we'll have phones will use the same syntax, the square bracket syntax that we use to get values. But now you can write your new key. So we'll add Joey. Now, if we do this right now, we will try to get a value from the join key. But if you do equals and then a new phone number in this case, then we're going to create a new entry in the dictionary. Are going to be create a new key and a new value. So let's add joy. Then let's print the dictionary once again. This is before we added Joey, and then this is after we added Joe. Now, Joe is here, joe is now at the end. You're noticing that the dictionary is not necessarily in the order that we're creating it. E.g. we added Rachel at the, at the beginning, but showing up in second place. Why is that? The reason is because dictionaries are created in a way that makes them very efficient. They don't need to keep track of the order because there is a mapping and under under the hood, this is called a pointer. The keys. Are pointing directly to the value that they have. And there's a, there's a bit of a technical explanation that we don't need to go into right now. But what it means is that you don't need the order of the keys, but you don't need to keep the keys in order because you can simply access the value by the key, no matter where in the dictionary that key might be. So with that said, there's three main properties that dictionaries hold and that makes them very unique in Python. One of them is, as I just said, they are unordered, meaning the keys are not in order. The other one is that the keys say not in order. The other one is that the keys are unique. Let's see an example of this. Let's add Joey again. Actually, let's, let's try to add a new, a new Rachel. I'm just going to type 0000 to make it obvious. Now let's run this piece of code. As you can see me, we don't have to. Rachel's, there's only one Rachel. What we did is override that. And in this case, this became an integer. So it's only a single zero because multiple zeros don't make sense. And so that's an actual number. But if I turn this into a string, then we will see all the zeros we added. So as you can see, what we did was overwrite Rachel, we didn't create a new Rachel key. So that is the second important property of dictionary. The keys are unique and the third important property is the dictionaries are mutable, meaning that we can, we can modify them as we go. As we have seen right now, we created a dictionary and then we're modifying the original dictionary. That is what makes them mutable that you can change, you can add on, you can remove keys and values in a dictionary. Now, something else to pay attention to when something is mutable. If you have seen how lists are mutable and you're always keeping a pointer to that one leads that you're modifying. Let's, let me show you another example of that. I'm going to create an empty dictionary called a, will store it in a variable a. And I'm going to create a new variable B. I'm going to say B equals a. So a is an empty dictionary. And an empty dictionary simply these two squiggly brackets. Or you can also type, dict, type ticked, and open and close parenthesis. This will be giving you the same result. So a is an empty dictionary, b equals a. Now let's add a value, a key, and a value to a. Let's say a equals one. And let's print a, and let's print B. So what do you think? Take a moment to try to think about what's going to happen. But adding a key and a value to a or OneNote, technically we're not really doing anything to be right. So let's, let's run this code and print the result. Now as you can see, both a and B are exactly the same. That is another important aspect of the mutability of dictionaries is that they are created once and you're always keeping track of the same dictionary. So even if we assign this to multiple variables, even if I assign, create a variable called c and assign V2 that. And then I print c. They are all still the same dictionary, even though you have three variables, they have the same dictionary. E.g. if I now take C and I add a new key to that, and I say C. So three, since the third letter in the alphabet, let me print this again. As you can see, all a, B, and C got the same update because they are all tracking the same dictionary. If you want to avoid this, if you want them to be independent dictionaries, well then you simply initialize or create dictionary for each one of those variables. There you go. You can see now a has a unique key, B is empty, and then see, because we added a key to C has its own values. So those are the main three properties that you should remember about dictionaries. Once again, the keys are not in order. That is because you get values from the names of the keys are cleaned, not from the order of the dictionary. The keys are unique, which is how you're able to get a value from a key. If you have multiple keys and you couldn't really know which value to get from the keys, right? So we have to keep them unique. And mutable means that you can change the dictionary on the fly. For that, for every unique dictionary, there's only one copy that's always being modified, even if you assign it to other variables. Okay, so now let me show you some common operations about dictionaries. Let me create a new dictionary with some. Keys and values in it. Let's have a, one, b, two, and c. The value of c will be three. Excellent. So you can check if something is in a dictionary. The same way we can check if some of the news analyst, we can ask if e.g. a. Dictionary a, actually, let me change the name of this dictionary. Let's call it, let's call it simply diction, dictionary. Dictionary. If a is in dictionary, print. Yes. Otherwise, else print. No. Go. So a is in dictionaries and we get yes. Let's say f. If not, it's not a dictionary, so we will say No. Excellent. So what happens if you try to get the value of a key that doesn't exist. We already saw that, let's say f that we know is not in the dictionary. Let's try to print that value directly. We're going to get an error. And that error is a key error because F is not in the dictionary. There's two ways to check if there's a key in the dictionary and if it is, get the value from it. One way is if we, as we already did here, we can say if f is in the dictionary, then print the value of f. So let me get rid of the else statement. So we will only try to access the value if f is in the dictionary. Now we can do that with a that we know is that already in the dictionary. And then we get the value. Now there's another way to get a value. And bullet proof is a little bit. Dictionaries have a function called get. Now get is the same, does essentially the same thing as the square bracket notation. I can say, let's store this in a variable called value. I can get the value of the key. And I can print it, which is one, as we already know. But if the key doesn't exist, get will give you a default value. So in this case, since f doesn't exist, we'll get a default value of None. But you can also provide a default value for that. So this is kind of like a switch and say, if the key exists, give me the value of the key, otherwise give me another, another value. This case I could say the default values is going to be a string with a single dash, e.g. and there you go. So since f is not in the dictionary, we're going to get the default value, which is the dash. Now we're getting this one printed here because of the if statement up here. And there you go, we have the dash. So just to double clarify this. This is similar to saying, let's say we have a key and we want the kids to be f. Can say, if key can dictionary print the value of that key from our dictionary. Now, key will be this variable that we created. Otherwise, print a default value, which in this case, let's actually create that as a variable, the default value will be a dash. So if the keys in the dictionary print the value from that key, otherwise, print the default value. Let's run this. We'll get a default value. This dot get function does exactly the same thing as these few lines of logic. Grid. So that is how we can access values from keys and how we can write new keys into a dictionary. Now how do we delete keys from a dictionary? Let's simplify this dictionary here. Let's just leave the a key. And let me, just, for the sake of example, I'm going to add a new key which will be B. Now again, same, same, same syntax. But now we do equals to. So now key B will be equals to. Now let me remove a from the dictionary. To remove a key, there's a function called pop. Pop will remove a, will take in the name of a key, and we'll remove it from the dictionary. So let's print this dictionary. Now remember we initialize the dictionary with a single key, that's a. We add b and then we pop, it, will remove a from that. So now our new dictionary is being. Now there's a little bit of a side effect to pump. That is that you get the value of the key that you remove, that you removed. So if I store the result of pop into this variable, I'm going to get the value of a, which is what we removed from the dictionary. That is value one. Now, there is something to be aware of is that if you provide pop with a key that doesn't exist, e.g. f. Again, pop is going to give you a key error because the key doesn't exist. So when you're using Pub is good to check if your key is in the dictionary, then you can remove it. But pop has another functionality that allows you to not have to check if a key is in the dictionary. And that is by providing a separate argument. Second argument, sorry. Once again, if we run this by itself, we get an error because F is not in the dictionary. But if we provide a second argument, the same way that GET has a default value, pub will get a default value of four. So if I type non e.g. I. Print removed, I print the, the remote variable. It's not going to complain and it's actually going to give me that default value. We're trying to provide it. So there's no key named f. So it defaults to that default value. If I change this to another dash, e.g. same as before. Well, there will be, since f doesn't exist, that will be the default value. But if we're trying to pop a, we're going to get the value of a because a actually exists. That didn't work out is because I haven't run the whole code. I'm sorry, I need to run the entire code. There you go. So now removed the per sprint is the value of a. Second print, the result in dictionary after popping a. But once again, now we can run the whole code. If I remove F, the resulting dictionary still has both a and then the Beta we added. And then we printed the default value from the key that we couldn't remove. The last thing I want to show you about dictionaries is how you get a list of keys and values. So let me delete the rest of this code and add a few more keys to my dictionary. The same example again. So let's say that I want to know what keys in the dictionary. Imagine that this dictionary has thousands of items inside of it. Dictionaries have a function called keys. And what this will do is give you a list of the values of those keys. Now, sorry, it will give you a list of the keys in the dictionary. So if we store this in a variable and then we printed, let me clear my output window. There you go. So you get a list of the keys inside of that dictionary. So that you can use that list in order to access values from that. Or if you want a plain list of values without the keys in them, There's a function called values. And I will simply give you a list of the values in the dictionary. Now, remember that dictionaries have no order. So the order of these values, as well as the order of the keys, will not make, will not follow anything that you might expect. In this case, we printed the values, but you can see that they are not in the order that we brought them in, which is fine. This is just a list of values, but we usually want to query values from a key, not directly from here. And there is one more function that I want to show you that is called items, but islands will do is give you a list that has the pairs of keys and values in the form of tuples. So let me save that in a variable called items and print that out. So you have a key, sorry, you have a list. And inside of that list you have these pairs, which are tuples. And you have the key and the value for that key. Key and value, key and value. So this way you could actually loop over all of the keys in the dictionary. Dictionary keys. So this will be four key in dictionary dot keys. And it would print every key in the dictionary. Now, if we do a values and say for value, in values, are sorted value not volume style as mine. What's my mistake? For volume values? Then we loop over those values. Now you can also do the same with items. But now remember that items. If I, if I simply print items, remember that items each, each element in the list, the key and the value. You can unpack that. It's called unpacking. When you have multiple values in a tuple or a list, you can unpack it directly in the for-loop. You can say for key comma value. So basically for every item in the list. Actually let me, let me show you this first. Let's print each item in the list. So as you saw me remove this. As you can see, every item is, it's a pair, a pair of a key and a value. So we can say, we can unpack those for key comma value. In each item of the dictionary. We can actually print that out separately. G dot value. So we have a and we have one. We'll see where three. Or we can put them together in the print statement. Like so. And while we get the same result because we're printing a tuple. But let me do this in the form of a string, d and c value that we can use a dot format function. And there should be a little bit more clear. So keys a and the value of a is one. So once again, that is using the iron's function and unpacking each one of those items as a key and a value. That's dictionaries. I believe I showed you some of the main things you can do with them. But of course there's much more to them than that. They are an amazing tool for handling data. I highly encourage you to play with them a little bit more. But with that said, you should have enough knowledge now that you can use them more efficiently in your day-to-day coding. 34. Final Project - Pose Library: Intro: In this project, we're going to be creating a post library. For this post library, which looks like this. We have already written the functionality that we're going to need that was done from the previous project. You're going to learn how to create a completely new tool that takes advantage of the code written either by you or by somebody else for another project. The main thing we have to build on top of that other code is the ability to save poses into files. So you're going to be learning how to deal with files. The second part of the work going to be going into creating the UI. The UI is a little bit more complex. It has a collapsible section. We will be taking input from the user and there's a list of poses populated from files on disk. Now the benefit of saving process two files is that we can now share them across my sessions or even with other people in different computers. So let me show you how this tool works. I have already saved some posts in advance. We can apply them to a single character as we have already seen before, or to multiple characters. I have sitting posts. And we can also save poses from the scene. So let's say that we, let's say that we want to save a pose for this prop. Let's put the hat on the ground. We're going to name it, had on ground. Save the pose. And when I save this pose, you see that it showed up here right away. And this is saved into a file. So once again, it can be shared across sessions or across computers with other people. Then I can go ahead and I can apply that pose to these characters. So this is a tool we're going to be creating. 35. Creating the Python Package: The first thing we're going to do is create the file structure for our code. So let's go back to PyCharm and we have the other modules here from our other projects. Now I'm going to create a new one, but I'm going to create a directory because this will need more parts than the other projects. So I'm going to right-click in the root of our project, and I'm going to click New. I'm going to create a new Python package. Then we give it a name. I'm going to call it pose underscore library. Now PyCharm created a new folder or a new directory with a file inside of it. I'm going to go back to this file in a little bit. But if you look at the folder itself, it simply, this is a scripts directory for your Maya in your documents Maya folder. And inside of that, there's this directory with a single file called underscore, underscore init, underscore, underscore dot PY. Then I'm going to create another file, just a single Python file. There were we're going to structure this code or this project is going to be keeping the functionality separate from the UI. This is generally a good practice. Something we did before for the post transfer to is create the UI directly in the post transfer module, which contains a functionality. I chose to do that because there was a first tool we use where we created a UI. And also it was a slightly simpler to, but usually it's recommended to keep your UI separate from your functionality. That gives you the ability to reuse the functionality, which is what we're going to do with this project, and keep your UI is completely separate. That also gives you the ability to create multiple UIs that take advantage of the same functionality from a single place. So I'm going to create a Python file. I'm going to call it core. I'm calling, I'm calling it core because this will be the core functional of the library. And I'm going to create another file called UI goes. Our UI code will live in here. Now, let's go back. Let's add something to the core module. Let's simply add a test function so that we can, we can test it out and make sure it's working. I'm going to type hello core four, pose library. Great. Now, what is a file doing? If we look at it, it's actually completely empty. It's simply a text file that is called double underscore init, double underscore dot p-y. If I didn't have this file, Maya would give us an error or Python would give us an error because this is the file that tells Python to treat directory as a module. So let me delete that file. It's going to be reflected here in PyCharm. Let's try to import this function. Let me go back to Maya. Let's open the script editor. We call this post library. So we're going to say from pose library, import core. And we know core has a function called hello that will simply print our hello message. So let me make this a little bit bigger. Let's run the code. You can see Python is saying no module named pose library. Now what happens if we add that init file by kin? Let's, let's create a new Python file. I'm not going to create a Python package. Which by the way, when you say Python package, pycharm will seem simply create a directory with that init file. But in this case I'm going to create a Python file. I'm just going to call it underscore, underscore init, underscore, underscore dot p-y. Let's try to run this one more time. There you go. Now our code is working and we're getting that message from our function. So that's what's important about Inuit files. You don't have to put anything inside of them. You can if you want. But they have to be there for the directory to be treated as a module. If you have submodules, It's going to be the same case. Let's call it, let's call this new module. This is an example. You see there's an init file inside of that. And then you can add more Python files in your new module. So it's once again, is just telling Python to treat that directory as a module so they can, so that it can be imported. Great, let me delete this. And there you go. So we have the structure that we're going to be using to create our two core will have all of the functionality, and UI will have all of the UI elements. 36. Working With Files: As I mentioned before, we're going to be reusing most of the code from the previous project. But one key thing we have to add to it is the ability to save files. So let's learn how to work with files. Before we start saving poses. I have created a directory here. This is just under my C drive. There's a python demo folder and then a post library folder. I am going to be saving my file here. Let's just start with a simple text file so that we can learn how to create files from scratch. I'm going to call, I'm going to say my path to my directory as their path. I'm going to add an r here. I don't believe any of these characters will be escaped. Know. But if you were to have e.g. an N like nose library, your character will be escaped. As you can see, the dash a backslash, n is the return character. It will give you a new line. So it's actually breaking up the path into two lines. So if for some reason your path in your, in your directory has a special character called the escape character. And other one of them would be T e.g. library. That would give you a tab. Or an a will give you an extra character. So one way to avoid that is to escape that escape characters. E.g. going back to nose library, which again back to backslash n is a new line. If I add a new backslash, it will escape that. Or you can do a forward slash. And that will also avoid the issue. However, when your copy pasting parts from Windows, you will usually get that backslash. So replacing everything two forward slashes can be a little bit tedious sometimes. So an easy way to do it is just put an r in front of this string. And this will turn it into what is called a raw string. Arrow string basically means ignoring any escape characters. These purposes. Now the R is also used for something called rejects, which is an expression language that you can use to parse strings. But we were not going to touch that here. Just, it's just good to know that R is actually meant for rejects. But we can use it to avoid issues like, like having that, that new line are those escape characters. Okay, so let me put this back to my actual path. And now I need a filename. Filename, we're going to call it text dot TXT. And this needs to be a string, right? So actually we can, let's join it together. I will show you something. I'm going to use the OS module. So I'm going to type import OS. And I'm going to say full path equals OS dot path. So always stands for operating system. So this is a library that deals with operating system actions. There is a path module in it that helps you with file paths. And there's a join function that allows you to join multiple paths. So if you, if you have multiple directories and you want to join them together, we could simply do this, e.g. let's say test that dx dy. Or we could also say your path loss. E.g. we're missing the backslash and plus filename. But they always got Path Join will give us this backslash automatically and we'll make sure that the path is valid. And if there's any escape characters, he will escape them for us as well so that we don't have to worry about anything funky happening with the path. I'm going to say their path coma, filename. Join those two for me. We're going to print full path. And there you have it. This is a full path to our file, file. So I'm going to actually call this file path. Great. So now this file obviously doesn't exist yet. We're going to create it. To create a file, we use a function called open. Let's call this. Let's call this F. Let's call the file f. I'm not calling it file because the file is another built-in function in Python. And I do not want to overwrite it. So I could call it underscore file, but in this case I'm just going to call it f. Now we want to open by a path. And then when you're opening a file, you have to choose the mode, the mode in which you want to open it, and that is a single string. The main modes you want. I decided to write to the file or read the file. So in this case we want to write a new file. So we're going to say File Open, open the file path in write mode. Then we're going to say F dot, right? So what you get when you open a new file is actually a file object. So let's see what that looks like. If I simply print f is going to tell me this is an open file. Would write mode at some address in memory. If I say, give me the type, there's, this function will give you the type of an object. It will tell me that that type is file, which is why I didn't want to name my file, my variable file because I would be overriding that time. Great. So now we know that f is a file object. An object is really any type in Python, e.g. strings are objects, lists are objects. So just like those ones, these file object has a function called write. The write function will be kin, some text or some string with some text and some texts for my file. Excellent. Now something very important to remember with files is that once you open a file, you have to close it. To close a file. You can do f in this case, dot close, then call that function. If you don't, your file might stay open and you'd might get corrupted, and it would break. So it says usually, it's always a very good idea, good practice to close your files. So let me run this code. And let's go ahead and see what happened. Excellent, So we have a new, a new text file with the texts that we wrote inside of it. Great. The next step is to read files. So let me modify this file with some new texts so they're going to read from Python. Let's, let's write new text. Read from Python. Let's save that file. Now to read, it's very similar to writing. All of this stays the same. We still want to open the file, but we want to open it in reading mode. That would be our same open file path or as the mode. And now instead of writing, we want to read this file. So I'm going to delete all of this and use the dot right function. Well sorry, read function. And I'm going to store the red, whatever we read from the file into a variable called contents. And read will give you a string with the contents of the file. And then once again, after reading, we want to close that file. Then I'm going to print continents. And there you go. So contents is exactly that string that we got from the file. That's the basics of working with files. It's very easy, it's very simple. The main thing to remember is to close your file. Now if you don't want to worry about closing the file, because it's very easy to forget. There is another way of doing this, and I actually recommend you doing it this way. I'm going to use something called a context manager. Now, let's type width, opened file path. This is the same thing as we talked above. We're going to open the file in radian mode. You type as f and then colon. And inside of this width statement, I'm going to say contents equals f dot read, just like before. So what's happening here is we're still opening the file in radian mode, where it's still saving that in the variable f. But we are doing it inside of this context manager. A context manager ensures that something happens before your code is executed. So before we read the file, something will happen. And it also ensures that after your code is executed, something will happen before moving on to the next lines of code. So in this case, we don't need to close the file anymore because the context manager will be closing it for us. And then we also don't need this slide. Now, let's see if we get the same results that we got. We got exactly the same result. So we can do the same when writing to a file. We can switch this to the right mode. And of course we don't have to use f as a variable. We can use FYI if we want or anything else you want, as long as this is, this is up to you. As long as of course you reuse that variable in your code. So open the file and call it something, will do something we're writing now. So let's write new text. Let's run this code. And if we check the file, our new text was written to the file. So you can do it either way. You can open the file manually and close it manually if you want. But I highly recommend simply using this, this pattern, which is the Context Manager. 37. Saving Poses to JSON Files: We know how to create files. So now we're ready to start saving poses. We're going to be using a special format for files called Jason. Jason, the JSON format is very similar to how dictionaries look. There's a library in Python that is designed to work in specifically with those type of files. So let's, let's get up pose that we can save. We're going to use our posts transfer code. And we're going to keep all of these series. Let me get a pause here and say pose transfer. And our function was called GET post deck. And we need to pass in namespace. Great, So let's, let's get the post for this hat down here. And the namespace of this character is Luna, one. That's a new character that I just added. So let's call this pose. Pose equals post transfer that get posted. Let's see what this looks like. Let me just bring that pose. Okay, there you go. It's this dictionary with some values. This small dictionary. That's great, just for the hat. So now we want to save this file. Now since we're using the JSON format, I'm going to go ahead and give that, keep that as the extension name for this file. And we're going to do, we're going to be using the Context Manager as well. I'm going to call this F, again, is for simplicity. But now instead of using epitaphs, right, we're going to be using something. We're going to be using the JSON library that we imported. So once again, import JSON and that's Jason without a JSON. And we're going to use that library to write the file. Now the JSON library calls it dumped. They're going to dump basically a dictionary into a file. Now we need to, we need to pass in the post. Let me be more explicit and call it post dict. We're going to dump the post dict into that while we still need to open the file and we need to close it. Now what we're going to pass that file object into json dot dump. Let's run this and see what it looks like. We go to our folder here. We have this text file, again with our dictionary. And that's exactly what we want. So now we have our posts saved in a file. Now, something else I would like to do is format the file a little bit nicer now. But the good thing about using the JSON library is that you can do just that. There is an argument called indent. I'm going to say I want or participating supplementation. So if I run this code again, it's going to write that dictionary again into the file. This time, nicely formatted. There is four spaces of indentation here, and it's easier to read that way. So let's go ahead and create the code that is going to be saving. Or perhaps the way we're going to be working with this is the name of the file, will be the name of the posts. And of course, the context of the file will be the actual values of the post. So let's go into PyCharm and let's go to our core module. Let's start from scratch. We're going to need, we're going to need the OS module. We're going to need the JSON library or they're always library and JSON library. Let's actually bring all of this into pie chart. Great. So we do need to know, since this is going to be the code, the code for our tool, we are going to want to know where we're saving the poses. So I'm going to make this a global variable. You call it root there. Now, what I mean by global variable is that this variable will be accessible to everything inside of this module. Even if we create a function. Let's say we have this function here. We can use a global variable inside of this function because it's outside of the scope of this function. Now if I were to create another variable here, this variable is only available inside of this function because, again, because of the scope. So that is a great thing about global variables. And because there are global, the convention is to make them all capitalized. Now this is not necessary. This is not going to break your code if you have lowercase here. But as a convention to make it obvious that it's a global variable, It's an important variable defining a value that you want to be constant. We make it. Uppercase. Great. So we also want to know the extension. So we're going to write extension. Let's just do EXT. Extension will be dot json. Excellent. So we need to create a function to write bytes. Let's type def, right, pose to file. Now we need to know what the post is going to be named. We're going to take an argument called postnatally. We need to know the contents of the file, right? So we need those dictionary. Excellent. So inside of this, we're basically going to be doing what we did in Maya with some modifications. So the file path will be the root directory, which is kind of a global directory for our purposes that's defined here. So whoever uses this function doesn't need to know where to save the posts. We're defining that in the tool. But we do need to know the name. Now, the name of the pose doesn't contain the, the extension because we don't want whoever is using this way. We only want them to give us a name of the posts. We will take care of that extension. So let's call, let's make a new variable called filename. And we're simply going to put that name and the extension together. Let me get rid of the period here and extension. We will take care of that inside of this function. So this will be post name, dot extension, pose name in the first place and then dot EXE extension. Great. And now we can join the root directory and the file name, and that will be our file path. Excellent. Then we are being provided with the post dictionary. We do not need to get it. Then we simply save the file. And that's it. It's great. Let's test it out real quick. So let's go back into Maya. And let's make a new Python tab. We're going to import from photos library. Import core. I'm actually going to namespace it core as library core, just to make it a little bit more explicit, library core dot write posts to File. And we do need to get oppose dictionary. So let me, let me, let me import, import Posts underscore transfer. And let's get both dictionary the same way we did before. Make couple of paste this in here. So we need to name the pose is going to be, let's just call it first pose. And we need to provide the dictionary dict. Great. So let me just make sure that my selection is still there. Select the hat. We're going to get the pose dictionary. Can then write that to a file. Object has no attribute, right? Post file. Of course, because we haven't, we haven't reloaded this. Let's reload. We because we added code, we need to reload that module. There you go. Now, if we check our file, we have a new I made a typo, I'm sorry, first, suppose should be first, but what we have the post contents inside of that. Great. So now we can write post two files. 38. Reading Poses From JSON Files: Now we need to read the poses from a file or a post from a file. So let's create a new function called red hose from file. And all we need here, by the way, I'm keeping two spaces between my functions just to make it a little bit more neat or organize. All we need for this function is a file path. Now, this is done in a similar way as writing files, but we need to do the opposite. Of course, we need to read it. So we're going to use the Context Manager. We're going to open the file in radian mode. And we're going to be using the JSON library once again, but to read the file. Now, dump will dump a dictionary into a file and load will load a dictionary from a file. Now, all we need to provide to the load function is that file that we want to load from. And we're going to save that into a variable we can either call it pulls data because that's what it is. Or post dict. We're going to get a dictionary from this. The Context Manager will take care of closing the file for us, of course, and then we can return pose. Now, something cool about contexts managers is even if you return inside of it, which means we are ending the function, it will still close the file before leaving the function. So that is the great things about, great thing about context managers, no matter what you do, they will make sure that whatever needs to be done when you end the execution of the code inside of them, it will happen. Great. So we are, we can now read a file. Let's quickly test that out. So we want to read. Let's do it here. Pose from file, will equals library core. Read prose, posts from file. And the file path of course, is these files here we want to read first post e.g. so let me copy this. Let's provide this file path. I'm just going to write it. For a spouse including my typo. We're supposed to dot json. And then we can print pose from bile. And of course, since we made some changes, we have to reload our module. And then I can use my function. Now, it says invalid filename. Okay, It's my escape character. Okay, Let me just add the r in front of that string to avoid escaping characters. And there you go. Now post from file is the director is a dictionary coming directly from the contents of that file. Excellent, so now we can read and write files. Before I move on, I don't want to forget to do my doc strings. Sorry about that. So let's do ride owes data to file. Then post name will be a string of the resulting file. Example. Once again, what you put into the doc strings up to you. As long as it's this descriptive and useful enough. Pause the act will be dictionary and we'll say contents of the pose. And we are not returning anything in this case. Then let's do the same for reading function. Bridge, pose data. File. File path will be a string. I think this one is very self-explanatory. Let's, let's just add file to read. And then the return value will be a dictionary. A dictionary. The next thing we need to do is to know what poses we have in the, in the directory so that we can populate the library with them. That that means we need to get all of the files inside of this root directory that we chose. So as I said, the name of the file will be the name of the pose and the contents of the file will be the values for the post. So let's go ahead and get the files inside of this directory. We can use the OS library once again. Let me go back here. Let me delete all of this code that we don't need it anymore. Let's simply leave the OS library. The directory we're working with. Let's put it back in here. There's a function inside of the OS library called list there. What it takes is a directory path, and it will give you a list of all of the contents inside of that directory. So we can say, since it's a list, we can loop over that list. We can say for, for item in always thoughtless there. Let's simply print that item. There you go. So what it is is the three the three files that we have created so far are two poses and TXT file. Now we don't want it to C file, we only want the JSON files. And also we want to know the file paths of this, of this file, right? So right now we have the file, the name of the file, but we want to get the full file path. So what I'm going to do is create a function that will have a dictionary that has the name of the pose, Let's say post name. And it will also have the path to the file that has the contents for that post. Let's go back to PyCharm and start writing this function. Let's put it here at the top. I'm going to, since we're going to get a dictionary from this pose, I'm going to call it get poses dictionary. Now, once again, I'm actually going to add the docstring because I know what I'm expecting. Good dictionary with a very level. Moses. And I'm going to run example. The dictionary will look something like this. We will have the post name as a key and the path to the pose as a value, five. But now this also means, this also means that the files have to be unique since we're relying on the name of the file to be the name of the pose. We are also limiting the tool to not be able to duplicate poses. But I think that's okay. Sorry, not duplicate pulses. You can duplicate the pose, you cannot duplicate the name of the pose. We're going to return a dictionary. Okay, so once again, we want, we want to list all of the contents in our root directory. So we're going to say for once again, item in our list there, we're going to use our root directory. Actually, I'm going to call this file name. Since that's what we're working with. We're working with volumes. Now. Let's initialize an empty dictionary. We're going to call it poses to be an empty dictionary. And we need to get first the name of the posts. Host name will, like I said, will be the name of the file without the extension. And also as I said, we only want to get the poses that end with dot json because that is the format we chose. So in this case, if we have this TXT file, we want to ignore it. So let's, let's put that check in first. Let's say if file underscore name ends with extension, which is x, actually let's do the reverse. If not filename and sweat extension that we chose, which is Jason. We're simply going to continue and skip that file. Excellent. So now we get to post name from that file. That is once again the name of the file without the extension. So we can do that by splitting filename dot split. We're going to split by the period by the dot. That will give us the name of the file, laced with the name of the file and the extension. And we only want the name of the file, which is the first item in that list. So we have the post name, and now we want the full path to the file. We want file path will be we're going to join the directory and the name of the file. So it's going to be always dot path the joint as we have done before. We're going to join the root directory. And the file, the file that we got from inspecting the contents of that root directory. There you go. So that will be the file path. So let's say, I hope are not going too fast. Let's, let's print what we have so far. Let's say hostname will be, let's see what we get for post name. Let me duplicate this line. We're going to print pose. File path will be the file path. And in here, Let's make sure we're skipping properly. So we're going to say it's skipping. And let's say the filename. What we're skipping. What I'm doing here is I'm doing inside of the print statement. I'm putting a comma and you can print multiple things together that way. So let's go back into Maya. Let's use our function which is called good posts that have it loaded here. Once again, since we made some changes, let's reload our module. And let's simply run library on the circle underscore core dot get posted. I don't need post-transplant right now. Great. There you go. So we're getting, we're getting for suppose that JSON or getting the post name from that by splitting the name of the file and only taking the first part. And we are also skipping test.txt because it doesn't end with Jason, right? So these are the values we want for the dictionary. We want the name of the pose which were getting. And we want the path to the file that has the contents. So all that is left to do than me delete these two lines and also this print statement. All that is left to do is populate our dictionary. So our empty dictionary here poses. Now the keys for the dictionary will be the names of the post name and the value for each key will be file underscore path. That's all we need to do. And then we need to return that dictionary. So let's test this out. Let's run this code, reload the module, and run the function. Nothing happened because we're not printing, of course. So we can show this in a variable called poses. And print that variable. Let me, let me empty my output window. There you go. Now we have a dictionary with multiple values, which are only the policies that we care about. So test pose, a path to the file. First pose and the path to the file. Now you can see that we're getting double-back slashes. That is because we're using the, the 0 is a path to join function. So it's going to make sure that you get a file path that it's valid and it's not going to cause any issues. So having those double-back slashes is not a problem at all. We can read files from them. There you go. So now we can know, we can use this to populate our library. 39. Creating the UI: Now that we have the functionality that we need, we can go ahead and create the UI. Now this UI is different from the previous one. Not only that, it's a little bit more complex, but not really that much. But mainly in that we're getting input from the user now, both through the selection of this list and from a text input. When Serbian, they're getting the name of the post to save. Let's quickly break down how this UI is composed. I'm going to take a screenshot so that I can draw on top of it. Remember that we talked about layouts and how layouts basically compose what UIs really are. So in this case, we have the list of poses and we have this button here. That means that we need a layout that flows from top to bottom. That would be a column layout. This first section will be a column layout. Then we have this collapsible section. And that happens to be another layout that has a layout inside of it. Now the reason we need that stuff in your layout is because this next section is also flowing from top to bottom. As you can see we have is this line here. And then we have the pose, the safe pose button. So that will be another column layout. But inside of that column layout, we have this text next to it. We have this input field that's going to need a row layout because that is flowing from left to right. So it's not really that complicated. It's just a few layouts Creating the flow of this tool. Basically we have one column layout to column layouts, one row layout, and then an extra collapsible layout. So that is really just for layouts to have this tool completed. Let's go buy term and start working on our code. So once again, the way we organize our code is we have this core module that has the main function. And we have a completely separate module that is going to be for the UI. So anything that is needed just for the UI will be here. Anything that has to do with functionality and really anything that can be used independently of the UI should stay outside of these UI module. I'm probably going into the core one. So something that we need to do that is also different for this type of UI, is that since we're querying information or we're getting input from the user through the UI. We need to name our UI elements very specifically are not specifically, but uniquely. We need to give our window of a unique name so that we can query the things from that window. And we need to give all their UI elements specific name so we can get information from them. Let's start by creating the window that we need. The Mayans him this library, that's your shoulder. So from Maya input, CMBS library, we're seeing the S. And I'm going to create a function called show you why. Now I'm using this name to keep consistent with the other function in our other two. Inside of it, we have to create a window, same as last time. Let's give it a title. Title equals pose library. And then as you remember, we have to show that window. So Cmd S dot show window. Let me make sure I'm not sure window. And then we need to pass in the name of the window we are showing. So last time for the other tool, we let my other side window, the internal name of the window, and we let Maya give it to us, started to store it in this variable. Then we simply show that. Now the way this way this tool works is that you can actually open multiple copies of that window. And that's fine. But for the post library, we want it to be unique. We want it to have only one copy of the window open at any given time. So for that reason, I am going to give our window unique name. I'm going to create another global variable, alright, level variable. I'm going to call it a window name. This will be the internal name of the UI library. Ui. Now, as I mentioned before, I'm choosing all caps for the global variables because it indicates that it's a constant value throughout the code. So we're going to create our window using this unique name instead of letting my, I choose a name for us. And then I'm going to show that window name. Correct. So let's go to Maya and see what this looks like. So we're going to import from post library, import UI, and we're making changes to it. So let's have our reload command ready. Let's say UI dot show, underscore UI. There you go. The window is empty of course, but we have the title that we chose. Now, if I run this again. Without closing the window, you will see that we get an error. We say post library UI is not unique. What's happening here is that when I close this window, the name is gone. But when I open it and I tried to launch it again, my I cannot create the window. You can see here is when we're trying to create the window, that it complains that the name already exists. So there's something we can do about that, which is simply delete the UI. So we can, we can add a check before we create a window. We can check if the UI exists. We can say if CMBS dot window, and then the window name, sorry, open and close parenthesis window name exists. Because true. So we're querying, actually we have to query this value. Or I can just say query equals true. You can also write q equals true if you want, but I'm going to write the full name of the argument. Query equals true. Basically if the window exist, we're going to delete that window. That is done through the CMBS Dot Delete UI command. And we're going to delete the name of that window. Window name. Great. So what's going to happen is if that window already exists, we're going to completely delete it and create a new one from scratch. So let's see how that works. There you go. So now we can run this code multiple times. We get no errors. Excellent. Next, we need to create the top part, which is the part that is used to apply the poses. So we need a list and a button that is going to be inside of a column layout grid. So let's call this section applies posts section. We need a column layout. Assume the S dot column layout. If you remember from last time, we were doing adjustable column equals true so that the, the widgets expand automatically with the window. So we're going to use that in this case as well. I just double column equals true. And remember that when we create a UI, I lay out that layout becomes the current parliament parent for anything else that we create after that. So we know that we need to create a button. That one is easy. We know bottom. We know that the text in the bottom lips under the label argument. So we're going to say label equals applied pose. And we do need to add a command to the button, but our approach is going to be, let's create the skeleton of the UI first. And then we do any connections for the actual functionality that we have. So we have a button and we need a list. Now to create this type of list that we want, there is a command called Text scroll list. Now how do I know this? I, I looked up online, I found a few examples on line. And this was the best widget that I found for what we want to do. So let's go down to the examples of this. Scroll is tech scroll is that documentation. See what we can do. Number of rows alone, multi selection. We might be able to simply create it empty. So let's, let's simply create the list and see how that looks. So this will be CMBS, the art Text scroll list. Next scroll list, let's call that. And let's see what this looks like so far. The code, there you go. We have a list and we have a button top light bulbs. Excellent. Let's, let's leave that section like that for now. Let's move on to the next session section. Once again, we're trying to get the skeleton finished as soon as possible. And then we'd go back and polish them. The functionality than any other details that we might need. For our next section, we need to make clear this out where our next section we need these collapsible layout. Now, that is a type of layout called frame layout. Frame laid out. Let's see documentation for that. I'm going to go once again straight to the examples. That's usually the fastest way to get an example of this frame layout. With a label. We do need a label because we want, we want it to stay to give us the name of this section. So that frame layout label equals a string. So this section will be saved. Pose section. Excellent. So CMBS dot frame layout. And we're going to call the label to be safe. Pose. Excellent. And then inside of that layout, we talked about having a column layout. Let's create the column layout that we're going to use that second section. And let's actually, let's, let's reuse what we have here. Because we need the same type of layout. We also want it to be adjustable so that it expands automatically. Now under that, we know that we want this row layout with a name, label called name, and a text field. So the current pardon is this column layout, right? So we want to add a row layout under that column layout. And we want it to have two elements inside of it. Actually remember once again, from our previous, actually it wasn't in the stool, it was in another example. We need to, we need to specify the number of elements that are, the number of columns that will be in the row layout. Let's see how that is done. We go to the documentation show rolling out. We made. It says number of columns. Number of columns equals the number of items we want in that layer. Excellent. So it will be number of columns equals two because we only need a text, a piece of text, and the text field as well. Great. So now that our row layout is the current parent, we can add our label or a piece of text. And if you remember once again from the previous tool, we can steal that. So we can steal the texts command with the label. We can keep it simple. Simple text with a label and the label will say name. Great. And then next to that text, label, this up. Next to that text label, we need a text field. For the text field, there is a very conveniently named widget called text field, which is this one right here. All it does is it gives you a space to type in. And we can also query information from that text field. So let's have a quick look at the examples. It looks like we can simply call it and it will give us that text field. So let's go ahead and do that text field. There you go. So let's see what is happening, what it looks like so far. Let's go back to Maya and rerun our UI. Okay, great. It's like it's seeing some progress, not amazing. Our collapsible section is not actually collapsing. It just looks like another piece of label. And for some reason our text field is not, is not taking up the whole space. Okay, we can fix that. But let's, let's finish adding all of the buttons and then we'll come back and make this, this section collapsible. So the last thing we need to add is the safe post button. That should be easy. We can say CMBS dot button, label equals save. Now, if we run this right now and let's see what it looks like. Too many children in layout, row layout. Okay? So once again, as I mentioned, the current parent is this rolling out. So we could fix this by giving the Rowley add one more child or one more column to add. And this will not ever anymore. But now the safe pause button is here and we don't want it there. We want it under that section, right? So what we need to do is change the parent from being the current row layout to the previous part, which is the column layout. How do we do that? We do it by using this set current command, toupper and function. And there's a very specific notation that my legs and it's two dots. I'm going to make a comment here. Layout as new parents. Once again, the current or the current layout. The current parent is the row layout. So we set the new parent to be the previous column layout. And now our button should be in the column and not in the row. Again. There you go. That looks more like what we're looking for. So now let's come back and make that section actually collapse. Once again, that collapsible section is the frame layout. So if we go to the documentation, will look for that frame layout. There is an argument called Collapse. Actually, sorry, it's called collapsible. It handles the collapsibility of the frame layout. That makes sense. So we can simply, it takes in a Boolean. So we can simply say collapsible. Collapsible equals true. And that should take care of that. Reload our code and run it again. And there you go. Now we, we're collapsing. We have a section that can be collapsed. Excellent. So this is working because once again, the column layout is parented under this frame layout and everything else in that section lives under that column layout. So we are really collapsing that column layer. Now, the next thing that we have here is that section, that classical section has a different color. We do not have that in hours yet, so let's change that. And then we want to fix this TextField being too short. We want it to take, to take the whole space that is available. Let's look at how to change the color of our frame layout. Back to the documentation. There is an argument called Background Color. And it says, this is the background color of the control. Controlling this case means UI element. The arguments correspond to red, green, and blue, since the RGB values, each component ranges in value 0-1. So as you know, or as you might know, RGB goes 0-255, does, does, does a range of values for RGB. Maya wants you to translate that into a range of zero to one, which is very easy to do. And it's asking for a list. In this case, it can be a list or a tuple with the R, G and B, red, green, and blue values. So let's go ahead and use that argument background. Background, color equals, I'm going to use a tuple. Now this will be values 0-1 and they are floats, as you can see here in the documentation, it's a float because there's a decimal point in it. So the first thing that I'm going to do is get a color that I'm happy with. You can choose any, any set of RGB values. What I like to do is look online for an RGB color picker. E.g. Google has one right here. I chose slightly like a light blue color. So let me find something like that. And these are the RGB values. So we can take these values and we can convert them from, once again, the range is from 255. So this is the maximum value and the lowest value will be zero. So we can convert these numbers from that value to turn that wrench to a range of zero to one. Let me copy and paste this in Maya. I'm going to use Python to do this conversion. So we need to do a little bit of math for this is compression, but it's very simple math. So let's say that if our value for RGB value is zero, the equivalent of that in the zero to one range would be zero, right? If your RGB value is, the maximum value is 255, the equivalent in the zero to one range would be one, or in this case, 1.1. So that means that all we have to do is divide our value by 255, and that will give us this range. So in this case, I have this. These three numbers here. All I need to do if I want to know the equivalent of this in the zero to one range, I can simply divided by 255.0 because I want it to give me a resulting float. And this is the result. So let's, let's do this more efficiently and do a simple for loop for these values. So for value in this place through this tuple of numbers, I'm going to say print value divided by, divided by 255.0. That will give me the values we want this or this, this tree. Let's copy them and bring them into our code. Now, these have a lot of decimal places, but we don't really need all of that. We just do this. Email places should be enough. So this will be our red. This will be our blue. Sorry, our green, G, red, green, and blue. There you go. Let's go back to Maya and see what that looks like. That is the wrong code. This is the code that I want to run. There you go. So now that's section looks a little bit nicer. I don't like how it looks with the text. So I am going to choose a slightly darker color. And since we already have the code to do that translation, we can simply that in here, run it. Let's very quickly with that in our code. So I'm just going to take the first two decimal places. I'm just going to tap it by hand. So it's ten. The next one is 0.54, and the next one will be 0.67. Go, go back, try it again. And that is nicer. Now we can see the title of this section and we have a different color on it. Of course, you can choose whatever color you want. This is your tool, you are in control. Now the next thing we want to take care of is the sizing of this TextField. Now an easy way to do it would be to give it a fixed width, let's say 400 pixels. And that takes care of it. The problem is that we can now not, we cannot resize the window under 400 pixels now, which is a problem. So instead of that, we can handle the sizing through the layout. Most of the time when you have to deal with, with sizes and how widgets are adjusted in the UI that is controlled by layouts. So there's a good chance that the row layout, we'll have something for us. It's right here. It's the first argument that we see, adjustable column. So let's see what it says. Specify, specifies which column has an adjustable size that changes with the sizing of the layout. That is exactly what we want and it takes an integer as an argument. So let's go ahead and use that. I just doubled column equals the number of columns. So let's say, let's remove this from the text field. We don't want to fix the size in the row layout. We want to say adjustable column equals. Now this will be layouts start counting at one, not at zero. So this will be the first column. The first item would be here. The second one would be what we want to be adjustable. So we're going to say I just double column equals true equals two. The second column should be resizable, know, and there you go. That is what we want to achieve. Excellent. The UI is taking shape. We could leave it at that if you want, but I think we can make a few adjustments to make it just a little bit nicer. They applied pause button. It could be a little bit bigger, since that is the main button that we have. There could be a little bit of spacing in-between our elements. So let's adjust the spacing, the column layout as an argument called row spacing. Let's set it to ten pixels, e.g. that is a little bit much. Let's cut it in half to five. And for the button, let's say a height of 60 pixels. Let's see what that looks like. That is too much, but I think the spacing is a little bit nicer. So let's call it the height in half 30. Okay, that's a little bit better. And I think the safe post button could be slightly bigger as well. So I'm going to take this argument and add it to this safe pause button, but I'm going to make it 25 pixels tall. It's slight change, but I think that's, that's pretty good. The last thing we want to do before we move into conducting the functionality to the UI is making sure that the list only has, only can select one item at a time. Now, let's, let's see how that works right now. So we can pre-populate that list for testing purposes. If you look at the examples, we can append, let's, let's steal this argument, e.g. append. Surely let's just take the first three items. So if we say append equals at least of items, we are going to be adding, we're going to be appending those to the list in the UI. So if I were to refresh this, now we have all of these items. So as it happens, it already only takes one. So the 11 selection at a time, which is great. If you wanted this to be multi-select double. There is an option for that which is allowed multi selection equals true. Now if for whatever reason for this or for another tool, you wanted it to be multi. To have multi selection. You can do allow multi selection equals true. And when we query the query the selection, you will get a list of selected objects. But for the case of this tool, we only want to apply one post at a time. So I am actually going to be explicit and say allow multi selection equals false. Now the default behavior is of course false, but I think having that explicitly will be, will be a good addition. Let's put it side-by-side. I think that looks very similar. It's very pretty much what we want. So let's leave the UI like that. Let's start connecting the function. All we do. 40. Connecting the Functionality and Sharing Your Tools: Okay, we can start working on the functionality of the UI now. Let me refresh my code. So right now for testing purposes, we are appending items, do it, but we do not want to do that. We want to populate it based on the existing files that we have. We were going to make a function called Add Pose. And by the way, I'm sorry, I didn't add a docstring to my show UI function. Show pose, library, URL. This one is more cell self-explanatory, so it doesn't really need the docstring, but I want to make sure I stay in with the good habits. So we're going to add a pose to the scroll list. And the way we do that, we're going to get or post name. And we're going to add that to the list. How do we do that? Well, we can use the same command. A lot of these commands for us, but also for dealing with nodes allow you to create, edit and query information using all this and command. So I am going to use the same command. But now we need to know which list where modifying, right? So the way I'm going to do it, and it's going to be similar to how we're trading the window. We're going to create a unique name for this scroll list so that we can use it throughout the code. So I'm going to, I'm going to create a new global variable called text list name. And I'm going to give it a unique name. Those names, text, list that should be unique enough. And sorry, I forgot to assign the value. So now when I create the text scroll list, I am going to create it with that unique name. And that is done by simply passing the name as the first argument to that list function. Which means that when I am trying to edit that list, I can use that same name to target the same list grid. Now, when I'm editing a list, if I do this right now, Maria will try to create a new list with the same name. What we want to edit that list, I'm going to say Edit equals true. And how you know, actually I'm going to let, so let's finish this and then I'll show you something in the documentation. What I want to edit is is the, the, the elements in the list. So I want to append new elements to the list. What I want to do is append this pose name. Now, this is what I want to show you. Let's go to the documentation and have a look at this argument. We are in the tech scrolled lists documentation. And if we look for the append argument, we can see that it has these colors and these letters here. C is for create. You can create a list while appending something to it. You can edit, which means you can append things to an existing list. And m is multi-use. Let's see what that means. This means that the flight can have multiple arguments passed, either as a tuple or a list. Okay, that makes sense because we're expecting a list. And if we see, we see the type that is expected. That is actually very misleading. It's expecting a string. But in the example we see that it's a list. It's so happens that you can do both. You can either provide a list of elements or you can provide a single elements. So in this case, we're going to provide a single element as a string. And let's just test it out. But before that, let's add the docstring. You post name to the list. We know that post name is a string. And I think the name is very self-explanatory, so I'm going to skip the description for that. There you go. So let's, let's test it out. So let's add a few poses to it. Once they, once everything is created, actually, even after showing the window, we can add a few poses, are going to add the next one. We're going to add test too, e.g. let's make sure this is working. So when I reload, I have those two poses. Excellent. So that is the first step. Now we know how to edit our list. And I mean, we could also do the same here. We can do, we can say UI, the Add Pose and just call this another test. And that updates the list for you. Great. So now we want to create a function that will automatically populate all of the poses for us when we start the library. The first thing we want to do is clear this list so that we can populate it fresh. It's kind of like a reload function. So there is an argument called remove all. Remove all. That will simply clear out the list. So let's, let's do that. Let's, let's actually called this function. We can call it populate poses. Poses, or we can call it reload. Reload is a new, better name. Reload. Reload poses. The first thing we want to do is clear out the list so that it's empty. And let me steal this line of code. So once again, we're going to target the same list. We're going to edit it. But now we want to remove all. And as you can see, we can use that argument when we create the list or way we edit the list. So let's do remove all equals true. And that should clear out the list, make a comment. And now we want to gather the names of the posts is that we have saved in this system. So for that we already have a function. Remember we created this function that gives you a dictionary with all of the policies that are available to you. So we need to import our core module. So we can say from a library, import Core, which will be this one here. Now from that core module, we want to get the post diction dictionary. This dictionary will be equal to core, dot, get poses, detect, get poses. And as you can see, PyCharm is autocomplete for me because I am inside of the same project. Excellent. So if you remember, this dictionary will have the name of the posts as a key and then the file path for that post. So what we care about in this case is the name of those poses. How do we do that? By getting all of the keys from the dictionary. Both names equals poses, dict keys. So this will give us a list of keys from that dictionary, which will be all of the names that we care about. Excellent. So all we need to do now is add those to our list so we can do a for loop for, let's just call it name for name in names. Add underscore posts. And we will pass in that string name. And they should, they should take care of. Popular in deposits means that we don't need this test code anymore. And we can simply, I'm going to add a comment, call it populates poses. When can simply call our reload function, reload, post this. After everything has been created. There you go. Let's test it out. And there you go. These are the policies that we had previously from our testing. Now, if you want it to have them in alphabetical order. There is another function called sorted. Or you could also say pose names, dot sort. What's the difference is that dot sword will sort your list in place. And the sorted function will give you a completely new list. So let's use this one. Sorted those names. I'm going to call this sorted list. Actually sorted poses certain names, I'm going to call it certain names. Now we want to loop over sorted names. Now they should be in alphabetical order. Refers to the code. Oh, actually, we're seeing this another test one because I'm adding it here by accident. So that should not be in there. But now when I refresh the code, we should not see another test because there was an accident. But we should see the other policies that come from the files in alphabetical order. Now you see that first pose with my typo is coming first and then the test is coming afterwards. So now we're populating the UI. Before we move on, the docstring, gather poses from files and populate, list with them. We do not return anything. So I can actually simply leave this as a single light. There you go. The next thing we need to do is get the selection from the user so that we can apply that pose to the character. There is an argument called select item in the scroll list that you can use when you're querying or editing or creating. So that means that we can query that argument from that list directly. So I am going to create a new function down here, going to call it get selected pose. So now instead of editing the list, we want to still target the same list, but now we want to query something. And the thing we want to query will be removed item. So when you're querying something, you simply wanted to tell my dad yes, you want to query remove I'm sorry, not remove writing select item. We can use this to select something dynamically or programmatically. But in this case we're going to query the selection. So we're going to query select item equals true, simply to say yes, please select item. So let's draw this in a variable called selection. Now let's simply return that variable. So let's see what we get from this. Let's roll up the code. Okay, so right now there's nothing selected. If we do print you, I dot get selected, pose. Oh sorry, I need to call that function. We're going to get a none. So that is good to know. Nothing is selected. We get to none. If something is selected, we get a list with the selection. And because we're only allowing single selection, are only ever going to get a list with a single item. So that is good to know. Let me add that in docstring. We're going to say returns either a string or actually let me keep it consistent and make it a string. Always return a string, get selected item from the list. And we could make it so that if we get a none, we return an empty string. That thinking about it again, that might mean, might be misleading. So let's actually make it so that we can return. Again. I'm sorry, I'm changing my mind on spot a string or in none. We will, we will do, we will add a little description saying like non, if nothing was selected, otherwise, oppose name, selected, pose name. Great. So selection. If selection is none. We've seen the return None. Once again, if we just return, python will return a non anyway. But I'm making it explicit in this case because we do want to return that. Otherwise, if we didn't enter this if statement and we didn't exit the function. We want to return the only item in our selection, right? So I'm going to say selected item will be selection, and it will be the one and only the first and only item in that selection. I'm going to return that. Excellent. Now let's, let's test it out. Let me reload and rerun the code. Great. So if I print the selection right now, it's a non. If I select something I've printed, then I get the name of that pose that was selected. Great, that's exactly what we want. Now that we know how to get a selection, we can start applying the posts. Because we can get the post from the selected item in the list. So I'm going to create a new function called apply selected hose in here. Actually, I'm going to create it above this function that I'm using. To get the selection. Great. So the first thing that we need to get is selected pose. And we get that from the function we just created. Select both. Since this is the function that will be applying the poses when you do add a few checks to it. So if nothing was selected, you can say if not selected pose or if selected pose is none, either one of them will be valid if selected pose is known or if not selected pose. Let's warn the user. Let's say assume the morning. Please select a pulse and simply exit the function by returning. The next thing we need to do, since we are applying poses to select the character, is makes sure that the user selected something, selected a character. So we can say selected namespace or namespaces because it can be multiple equals. And this is where we started using the code from our previous project. If you remember, our post transfer module has a good select namespaces function, which we can use. In this case, I am going to import post underscore transfer. Now from post transfer, I can get the get selected namespaces function. Once again, we know that if nothing was selected, we're going to get an empty list from this function. So I can say, if not, select the namespaces, we throw in another, throw another warning to the user, saying, Please select at least one rake and we exit the function. The next step is to get the pose that we want, which means we need to get the file that corresponds to the post that we chose. To do that, we need to get the posters that have already been saved. Saved poses. We know we can get that from a function that we already use that to populate our UI is in the core module. Get poses dict. Now, since we use that same dictionary to populate our list, we know that the selection from that list should correspond to one of the keys in the dictionary. But there's a chance that while we're working with a tool, that file was deleted, in which case we're not going to be able to get it. So we should add a quick check for that. We can say, if selected pose not in saved poses, we're basically saying if this pose is not in the key in the dictionary, we can either do it directly like this or we can say dot keys or chicken the keys list directly. What I'm just going to leave it like that. So if that's selected pose is not in, they saved poses dictionary, which means it's not a key in the dictionary, then we have nothing to apply, right? So we can throw in another warning to the user saying that pose. Now I'm going to add the name of the pose. In quotation marks. I'm going to add double quotation marks, curly brackets as a placeholder. Let's say it does not exist. Then I'm going to say dot format. And then show the user the pose that was selected. And then after the warning, exit the function by returning. And finally, once we pass, we went through all of the checks and everything seems to be in-order. We have a selection on the list of poses. We have a selection in the scene for one or more characters. We know that the posts exist. Now we can get the file from that pose. Pose file equals once again, saved poses. I'm going to rename this to save, to post as dict, to make it more explicit for you. Saved both sticked, we can simply get, now that we know that that keys in the dictionary, we can get the value from that, which once again will be the file path. So that would be selected pose is the key in that dictionary. There will be the file. Then we need to get the information from that file. That would be the pose that we want to apply to our characters. Now, to do that, we already have the functionality in core. We called it read posts from file, read from file. And that will take a file path, which is its pose while variable. Excellent. Now all we need to do is once again reuse the, the post-transplant functionality and apply that to all of the namespaces we have selected. And that will be applied posts. We take in a post dictionary and a namespace. Since we are allowing for multiple namespaces, we can simply loop over that list. So for namespace in selected namespaces, post underscore, transfer dot apply pose will take opposed dictionary and the namespace to apply it to their ego. And before we test it out, let me do my docstring. And let's say Apply selected policies to select it. And we're not returning anything. So this can be a single line docstring. So we haven't connected it to a button, but we can test it out without having the connection. Let's go into Maya. Let's reload the UI. There you go. It's called apply selected pose. So we can do, we can say UI dot apply selected pose. Let's do some testing. We need to select a pause. It make sense. We need to select at least one character that makes sense. Now if we select the character, we run in, that post should be applied. Now. Now this is probably the same pose we saved. Let's change this character a little bit. And let's apply that. There you go. So the first, this first pose be saved was for the hat, which means it's only moving that controller. But there you go. So now that we have this function, we can start connecting it to the plight post button that at the moment is doing nothing. Let's go back to PyCharm and we can apply bows button. We can simply say command equals apply selected posts. Now remember we're passing in the function itself. We do not want to call it because then we would be giving the command argument, the result of that function. And that is not what we want. We want to give it the function so that it can call the function itself. So now if we reload our code and click the button, we have an error. Takes arguments with one argument given. Now, if you remember from our previous tool, the button that we connected, the button transfer selected, we had to account for an extra random argument. Then my N is given to us. And we use this star args argument, a sort of catch-all for the arguments passed in by my. So we can simply do that again. It will not change the functionality of the tool. It will simply let us solve what Maya is giving us. Supplies. I'd like pose, we're not even using it. We're just using it as a, as a net to catch that extra argument. Now, if I move this controller and apply that post again. Now this is working. And of course, we can apply that pose multiple characters as long as they have a hat. In this case. There we go. The last remaining part of our tool is the ability to save poses. And the first step in doing that is getting the input from the user to choose the name of the pose we're going to save. So to do that, we're going to be using the text field. And if you're guessing that we need to give it a unique name like we did with the window. We did with the scrolled lists. You're exactly right. So let me go ahead and create a new global variable. Let's call it text field. Name equals pose. Let's call it save those text fields. Now, the actual name doesn't matter as long as it's unique enough. Great. So when we create our text field, we need to provide with that name, again as the first argument in that text field function. And to be able to get the text that is typed into that, into that field. There is an argument. We go into the documentation. During the TextField, there is an argument called text. Should be around here. There you go, text. So we can use it when we create a text field, so that is pre-populated. We can edit it, edit the text in the text field, or we can query. So we're going to be proving that text. Let's go ahead and create a new function called save pose. We can do everything inside of this same function. And since we know we're going to be connecting this to the safe pause button. Let's add that star args sort of catch-all for the argument that we know mine is going to be given us. So to get the name of the pose from the, from the user, Let's call it. Let's call this variable called post name. Sorry about this. Firstname equals. Let's deal with TextField function. Once again, we're going to be targeting the exact same text field we created. We're going to query information from it. We will say query equals true. Now you can either type q equals true or query goes through, sorry, query, both are valid. And now whenever you're querying, you're going to use a boolean for the argument that you want to query. If you look at the documentation, is asking you for a string. But when you're querying something, once again, ignore that argument, ignore that part of the documentation, and simply type a Boolean. So just as a test, Let's return that post name and see what we get from it. So I'm going to reload our code. And we can say dot save pose. We know that right now. In the meantime, it simply giving us the name of the posts or whatever we type. That text field, print safe, Bose. There's nothing in it, which means that it's an empty string. But if I type post name my posts, e.g. I'm going to be getting that. So that's great. So we know that if nothing is typed in there, we get an empty string. We can add a check for that. There are still ways to do it. We can either say if name equals, equals empty string, we will give a warning or an empty string can be traded like a non or like an empty, an empty list. So you could say, if not Posts underscore name, we do a warning. And we will say, please hi, oppose name. Once again, we exit the function by returning. So I like to be a little bit more explicit. So I'm going to change this back to if post name equals, equals empty string means that nothing was typed by the user. And then we throw the warning and returned. And I'm going to remove this return here because that was only for temporary testing. The next step is to get the pose that we're going to say that we need to get the, check the selection in this scene. So as we know, we're going to save whatever controllers are selected. And we're limiting it to one character. We could actually allow multiple characters and simply save all of those controllers. And suppose if you want, that is your choice. For these cases though, I'm going to limit it to a single character. So to do that, we can steal some of the code from here. We can still select a namespaces. Actually let me steal all of this. This is a check. I'm going to copy it here. I'm stealing from applies selected posed, by the way. Because in applied selected pose we're also checking for selection of namespaces or rigs. So selecting spaces. Now in this case, we want to make sure we have only one rig selected. So we can, we can type if len of selected in spaces is not one. We can throw a warning saying Select, select the one rig in the scene. We show their warning and then we returned. After that, we need another check. Remember that we said that we want the poses so the pose names to be unique. So if oppose already exist, we cannot overwrite it. Maybe you, maybe you want to override it. That's perfectly fine. But in this case, we're going to say, we're going to throw a warning to the user saying that post already exist. Please choose another name. For that. We're going to get. The pose is dictionary. Once again. From applied selected pose, we're going to steal this dictionary once again, saved poses, dict. And let me add some comments here just to, to keep track of what's happening. Here. We're checking, check, or validate or invalidate those name. Here we're going to validate seen selection. Selection. And I'm leaving space between them, just give them some breathability so that It's a little bit nicer to see. Here, validate unique hose. So we get the poses from file. We could get the poses from the list as well. But getting them from the files is more reliant or reliable because we are truly getting it from the source. So if the tool happens to not be updated, we can still check the true source of the posters, which is the file system. So we can say, if we can write, sorry, if post name is already a key in the dictionary. So if pose name in is in saved pauses, dict equals name is impulse is ticked. Once again, we warn the user. We're going to say pose. We're going to put the name of the posing quotations in curly brackets as place holders is what I'm going to say already exist. Please choose another name. There we go. And then I'm going to say dot format and tell the user the name of the pose they provided. Post name. Once again, we exit by returning. Now finally, after we have all of the checks that we need, we can outlast save our posts. So the first thing we need to do is get the post dictionary that was other pose that was selected in the scene. So the controllers that are selected by the user in the form of a dictionary. Once again, we have this functionality from our previous, previous project in post transfer, post transfer, get, pose, dict. Now this function will get a single namespace. Since we know that our selected namespaces least should only contain one namespace. We can safely say, or we can say, safely provide the first item of that list selected spaces to our function. So there will be a single namespace. And now that we have the dictionary, all we have to do is save it to our file. And we already have that function as well. So that will be in core dot write posts to file. And that function will receive the name of the posts or the name of the file we want to save, which is the name of the pose and the values or the dictionary that we want to store. Suppose name, pose, dict. And we're going to print just doesn't us information saved pose. We're going to say successfully saved pose. Once again, let me fix this typo. Once again, curly brackets is placeholders, dot format, and post name to show the name of the posts. And finally, once we have saved the post, we want to reload or refresh the tool. So we can say Reload poses to show the newest posts that we saved. Excellent. So let's go ahead and test this in our scene. Oh, well, before we do that, we can simply connected to the button. We have gone through this multiple times. So command equals safe pose. Once again, just provide a function, do not call the function because that will give the result to that command argument. We want, we want the button to call the function for us. So let's come here. And let's delete this line that was protesting. Let's reload our code. Let's try saving oppose. Let me make a change to these characters arm. There you go. It's a little broken, but it will be very obvious when we applied. Let's actually call it a broken arm. We don't need the underscore wide-awake be a space as well. Broken arm, save the pose. That both shows up here. And we have here the concentration that's a successfully saved posts. And we can apply that to this character. And there you go. If you want to be more explicit with your confirmation, we could, instead of simply printing a message, we could show the user a little window. There is a function in C in this, in this library called confirmed dialogue that takes in a message, can type message equals. And we can pass in this message here. There you go. Confirm dialog. We have that message. We can reload our code. We can save a new post, new Bose ones that he saved. We get this confirmation dialog or the user. So it's up to you if you want the dialogue or simply print statement. And we're done, you now have a working post Library tool that can be used to save and apply poses across my assumptions are uneven, can be shared with other people, your friends, co-workers, anybody that uses Maya. Congratulations, This was the biggest project of the course. Now, before we go, I wanted to mention something about modules. So far, we have been working inside of these documents, Maya scripts folder. And that is because we know that Maya can get code from there. Now, let me go to that location. It's here. Our code is all here. There's other locations where might I can get code from? The other. Popular one is inside of the Maya version. There is another scripts folder. So this is only for my average in 2020. And the one we're working with is kind of more global for all of my aberrations. But now that begs the question, how do we know exactly where am I getting the code from? That? There is another module or library is called sits ASIS. Asis, That's stands for system. There is a list in there called path. So if I print sys.path, I get a list of the paths that the Python and Maya can get code from. So since it's a list, let me loop over it. For I in is a path. Hi, there you go. These are all of the parts. There's a lot of them. If you have plugins installed. Let me run that again. If you have any plugins installed, some of the paths for those plug-ins will be in here. And there's also the ones that we're using here at the bottom. So 2020 scripts for the migration are using Maya scripts, which is the one where we have our code right now. So this is, you can put your code anywhere here. But if you wanted to add a different location, since this is a list, you can simply add to this path you can append. So let's imagine that we have a custom, custom folder in. Let's say you're working at a studio or you're working on a project with your friends and you want to have your code and drop box or in a shared common location where everybody can access the code. I have created a folder as an example. Now, let's get started. Let's imagine that this is a folder that many people in your network can have access to. You can, you want to have your code in here? So let's move our code or let's copy our code from the documents Maya scripts location into this other folder. To post library was transfer. Let's just use neat post library and post transfer. I'm going to copy that into here. As it is at the moment, Meier has no idea about this location, right? What we can do is simply modify that list. Let me draw this in a variable called path. And I'm going to put the R at the beginning just in case what we can do is check if the path already exists. So if or if it doesn't exist actually, if no path, not in series, dot path, we've simply added to that path, says dot path, dot append. There you go. So as an example, since we're already loaded the modules from the scripts location, I don't want to get you confused. So let me create a whole new module called Hello path, e.g. and we're going to call this. We're going to rename the dot TXT extension to dot p-y. Excellent. And I am simply, I'm going to open this with Notepad for quick editing. There you go. I'm going to add a simple function called hello. Hello, or especially so in addition, and we're simply going to print hello custom path. Great. So right now, if I try importing hello path, I'm going to go to a new Python console. If I import hello. Of course my others know about it. It will say there's no module named Hello path. But if I add my, my custom path that says dot path. And once again, if I loop over it, printed for I in Notepad, I are going to see that our path is at the end of it. So short code, great. So now that it's in Python path, I can import Hello path. If I print hello, hello underscore path, you can see that it's a module coming from that shared location. And now we can use that function that I wrote that is simply called hello. And it's printing our custom string. So there you have it. I wanted to mention that as an extra because if you're going to be working with other people and you want to share your code. This will be the easiest way to do that. You could put all of your post library code. Again in a shared location. Simply have a shelf script that adds your custom path to the Maya session. You have to do this for every minute session, by the way. And then you have code that is being shared with multiple people.