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.