Animated images in tkinter

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
let's talk about image animations what we are going to make will look something like this I can click on a button and we have a star if I click on the button again the start disappears the important part is that we have animation for this star to create something like this you already know most of the steps if you can import images and animate widgets you know the basics although there are some problems let me go through them number one for these kind of animations you have to import a lot of images so far we may have imported one or two images for this one there are going to be a lot more that very neatly brings me to the folder structure this one looks like this we have one python file this one contains the program we are going to make other than that we have four folders and all of them contain images for example inside of yellow we have a whole bunch of stars what we're going to do is we're going to play one star after another and if we do that fast enough we have an animation in case you're wondering here the first image is only white because Windows is weird the image here is actually empty going through the images is actually not that hard we just have to configure a button what is much more important is how can we import all of this I guess you could write 30 import statements but that would be kind of a pain because we also have other folders We have the black folder here we have a black star then we have a dark folder and here we have another kind of animation and then we have the light folder here's one more animation if you were to import every single file manually you would have to write a lot of code which means I want to find some smarter way of doing that for part number two we have to organize State Management this is going to toggle if the button has been pressed and goes forward with the animation or the button was already pressed and we have to run free animation backwards while doing that we also have to make sure we are preventing interruptions so what happens when the user is pressing a button while the animation is playing in my case I'm going to prevent any kind of button click other than that it's just basic animation logic so let's have a look in my case I am relying on custody Kinder because this one is slightly more complicated but all of the logic would also work for ttk the reason why I'm using custom tkinder is because well it looks much nicer but and if you can use this with custom decanter you can also use it with no multi kinder other than that I'm importing from pil image so we can have images then we are creating a basic window and we are running main Loop the result is going to look like this a basic window to stay consistent one thing I forgot is the comment to say run before the main Loop now for the button I want to have a dedicated class class let's call it animated button for this one I'm going to inherit from ctk button I forgot this ctk first of all this is the button we want to work with so far I only really inherited from frames but you could inherit from any widget and add specific bits to it in here we want to create a Constructor so a Dunder init method this one needs four arguments in total or what parameters self as always demo need a parent after that we need a light path and we need a dark path those are for the light images and the dark images because remember in custom tkinter when we are creating images we have to account for a light and a dark theme which means you always need two kinds of images I guess if you want to be lazy you could have the same image for the dark and the light image but that wouldn't look as good what we have to do in here first of all is run this super Dunder init method the master is going to be the parent besides that we want to have some text what you put in here really doesn't matter let's say a button or rather a animated button once we have that we can place the entire thing using the pack method or while any kind of layout method here is fine I'm going to use pack because it's easy with expanding true the button is going to be right in the middle with that I can create the animated button I can add in the window as the parent then I need two paths although for now I'm just going to add none and none because we have nothing for the paths if everyone is now we have an animated button although this one doesn't do anything right now that is bringing us to the first part that we really have to care about we want to write a method that Imports a whole folder or rather folders because what I want to do in here is I want to add self then I want to have the light path and the dark path the argument I'm expecting here is a path to a folder so if I reopen the folder again we have the python file here and what I'm expecting is for example yellow or black or dark or light any of these folder names is what I'm expecting just to be explicit here I want to have for the light theme I call this one black for the Dark theme so the second argument I want to have yellow once we have the actual images we can play around with the colors here but they all work in the same way in here we have to figure out how to work with that first of all though I want to Loop over both of these paths which is very easily done for path in then a tuple with the light path and the dark path if I print the result path while also making sure that I'm running this method in the init method self dot import folders with the light path and the dark path this method needs to be run before the Super method that is really important you will see later on why although if everyone is now we get black and yellow now we have to figure out how to get the folder contents from this path for that we are going to need another module that module is called OS however I don't just want to import OS instead I want from OS import walk what walk is doing is if you give it a folder name it gives you the content of a folder for example what I could be doing instead of printing the path I can print walk and then the path if I run this now we get a generator object that didn't go as planned but if you convert this thing to a list then you can see what's going on this is looking much better let me extend this a tiny bit we are getting two lists this list here for a light path and this list here for the dark path you can actually see for the first argument we are getting black and we are getting yellow those are the folder names for each list there are actually only three arguments the first one is the folder name this one we don't care about because we already have that next up we have an empty folder for both of them if there were any subfolders inside of these folders the names of these folders would be in this list since we don't have any subfolders we can entirely ignore this one as well it's simply not relevant for us however afterwards we have a long list with all of the files inside of this folder this is what we actually care about on top of that you want to be aware of the order here in my case I'm starting with zero zero zero zero zero zero one then two three and so on on some operating systems I think in particular for Mac OS this order might be messed up I'm going to show you later how to account for that but it's definitely something you want to be aware of it can mess with your animations quite a bit although another really important thing to mention is that what we are getting in here are just file names all of these are strings they are not files they're not images we just get a string of a file name we still have to do the actual import but that comes later first of all though I want to add another for Loop or let's call it data in walk path the result is going to be if I print the data we have the stuff I've just shown you although we can be a bit more elegant here since this walk path is returning three things we can unpack them right away inside of the for Loop we get the folder name we get sub folders and then we get the let's call it image data all we care about is image data this is what I want to print so if I run this we get all of the image names to indicate that I don't care about the folder name I will replace this one with an underscore a subfolder I also don't care about so this one is going to be a double underscore next up the image data if I print this again we absolutely have to make sure that this data is sorted it always says to start with zero zero zero zero and end with zero zero zero and two nine with the numbers going from the lowest to the highest you might get this by default you might not and you really want to make sure for that I want to create a new variable I'm going to call it sorted data in here we want to use sorted what I want to sort is the image data but to be a bit more specific here I want to set a key because if we just passed in the image data let me show one entry I want to print image data and then the one with the index 1. also let me comment out this bit here so we're not getting an error if I just print one we get this bit here and this is a string if we use this with sorted python wouldn't really know what to do with it because if you compare different strings it's not really clear which one is smaller and which one is larger instead what I want to do is only get the last five digits if I then turn this into a number python can sort it from the lowest to the highest and obviously if you have different file names the logic here would be different it very much depends on what kind of system you have in my case I created these with After Effects and this is the default name so I can't really change that let's first of all only get the number here with this one item we first of all have to get rid of the dot PNG that we can do using the split method and I want to split this wherever we have a DOT like so if I run this now we're getting a list with two items returned I only care about the first one which means I can use indexing here to only get the first one with that we get image and light zero zero zero one since we can do indexing on strings we can use indexing here once more I want to go from negative five all the way to the end if everyone is now we are just getting the number itself although if I print the type of what we are getting we are still getting a string which means we have to convert all of this to an integer and now we are just getting a number you can see we're getting a number because python removes all of the zeros which means this logic here is what we actually want to use let me uncomment sort it put this over multiple lines now for the key I have to use a Lambda function the Lambda function will always get one let's call it item this item is now going to be my image data which means I can copy all of this paste it in here and replace image data with item then I can comment all of this out fix the white space here on top of that we have to remove this one here we only use that to get one item from the image data this we don't need anymore now if I run this we're not getting an error that's a pretty good sign but just to be sure let me print sorted data the result is still going to go from 0 all the way to 29 although now I think if you are on a Mac the result here should now be a properly sorted list with that I can get rid of the print statements this one and this one and we have sorted data this is still not enough because now we need the full path you might be wondering now don't we already have that from this image data the answer is not quite let me print it actually again if I print sort it data what we get is a file name but this is not a path what we have to do instead is first of all for the full path we need the folder name then Plus then a slash and then we need the image name fortunately we have everything we need the image names we are getting from this list here the folder name is I have a light path or dark path which we are getting from the path although since we have quite a few of them I want to store all of this inside of a list let me change full path to full path data and now we are going to use list comprehension I first of all want to get the path this is either light path or dark path in our case this would be black or yellow after that I want to add a slash and then I want to add one of these file names I guess we can use item again so I want for item in sorted data with that if I print default path data we are now getting actual paths we go into the black folder and then there we have image000 then black image001 and so on this is what we actually need although we have to get all of this out of this for Loop so we can use it a bit more efficiently for that I want to have another variable I'm going to call this one image paths this for now is going to be an empty list and once I have full path data I'm going to append that image paths dot append bold path data with that after we are finishing the for Loop let me minimize it actually I want to print image paths this is not going to give us two long lists full of image paths the first one is all of this and the second one is all of this this is a good start but I want to modify this a tiny bit ideally I'm going to have a list let me draw it here inside of this list I want to have lots of two builds each Tuple should contain one dark image path and one light image path so one in here and one in here then I want to have another Tuple with two more of those so that would be this image here or this image path and then this image path here I want to assign a new value to my image path the value I want to assign is zip and then I want to unpack the image paths let me print actually what we get this might be a bit complicated image paths what we now get is a zip object that's not particularly helpful but we can turn this into a list and now we can see what's going on what we're getting now is one long list and this list is full of tuples one Tuple contains the first image for the dark image so black image000 and then the light image so yellow image 0 0 and 0. then we have another Tuple here and this continues forever we keep on having more and more two builds this makes it very easy to work with this data which is exactly what I wanted also in case you're wondering here zip is a function that zips together two lists in a way that you take the first item from the first list and the first item from a second list and then you combine those two into one Tuple zip is expecting two arguments which should be two lists which we get by unpacking image paths because this one contains two lists with that we have all the data that we need so now we can turn all of this into actual ctk image objects those I also want to store on a list so we're going to continue by creating ctk images which is going to be another empty list we are almost done actually this is the much easier part what I want to do now is for image path in image paths let me print what we get image path we are simply getting a tuple with the light images and the dark images or however you want to call them the naming here doesn't matter terribly much what I want to do with that is I want to create a ctk image this I do with ctk and ctk image this is what we need in custom to kinder to store image data inside of this we need a light image and we need a dark image since in my case the first item inside of the image path Tuple is the light image this is what I'm going to assign here which means we can now use image dot open and pass the path in here which would be image path 0. I should probably put all of this over multiple lines otherwise this will be difficult to read we are using image open on the first item now we are doing this on the second item as well or the one with the index one once we have that all we have to do is get ctk images and append the ctk image with that we are going to have one long list with all of the cdk images this is what I want to return so return ctk images then we can close this method and never worry about it again and this was by far the most difficult part of this tutorial if you got so far the worst part is definitely over once we have the imported folders I want to store the return value inside let's call it self dot frames just to make sure this is working let me print self dot frames and we are getting a list of objects this doesn't tell us very much right now but at the very least we are getting something I guess you can tell here we are getting ctk images so that's a pretty good sign once we have that we need actually to create a bit more stuff in here so let me create a proper section that I call animation logic setup besides the frames we also need what I call a frame index this by default is going to be zero later on I'm going to explain the animation logic but for now this is what we're going to use to pick one item from the frames that we want to display we can actually already use this when I'm setting up the button inside of the init method I can also add an image this image is going to be self dot frames and since this is a list I can use indexing I want to get self.frame index in here and once again this should be over multiple lines otherwise I'm cutting off some text if I now run this there should be an image you can see this in empty space but nothing more the reason for that is that the first image is completely empty this is why you can't see it however what we can do now if I change the frame index from 0 to let's say 20 now we can see one star if I increase the number to 21 we get a slightly larger star if I go to 26 okay this is going to be very hard to see but if I go to let's say four we get a very small star the differences between these images is so small it's really hard to see if you see them by themselves for now just trust me this is definitely working next up we need self dot animation length this is going to be the length of self dot frames although from that I want to subtract 1. the reason for that is imagine if we had a list right now with three items the list would be 0 1 and 2. this would be three items however if I want to use indexing it would always be the length minus 1. the final item would be the length of the list so three minus 1 this is going to be really important for this animation length because I only want to go up to this final item but then I want to stop so I have to know what is going to be the final index of the list which I'm going to call length it's not exactly accurate but I think I get the idea there's one more thing that we need and that is self dot animation status this is going to be a teak into a variable almost specifically is ctk string VAR this one is going to have the value of start by default now why do I want the animation status to be a string VAR well the reason here is because with that I can use tracing so self dot animation status and I can use trace and anytime I'm updating the value so I'm writing in it I want to run a certain kind of method this method is going to be n mate let's create this one right away I want to have animate what is important here besides self we also need unpacking and arcs because every time we are using trays we got some default arguments although in our case we don't really care about them for now I'm going to add pass in here because there's one more function that I want to create that is going to happen inside of this super init method because I only want to start the animation when I'm clicking on the button which means this one needs a command the command function is going to be self and I called this one trigger animation I'm going to create this one above animate so trigger animation with self and nothing else in here we are going to do State Management which means we are updating animation status for example if self dot animationstatus dot get which would right now get us start if that is the case so if this is equal to start then I want to set self dot frame index to zero so we are starting on the first frame of all of the images on top of that I want to set self dot animation status with the set method 2 for Ward which basically means that we want to start at zero and move the animation forward besides that I also want to check if self.animationstatus dot get is equal to end if that is the case I want to get self dot frame index and set this to self dot animation length other than that I want to set self dot animation status with the set method to backward since with this we are updating the tkinder variable with forward or backwards this animate is going to be triggered now we can check for different things for example what I could be checking for is if self dot animationstatus dot get is equal to forward if that is the case I want to play the animation which in this case means I want to get self dot frame index frame index like so and increase the value by 1. which means that this number here self.frame index when we use it first time gets the first image which we are using down here with the image this I want to increase to one two three four all the way until the end of the animation frames if I go through them fast enough we are going to have an animation which means I can use this now with self.configure I want to update the image the image is going to be self dot frames in here self dot frame index let me run this actually if I now click on the button we get a very small star this is because we're only calling this animate once so we only go from frame 0 to frame one we will need self dot after after 20 milliseconds I want to run self.animate again with that if I click on this now we have an animation and then we are crashing since we are running this method here forever at some point we are going to run out of frames or more specifically we have a larger index than the length of this list which is giving us the error or more specifically we get list index out of range to account for this we need an if statement if self dot frame index is smaller than self dot animation length only then do I want to update all of this if I try this now and click on the button we have an animation that's looking pretty good if that is not the case so else I want to set self dot animation status using set to end that is telling us once we get to the end of all of this I want to have a new status because I know the animation has finished if we're done pressing again this is stepping here with run and we would set the animation status to backwards by now this wouldn't do anything because inside of animate we only have this bit of code here meaning we're only running any kind of Animation if the status is forward to account for that we need a second one that checks self dot animationstatus dot get is equal to end if that is the case I want to do very similar things compared to what I have done here although not exactly let me start by copying it and now I want to set self.frame index and reduce it by one every time we are calling this configure still works just fine although for this if statement I only want to do all of this if self.frame index is greater than zero this part is still fine although in the else statement if we are finishing the animation I want to set the status back to start if I'm running all of this now I can click on an animated button and now we are almost done the animation works but we always go to the end and then back to the start the reason for that is that we only want to do this if the status is backward now if I run this this goes forward and now nothing happens but if I click on it again we're now going backwards yes I can do multiple times still works just fine on top of that when I'm creating all of this I can get ctk set appearance mode and switch all of this to light mode we are now getting a different kind of Animation or while the same kind of Animation just different colors if I open the folder again we are importing yellow this is the dark images this one is all yellow and then black has the same animation just in completely black although besides that we also have dark this one has a hard animation either for white or for the dark path which means if I add in here light and dark we importing those folders and now we get a heart that we can also animate although I think there should be the other way around dark and Light that is definitely looking better if I removed the appearance mode and I guess it kind of works let me stick to yellow and black I think those look a bit better although I had this the other way around earlier this was black and yellow there we go now we have the yellow animation again cool with that we have made a lot of progress although there's one challenge I have for you I want you guys to create another animation and this one should run forever that means we go from frame 0 all the way to the end once we reach that point we're going back to the start and then once again we go from zero to the end that way the animation will keep on starting over and over again try to implement this one I want to keep all of the logic contained inside of a method I will call this one infinite animate no need for custom parameters this I also want to trigger when we are pressing the button to check if this is working let's print infinite if I now click on the button we get infinite that's a good start now we have to figure out the logic for that self.frame index and increase it by one once I have that I want to use configure again I want to update the image the value is going to be self dot frames with the index of self dot frame index finally I have to run self dot after I want to update this after 20 milliseconds and call Self dot infinite animate again if I run this one now we get the first animation but then we are crashing because we are running out of list indexes again to account for that we need one more line of code basically what I want to do I want to update self.frame index and I want to set it to zero if self dot frame index is greater or equal than self dot animation length however if that is not the case else I want to keep self.frame index as self.frame index the way you want to think about it is if we have a list with 0 1 2 3 and 4 items self.frame index is going to start here then it's going to jump to this one then to this one then to this one then to this one and if we get to this point here this if statement is going to trigger and it is going to put self.frame index back to the start that way the animation will go on forever let's try this one now if I click on the button we get a continuous loop although I think because of the equal sign here we are skipping the last frame let's remove it and play this again there we go you can't really see a difference but well there's one more frame in the animation now and with that we have animations obviously you can make all of this a lot more fancy but this should be a really good start
Info
Channel: Atlas
Views: 8,942
Rating: undefined out of 5
Keywords:
Id: wf0aArIhTrk
Channel Id: undefined
Length: 32min 35sec (1955 seconds)
Published: Sat Jan 07 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.