Using images in tkinter [ including how to scale them ]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
another really important part for good looking user interfaces are images in tikkinder you can add images to some widgets labels buttons and canvas is what you're going to use most of the time that being said for images that scale properly you will need to create a logic yourself so the scaling logic doesn't come with tkinder that is something you have to make yourself on that note what I have mentioned yet what we are going to create is going to be this one here we have two buttons one with TDK and one with custom tkinter and then we have a raccoon and this raccoon we can't scale and it's always going to cover the appropriate area this kind of logic you have to make yourself it doesn't work by default now before we can do all of this we have to do one more thing and that is we always need a pillow library to use images pillow is the default python image Library this one you have to install on your own first of all this you do either with the Powershell or The Terminal in my case this is the Powershell what you have to type is PIP install hello this is going to install quite a few things obviously if you're on a Mac and using terminal this would be pip free install hello once you have that we can start working on some code for the starting setup I am importing tkinter and ttk on top of that I am importing custom tkinter we are going to use both because they have slightly different setups for importing images after that we're creating the basic teak into window and then we are running the main Loop the end result is going to be a basic window before we can start properly we have to do one more thing and that is we want from pil import image and image TK pil is the pillow we just installed for some reason the input here is a different name compared to the import in the Powershell or The Terminal pil stands for python image library and some are for a PQ to call it pillow I don't understand it either it's quite weird but this is how you would import it inside of pillow you have one really important object and that is called image you basically always use that one and image has a custom teak and a variant that is called image TK on that note I have made a really long tutorial on pillow itself it's on YouTube you can watch it for free you can use pillow for all sorts of things like scaling images changing colors adding text and well lots of other things so check it out if you are interested although for this video I just want to import an image to make that work first of all I need to cover our folder structure right now I have one folder and this folder contains the code the code file here is called images in this folder I have three more images I have python dark python Lite and raccoon those are the images I want to import for the import first of all we need to import the image itself so image let's call it original this we get with image dot o pen and now we need the file path since I want to import let's start with the raccoon I want to import raccoon and the file ending here is jpeg with that we have an image so I can run the code we're not getting an error that's looking pretty good now I have to figure out how to get this image on a widget for that first of all we need a widget to get started I just want to create a label and this can be ttk label the parent is going to be window the text could be let's go with raccoon and let's pack the label there we go we have raccoon a label can also take an image although what we cannot do is simply pass in the original image in there we will get an error that the image specification must contain an odd number of elements I don't know what that specifically means but basically what tikenter wants is an image TK not an image we can account for that quite easily I want to store this in another variable I'll call it image TK this image Decay we create with image TK dot photo image you only need one argument that is the image original with that we can pass an image Decay into the image and it should be working there we go the problem we have right now is that the image is way way too large for reference the raccoon has a size of six thousand by four thousand while our window is only 600 by 400 so we can only see a very small part of this window right now but we can resize the image this we do when we are importing the image this image here is returning an image object this image object has a couple of methods the one we care about right now is called resize resize wants a tuple with a new size and a new width let's say for now I want to cover the entire window with the image this would be 600 by 400. with that we have the raccoon covering the entire image although if I resize the window this breaks quite fast but at the very least we do have a start unfortunately though you basically never want to use an image inside of a label for the simple reason that you cannot resize the image if the window changes you always have the same image size depending on what you get from here to change that you need quite a bit more logic so I am going to cover that later on let me get rid of the resize and comment out the label running this again we have a plain window but we do have an imported image before I expand the logic on how to use images I want to cover buttons because buttons are very easily usable with images let's say I want to create a button this is going to be ttk dot button the parent here is going to be the window and for text we can go with a button this button I want to pack right away now we have a button this button can also accept an image I could add image Decay now we have the raccoon again but remember the raccoon is way too large but the raccoon is not what I want to add to this button instead I want to import a different image that image is going to be python dark which we get with image dot open the name of the file is called python underscore dark dot PNG important here we have a different file ending because python dark has Alpha values so we need a PNG this once again we have to convert let me call it python dark TK for that we need all of this I can simply copy it like so and paste in Python dark this python dark TK I now want to use for the button like so and this is still way too large although I guess a little bit better since the button isn't going to change size for this one it is fine to Simply resize the button here and then you don't have to worry about it anymore in my case I want to resize the button to a size of let's go with 30 by 30. now we have a button with a python logo to see the text again we need a compound argument if I for example set right here we now have a button and then the logo to the right this can also be left and then we have the image to the left of the button for a bit more spacing you could do a tiny bit of a hack here and simply add some spaces for the button and now we get a bit more spacing between the two however if I duplicate all of this and change the button to let me call it a button ctk because I want to create ctk dot ctk button for that one we are getting an error that photo image object has no attribute create scaled photo image once again I'm not quite sure what that means but the concept you have to understand is that you couldn't use an image TK this one here inside of a ctk widget instead what you would need to do is create a separate ctk image as you get with ctk and ctk image this is what you have to pass into is ctk widget if you want to have images I'm going to store this one in an image ctke variable the reason why this is different is because for custom tick enter we always need a light image and we need a dark image those we have to import separately but other than that we are using it the same way how you usually want to do this let me put it over multiple lines actually what you can do with the input here is simply use image open as the argument for example I could paste image open python dark in here and then for the Dark theme I want to have python light this might look confusing here basically you want a dark image for the light theme and the light image for the Dark theme for the simple reason that light image has a light background so you want to have a dark image and then the other way around is going to work the same with that we have an image cdk and this is what you want to pass into this image now I can run this and there we go we have a python logo for a button that is bright right now if you were using custom tick enter and had a light and a dark theme this would also cover the Dark theme although since I am using tkinter by itself this isn't really applicable but it would definitely work with that we can actually cover how to use tkinter with images the major problem that you have in tkin type of images is that images do not scale by default we could have as large or a small of a window or any kind of container the image unless you add some custom code is not going to update which would break any kind of layout really fast to account for that we have to add an image to a canvas and then use the canvas Dimension to update the size of the image that we can do in real time although we have to figure out a few Mappy things in here it's not going to be too bad although once we have that we can add an image to a container and always make the image scale to that container and before I start I want to create a different kind of layout I want to have a grid where those two buttons are on the left and then on the right we have the actual image for that I'm going to put it up here I want to create a grid layout I want to get window dot column configure in here I want to have 0 1 2 3 in total four columns that all have a weight of one and I want them to be uniform like so so uniform is equal to a besides that I only want to have one row so row is just going to be zero uniform we can just ignore for that the image Imports can stay the same although for the buttons we have to update them because we're using pack but now we have a grid as a consequence I want to put both buttons inside of a frame I'm going to call this button frame and this is ttk dot frame with the window as the parent both of the buttons are going to be mastered to this button frame finally I want to place the button frame using the grid method I want column to be zero I want the row to be zero and then I want to have sticky with north south east and west if I run this again now there we go we have the two buttons all the way in the top left I think to make this look a tiny bit nicer we can give both a bit of vertical padding let's say 10 pixels now there's a bit of space between the two with that I can create the canvas for the image this is simply going to be a canvas so TK dot canvas the parent is going to be the window and for now this is all we are going to need this canvas I want to place right away canvas dot grid this is going to be column one column span is going to be free we are covering all of the remaining columns finally row is going to be zero also I want this to be sticky to all four sites if I run this now we can't see anything because we don't have a background color which I can change quite easily background let's set it to red and there we go now we can see the canvas itself however the canvas doesn't cover everything perfectly for example at the bottom here we have this very thin line same on the right side and we would have the same on this side and this side as well this we have because of the border of the canvas we can get rid of it though all we have to do is set BD to zero then we need High light thickness and set this to zero as well finally the relief should be rich with these numbers the canvas is now going to cover the entire area and we have no border also let me set this to Black so it looks a bit better there we go on this canvas we can now add an image yes we do with canvas dot create image for the arguments here we need an X and the Y position I'm going to set it to 0 and 0 which is the top left and then we need an image this needs to be a TK image which in my case for the raccoon I have image TK mhdk now for runners we can see one eye of the raccoon what we could also do in here is set the anchor by default we are placing the center anchor like this but I want to place the North West now we can see a top part of the image again but it doesn't do very much the way you have to think about it is that the entire image starts here and then goes really really wide to the right and to the bottom I believe the numbers were six by four thousand so much larger than our window to account for that we have to dynamically resize the image I don't want to create the image right away instead I want to do all of this inside of a function this function I'm going to trigger every time we are changing the size of the window this we get with canvas dot bind in here we need configure this is going to trigger every time the size of the canvas changes if that changes I want to run a function is simplest for the image is stretch image you're going to see in a second what this one is going to do but let me create it first of all all the way at the top I want to create stretch image in here since we are working with an event we need one default argument and that is the event what we are going to do in here is we are going to get the size of the canvas and then we are going to scale the image to that size finally then we can actually place the image for that though first of all we need to get the windows size or more specifically let me call it size we want to get the width and we want to get the height the width is going to be event dot with and the height is going to be event dot height if I print both of those numbers we have with and we have height now if I run this we are calling this function and if I resize the image we get updated numbers so this is working quite well just to make sure now the thing is really narrow and very very small but now it's much larger so the numbers seem to be pretty accurate these numbers we can now use to create an image although what is really important you already want to have one image imported and you only resize it when you resize the entire window you do not want to re-import the image every single time you are resizing the window that would be really inefficient in my case though I already have one image imported this is what I want to work with I want to store all of this inside of a new variable I'm going to call this resized image we need the image original and then resize it since resize wants to have a tuple with a width and a height this is quite easy because we already have width and height although this is still just an image but we need an image TK we need another variable that we call it resized TK this is going to be image PK dot photo image the same thing I have done down here except now the one argument is going to be the resized image finally this we can place on the canvas for that all we need is canvas dot create image the start position here is going to be 0 and 0 the image I want to place is resized EK finally the anchor is going to be North West now if I run this we only get a black background color so something didn't go well the problem is that the image TK this line here always has to be in the same scope as the main Loop or wherever you call it in our case an easy fix here would be to set resize Decay as a global variable resized TK and now we can see the raccoon so just to reiterate we have our window in the global scope and we are also calling window.main Loop in the global scope because of that the image Decay needs to be in the global scope as well they always have to be in the same and once again I have no idea why but with that we have the image of a raccoon and if I resize the window the raccoon resizes with it because we are updating the size of the window every single time we're updating the canvas which is working but you can see a limitation here we are stretching the raccoon quite a bit to account for that I want to create a second function I'm going to call this one fill image this one is not going to resize the image instead we are going to fill the entire image and we're going to cut off parts of the image that don't fit what we are going to do for this one let me explain it first before I create a function let's say this one here is the canvas and the image might be as large as this bit here inside of this function if the image is larger than the canvas we are going to cut off this bit and this bit that way the raccoon is going to keep the same aspect ratio later on we will create a third function that will keep the raccoon always inside of the canvas but for now let's fill the entire canvas for that once again the one parameter I need is the event I want to have Global resized PK now in here we first of all need to get the current ratio of the event or off the canvas the same thing this we get I want to store in the variable canvas ratio open it here is event dot width divided by event dot height this number is really important and we need it both for the canvas and we need it for the image we have imported this image here this we can also get quite easily I want to store it in a variable let me call it image ratio to get the width and the height we need image original dot size this is returning a tuple with the width and the height which means the width is going to be zero and this I want to divide by the height so size 1. if I print all of this image ratio this one we get 1.5 that number we get if I open the folder again the raccoon has a size of six thousand by four thousand pixels dividing six thousand by four thousand gets us 1.5 this number is really important because it tells us how we are going to scale the image itself for some context here let me do all of this actually on a separate surface imagine we have a perfect square where all sides are one if that was the case and we would divide the width by the height we would get one this is telling us that we have a perfect square if we have some kind of rectangle with a width of Two and a height of one in that case we would divide two by one which would give us two which tells us that the larger that this number gets the wider the entire thing is by the same logic if we have a really narrow container something like this where we have a height of one and a width of 0.5 that is a horrible five 0.5 then we would do 0.5 divided by 1 which would be 0.5 which means the smaller this ratio becomes the more narrow our container is going to be this applies both to the canvas and to the image itself this information we can use so in our case we know the ratio of the image and of the canvas what we want to do imagine this one here is the canvas or C in short this we want to compare with the image itself or the aspect ratio of the image let's say this could be something like this this would be I for image if the canvas has an aspect ratio of let's say 1.8 with the image having aspect ratio of 1.5 we know that the canvas is wider than the image if that is the case I want to scale the width of the image to the width of the canvas which right now would simply stretch out the image so this wouldn't be particularly useful however since I know the aspect ratio of the image I can simply use the weft and then combine it with the aspect ratio to get a new height of this image even if I resize the entire canvas I would fill the entire width and then the top and the bottom part of the raccoon so the parts that are outside of the image are simply going to be cut off I hope all of this makes sense the math here isn't particularly difficult but you have to get your head around it I think if I Implement all of this this is going to make a bit more sense what I really want to figure out is I want to get the coordinates of my image which means ultimately I want to get a whiff for the image and I want to get the height of the image although for these images first of all I want to know if the canvas ratio is greater than the image ratio if that is the case we know that the canvas is wider than the image if that is the case I want the width of the image to be the event width that way we are filling the entire width of the canvas there's one thing that tea can do is a bit fussy here and that is that we always need to have integers a very easy thing to account for once we have that width let's say for some imaginary numbers the width of the canvas could be 800. on top of that we know that the image ratio is 1.5 these two numbers we can now use to calculate the new height of the image all we have to do for that is rearrange this formula here initially we got the canvas ratio but now we want to have the height this height here this we get by getting event dot with or the width of the image itself this with here and then divided by the image ratio this also needs to be an integer let me remove the white space this information we can now use because with that we can create a resized image which is going to be the image original and then I want to resize it with the with and the height after that we can create a resized TK which is just going to be image TK dot photo image with the resized image finally there's one more thing we have to cover we still want to get canvas and create image although now for the X and the Y position we want to place the image right in the middle of the canvas all we really have to do is get event dot with that is the width of the canvas and divide it by two decanter or the canvas more specifically wants to have an integer this we can do again for the Y position I simply want to divide the height of the canvas by two finally besides that I want to set an anchor the anchor is going to be the center once we have all of that ideally over multiple lines so it's easier to read we can place the image itself the image is going to be resized TK now if I run this we can see the raccoon and the raccoon scales with the canvas because I forgot to actually call fill image whenever we are resizing all of this now if I run all of this again we are getting an error because by default this if statement here is not true to account for that let me change it to else with with being one and height being one as well so we get at least something with that we can't see anything however if I make this less tall there we go we can now see the raccoon at least on a certain aspect ratios but if I make this smaller at some point it disappears that happens because this if condition doesn't apply anymore because in the else statement a canvas is narrower than the image this is what we have to account for as well for that let me copy the width and the height now I need these numbers here for this one we now want to have the image covered the entire height of the canvas which means the height is simply going to be the event dot height for the weft I want to get the height once again and multiply this with the image ratio this should be multiply not plus with that all of this should be working now we can see the raccoon and the raccoon works just fine we are never scaling this one we are simply cutting off parts that are outside of the canvas this is looking pretty good for the math here if you really want to understand this I would recommend to go over this a couple of times basically you always want to imagine that you are scaling a canvas and then you are fitting an image inside of it when you're hearing this the first time it can be quite confusing so definitely go over this in your own time the math really isn't hard it's just some logic that you have to grasp with that we are nearly done there's one more thing that I want to do in this case the raccoon is always going to be fully visible and we are scaling it in such a way that we never cut off anything but the aspect ratio itself stays identical this is going to be reasonably similar compared to what we have done here with fill image which means it's going to be your exercise I want you guys to create a third scaling Behavior to always show the full image without cutting off parts the video now and try to figure this one out you can reuse a lot of this since I am going to reuse quite a bit I am simply going to copy the entire function paste it in here and rename it to show full image this function I want to call right away down here instead of configure I want to show the full image inside of the function Global can stay the same the current ratio we also need and all of the stuff down here is also going to stay identical which means the only part we have to change is this bit here specifically we have to get new numbers for the width and the height for all aspect ratios the way you have to think about this one we once again check if the canvas ratio is larger than the image ratio which means that the canvas is wider than the image for example if this one here is the canvas the image would be something like this what we have done before is we have scaled the width of the image to here and then use that number to calculate the new height of the image this I want to change now instead what I want to do to show the entire image I want to scale the height to only be as tall as the canvas itself this number I am then going to use to calculate the width of the image which means for this one I want to have a new height the height is simply going to be event Dot type with the with simply being the height multiplied by the image ratio we can try this one right away if it's like this we have the entire image but now if I go even further now we have a black background and we can see the entire raccoon which only leaves us the last bit here or this one I want to start with the width because now the canvas is narrower than the image in this case the width is going to be the event dot width order height I want to get the width and divide it by the image ratio that should be all we need with that we have the entire raccoon and we can always see the entire raccoon regardless of aspect ratio and well the raccoon updates automatically this image in all of its forms so these three functions work slightly differently but once you have them you can place the canvas in any kind of container and the size would always scale that way you can use the image wherever you want which is what makes it actually useful
Info
Channel: Atlas
Views: 23,360
Rating: undefined out of 5
Keywords:
Id: VnwDPa9biwc
Channel Id: undefined
Length: 31min 56sec (1916 seconds)
Published: Fri Jan 06 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.