Python Tkinter Tutorial (Part 2): Using Classes for Functionality and Organization
34:46

Python Tkinter Tutorial (Part 2): Using Classes for Functionality and Organization

Corey Schafer 15.08.2024 44 337 просмотров 1 420 лайков

Machine-readable: Markdown · JSON API · Site index

Поделиться Telegram VK Бот
Транскрипт Скачать .md
Анализ с AI
Описание видео
In this Python Tkinter Tutorial, we'll explore the importance of using classes when working with Tkinter in Python. While many tutorials introduce classes later on, I believe it's essential to start with them right away to avoid common pitfalls and simplify your code. We'll walk through refactoring a basic Tkinter application to use classes, demonstrating how this approach leads to cleaner, more maintainable code. Whether you're building small apps or planning for larger projects, this video will show you how to organize your GUI applications effectively. The code from this video can be found at: https://gist.github.com/09ac42ad88a3ca963fb1452e679a9195 Python Classes/Object-Oriented Series: https://www.youtube.com/playlist?list=PL-osiE80TeTsqhIuOqKhwlXsIBIdSeYtc "If __name__ == __main__" Video: https://youtu.be/sugvnHA7ElY ✅ Support My Channel Through Patreon: https://www.patreon.com/coreyms ✅ Become a Channel Member: https://www.youtube.com/channel/UCCezIgC97PvUuR4_gbFUs5g/join ✅ One-Time Contribution Through PayPal: https://goo.gl/649HFY ✅ Cryptocurrency Donations: Bitcoin Wallet - 3MPH8oY2EAgbLVy7RBMinwcBntggi7qeG3 Ethereum Wallet - 0x151649418616068fB46C3598083817101d3bCD33 Litecoin Wallet - MPvEBY5fxGkmPQgocfJbxP6EmTo5UUXMot ✅ Corey's Public Amazon Wishlist http://a.co/inIyro1 ✅ Equipment I Use and Books I Recommend: https://www.amazon.com/shop/coreyschafer ▶️ You Can Find Me On: My Website - http://coreyms.com/ My Second Channel - https://www.youtube.com/c/coreymschafer Facebook - https://www.facebook.com/CoreyMSchafer Twitter - https://twitter.com/CoreyMSchafer Instagram - https://www.instagram.com/coreymschafer/ #Python #TKinter

Оглавление (7 сегментов)

Segment 1 (00:00 - 05:00)

hey there how's it going everybody in this video we're going to be learning how to use tenter with classes now if you're following along with some other tutorials from other sources this may be one of those things that they don't cover until later on but in my opinion this is something that you should be doing almost immediately with tkinter uh because it's going to save us a lot of headaches down the road uh even when building small simple applications I think it's important to get into the habit of using classes and we're going to see why in this video it's not only going to help us with easier layouts and organization but it's also going to prevent a lot of headaches and ugly workarounds that you'll find yourself using if you're not using classes now we're not going to go into anything super Advanced here but if you're completely new to classes and have never used them before then I would recommend watching my series on how classes work in Python and that should give you a better idea of the basics that we might not touch on in this video uh like I said we're not going to go uh super Advanced but I will be uh going into this tutorial with the assumption that you know how to create you know very simple applications uh how they're initialized and things like that uh but if you don't know those things then I'll leave a link for my series on object or and programming and python in the description section below uh but with that said let's go ahead and get started so I already have an example of a problem that we can run into when not using classes pulled up here in my editor right now we have a very simple application that we've created with tkinter and this is the same code and application that we ended up with in our introduction video now if you miss that video then I'll have a link to this code in the description as well uh but basically what we have here is an application where we just have two frames so right here we have one frame and our second frame Now by using those frames it allowed us to uh organize our code a little bit better but there should be a problem here that's kind of popping out to you uh that is pretty common and it can make code unmanageable pretty quickly so when we created our second frame we reused a lot of the same code from above so you can see that this Frame two here basically we just added a two added this to the second column and all of that code is basically the same as all of this code but we're just creating a second frame and only changed a few things so anytime that you're repeating yourself in programming it's usually a sign that there's a better way to do things but the repetition isn't the only issue that we have here so let me run this and we'll see what I mean so we can see here that we have a simple application that has two input forms let me make this a little bit larger here and these are side by side now on the left we're using themed tenter frame and on the right we have a regular teiner frame and using frames allowed us to simplify our layout and easily add this second input form on the right side by just copying and pased the code that we had from the first frame but there is an issue with the code that wasn't addressed and some of you noticed this and left a comment about it now I actually introduced this bug on purpose into our application because I wanted to show how if we used classes then we would never run into this problem to begin with so what's the bug that I'm talking about out so let me go ahead and try to add in some information here if I try to insert anything into this left form it doesn't work if I do it in the right form then the button seems to work and my enter input seems to work but it seems that uh this left form is broken so let me close this down and go back to the code to see why this is so if we look below our first frame here then we can see that we have this entry uh binded to our return uh key here and it's B bound to this add to list function so now if I look at my second frame here then we can see that we created a another widget called entry and that we've added that to our second frame and we're also binding that to an event so that when we hit the return key it runs that add to list function as well now you might be thinking that the bug simply comes from using the same variable name uh for both of these widgets and that's part of it but changing that alone won't solve our problem uh let's go up to the add to list function and see what this looks like so here we have our add to list function so there's another issue within this function that should kind of jump out uh to us here as being what's known as a code smell and that's that we're accessing variables from our Global main name space within this

Segment 2 (05:00 - 10:00)

function so for example we are accessing the uh entry and text list objects here and it's not immediately clear just from looking at this function uh where these are actually coming from so that's one issue and we can see that within this function we're specifically getting the text from that entry widget and then adding that text to our text list so the fact that we did use the same variable name for that widget is part of the problem uh because now this entry widget is specifically referencing the entry widget on that second frame and not the first frame since we recreated that but that's not the entire problem uh even if we were to rename this uh to you know entry two so if I were to do that let me go ahead and grab all of these rename this to entry two let me take that out there then now we just have that same problem in Reverse now that add to list function is going to be referencing that entry widget from our first frame but not our second frame so what we need here is for our function to reference the entry widget and the text list of the specific frame that's calling that function now there are ways that we could get this to work without classes but those would take some workarounds with using lambdas or wrapping functions within functions and stuff like that can get kind of confusing pretty quickly especially to maintain uh not to mention that we already have repetition and Global variable access in our code that I mentioned before so with all that added up the better way to do this would just to be to use classes so let's convert what we have here now to classes and we'll see how much this cleans everything up so first let's go ahead and do the bare minimum and simply create a class that creates the most basic application there are a few different ways that we can do this but I'm going to comment out almost everything that we have so far so all the way up to here I'm going to comment out and now the way that we can create an application uh that's basically empty is from these lines here so first let me show this to you without uh doing this with a class so I'll say root equals tk. TK give it a title and then we want to do a root. main Loop and run that Loop so if we remember that's a very simple empty uh window so if I run that we can see we get an empty window and now let's create the simplest version of uh a TK application using classes so I'm going to show a few different ways that we can use classes and then I'll tell you my preferred way uh one way that we can create a class is to create one that does not use inheritance so that would look something like this I can just say class application and then within here we need our anip function so I will go ahead and create that oops and I thought I had my tab completion off here um I don't want it adding in a bunch of stuff that's going to confuse us here I just want to focus on what we currently have so now within our init function we need to initialize this so I can just say self. root is equal to the same thing that we did here and actually I can just take everything that we have here put it inside of our anip function and all I would need to do is put self in front of this and that should be enough to give us our very basic app uh using classes so if I say app is equal to application and create an instance of that if I run this then we can see that we still get that uh blank window there but instead of creating it outside of the class we just created everything here inside the class and then created an instance of that class so the approach that we're using here creates this tenter instance within our application class itself and we can see that worked but if we wanted we could also create the TK instance and run the main Loop outside of the class uh so to do this we need to pass in the instance of TK as an argument to our class and we can do that by adding it as an argument here and since we want to create the TK instance and Main Loop outside of the class all we need to do within the class is change things like the title so I'll just set

Segment 3 (10:00 - 15:00)

self. root is equal to that instance that we're going to pass in there and then we can just set the title and now outside of here I can say root is equal to tk. TK and then we can pass this in to our application class and it passes that into the anip method there uh sets that sets the title and then with uh from uh outside the class we can run our main Loop like that now if I save that and run it then we can see that works as well so now I'll close that down so with those first two ways of creating classes like we did uh one where we created the instance inside and it outside we can sort of think about this in the sense of saying that our application class here has a tkinter instance it's not a tkinter instance itself if we wanted to make it a teiner instance itself then we can inherit directly from tk. TK and this is my preferred method because I feel like it makes more sense and helps organize the code better so to inherit from TK we can simply add that to our class definition so I'm going to take this here uh without the um parentheses there because we don't want to create an instance and I think I messed that up while I was uh moving things around this needs to be tk. TK and now we no longer need to accept this instance as an argument so I will remove that and another thing that we need to do I'll go ahead and remove this as well now another thing that we need to do here is to run its Constructor and we can say super Dot and run that supers AIT function and that's basically the same as what we did down here with running tk. TK so it's just initializing that parent class and then allowing us to make our own customizations after that so now instead of us thinking about this as our application having a teiner instance now we can think of this in the sense that our application is a teiner instance so instead of self. root. tile we can simply say self. tile because this is the instance of TK you can see that I also removed that uh self. root equals root uh because that's no longer needed and now since our application uh actually will give us an instance of TK instead of doing this down here we're no longer going to do anything with these root variables here um all I have to is say app is equal to application and then I can say app. main Loop to run that main Loop oops and I almost made a mistake here uh when we run this super. anit I don't know why I put that there at the top uh this needs to be within our anip function because we want this Constructor initialized uh whenever we create our application and all that's going to get done within there so now I think this is much cleaner and easier to understand and a lot easier to maintain than what we saw before uh so if I go ahead and run this then we can see that we're still just getting our blank application for now one more thing about organization uh this is more of a personal preference than anything but when I'm looking at code like this it logically makes more sense to me when the code that's first being run is at the top of the script uh so for example when we run this script the first thing that we're doing is creating this application and then running that main Loop but that's currently at the bottom of the script uh and we can't put it above our class uh because we have to Define our class before we use it but there's a super easy work around to this all we have to do is create a function at the top of the script and add the code there now this is typically called something like a main function but you can call it whatever you want so I'm going to go ahead and do that real quick and show you what I mean so here at the top I'm just going to create a new function called Main and then I'm going to put everything within here so app. application app. main Loop and then below here where we were running that code I'm just going to run that main function and that's just going to do the exact same thing that we saw before but now we have the code that creates the application at the top of the script and while we're doing this we may as well follow some best practices here and add

Segment 4 (15:00 - 20:00)

a conditional that you all may have seen before um it's just this uh if double uncore name is equal to and then within quotes here double underscore Main and if it meets that conditional then I will run that main function uh basically all this means is that we're not running this main method unless we're running this script directly and not via uh an import or something like that it doesn't have anything to do with t Hiner it's just a common practice that you'll see in Python from time to time and I do have a separate video uh explaining this conditional and I'll leave a link to that video in the description section below if that's something that you're curious about okay so now that we've got this simple application created uh using a class now let's take what we had before and add everything back in and we can do this just by adding them to our application class so I'm going to copy a lot of what we had before but use our class instance instead so let me grab a few things here so first of all I'm going to get rid of a couple of things that we no longer need so where we were using uh creating that instance we no longer need so let me go ahead and grabbed this section here where we're setting up our root and configuring some of that stuff so I will paste this in here get that correct so this is where we were configuring The Columns and the rows instead of root here this is just going to be self and I believe that's all of the customization that we had with our root application um now let's look at our frames so remember we were creating two frames here that we basically identical and we're repeating code uh which should be a hint that we could probably do this in a better way if we look at what we're doing here we're creating a frame and we're adding that to our root application sorry this is a little dark because it's uh commented out and this frame has an entry field that's added to the frame it has a button it has a text list and also our add to list function is the Handler for some of the events on those widgets so if we think about this our frame basically has variables and functions of its own which is a good sign that it could be a candidate for a class at self allowing us to add those as properties and methods of that class instead so let's see what this looks like now so I'm going to create a new class under our application class and we'll just call this input form since that's basically what we're using those frames for so underneath here I'm going to create a new class and I'm going to call this input form and instead of inheriting from TK here instead I'm going to inherit from the themed tkinter because uh or the themed tkinter frame I mean uh since that's what we uh we're using for those frames so I will inherit from a frame here and now let's go ahead and create our init function I'll just copy that and paste it here now remember when we create a frame it actually takes in an argument it needs to know the parent container that this Frame will be added to uh so for the arguments that we're going to give in this initialization here we can add the argument of parent it needs to know that parent container and that's going to need to be passed into that frames Constructor so that it knows to add our frame uh to this application so just like we did before where we ran the Constructor of this TK instance here we're instead going to run the Constructor here of this super which is going to be a frame and the argument that it's going to take is in this parent argument uh so that it knows uh to pass that in whenever it creates the frame so just like that we now have a class for an empty frame so now let's add those properties and our method to this Frame class to customize this a bit uh so first let's copy the widgets that we added to our first frame so down here let me go ahead and I'm going to go ahead and grab everything that we did to this Frame after we created it uh except for uh the grid layout there I'll save that for later so I'm going to grab where we configured The Columns and rows all the way down here until we get to our second frame of creating this text list so I'm going to cut all of that out and I'm going to put this here within our nit function let me get the indentations and everything correctly and uncomment this and now I need to make a few changes here so you can see uh that it's giv me some

Segment 5 (20:00 - 25:00)

underlines for some warnings um or some errors in this case so I need to change frame to be self so we have self. column configure self. row configure when we create entry we need to uh pass in self which is this Frame as that entry's parent container we haven't added our add to list function yet so I'm not going to worry about that uh and lastly let's go ahead and change that last frame there as the parent for this button and this list box now I want these to be uh available within this Frame itself so here at the beginning I'm going to put self dot in front of all of these here and let me save that now just to clarify the reason I added self here before all of these is because I want to have access uh to these variables Within in the entirety of our uh frame here oh and uh you guys probably noticed this uh way earlier than I did but this isn't even in my frame I don't know what I'm doing here um let me put this where it's supposed to be in the frame sorry about that would have been a big mistake okay so again to clarify um the reason I added self. entry and self doot entry button and things like that is because if we uh hadn't done that and we had uh just put entry then what that does is it just creates a local variable within our anit function and it wouldn't be accessible within other methods and things like that within our class so we want this to be accessible so it's self. entry instead of just entry there basically anything that you want to be able to access outside of just your init function uh will need to be uh prefixed with that okay so now let's add our add to list function to this Frame class as well now if this was a function that was acting on your entire application then you could add it to that application class but this functionality is specific to our frame so we'll go ahead and add it there so let me go ahead and uncomment this and cut it out there and within our input form class here I'll add this in get that indentation correct now since this is going to be a method of our class it needs to take self as the first argument and now since this is a method we're also going to uh need to say self. add to list whenever we are trying to call this so anywhere that we're using add to list I'm going to say self dot add to list there so that it calls that method okay so now within this function itself uh now that we have this inside of a class instead in of using these widgets that used to be in the global Nam space we can instead simply uh reference these widgets from the instance of the class itself so to do that just like we saw before I can put self dot entry and self. text list and self. entry there and that's going to reference that specific instance of this class and not just something on the global name namespace and just by doing that that's actually what's going to solve our issue that we saw earlier so now to create some frames using this class uh so that we can see that issue was solved we can simply do that within our application class so I'm going to copy that code that we used to create those frames before and see how this is now different so here where we were creating these frames before I'm just going to cut this out and up here within our application class let me go ahead and paste this in and make sure that's indented correctly there now here instead of creating a themed tkinter frame and passing root in as the parent now within our application all we're going to do is create this input form which is also a frame and now we just need to pass in self as our parent container uh because this application is self so it's basically the same as passing in Roots there and this grid layout is just going to remain the same we're still just adding this to that First Column so that looks fine um now if I wanted to add a second class here then I could do that as well and all I would need to do here is say you know frame two is equal to input form uh frame two. grid and we could put that in the second column there so now everything that we had before where we were creating these frames so

Segment 6 (25:00 - 30:00)

we created our first frame and then we added in an entry a button uh the function and the uh list box and then we did that same thing for the second frame as well and we had that entry for the second frame entry or the uh button for the second frame and so on now all of that is contained within this input form class so we don't need to do anything else other than create that input form class like we did here and initialize that that's already going to contain all of those widgets and actions uh for that frame so now since these are their own instance we're no longer going to have that problem where our vent handlers were referencing the wrong widgets since each of these frames now references its own widgets using the self keyword we're working with instance variables of those specific frames instead of relying on global variables and this should basically give us what we were originally after in that first video so I'm going to go ahead and delete any of the commented stuff that's left because we no longer need this repeated code that we were using before down here and now this is much more contained and is much more functional as well so now let me go ahead and run this and make sure that this works and that I didn't mistype anything here uh so we can see that we have our two input forms here and I'll just say this and hit enter and that inner uh binding Works our button Works in this first frame and if I hit enter there that works if I use the button then that works as well so our first frame is working our second frame is working uh and it's both laid out here in our application so that seems to be good okay so that works but I have a feeling that some of you are going to look at this with these classes here and you're going to be thinking okay now that was a lot to do in order to get our simple application working is it really worth it you know couldn't we have just used one of those workarounds and I get that it seems like if you just want a super simple application that classes might be Overkill and you just want to keep everything in the global name space but remember we were changing uh what we had already written in the last video uh if we'd written this from scratch it really wouldn't have taken that long to just do it this way from the beginning but also even with simple applications there's almost always something that comes up that we're going to want to add later uh that we'd overlooked when actually planning even a simple application so doing it this way from the beginning just makes it so much easier than doing it uh the way that we saw in the last video so for example let's say that we have our two frames uh our two forms here our two input forms but when we replace in this application uh we overlooked that we also wanted to have a way to clear those list boxes of everything that's been added so the way that we did it before we would need to add a new button widget to both of those first and second frames separately uh we would have to make sure that the event handler handlers reference the correct list boxes for each one of those frames and that leads us right back to the situation that we had before and just leaves a lot of room for mistakes but the way that we've done this here it just makes it super easy so if I wanted to put a clear button here all I would have to do is go down to our frame class and let's just add this second button right underneath our add button so I will copy that and I'll paste that here and we'll just call this entry button two and also we want the text for this to be clear so I'll say clear there uh for the command let's call this clear list and that is a method that we'll write here in just a second and we also want this to be in the uh third column so column two there uh and that looks good for adding another button and since we now have three things in our first row uh for this text list let's also column expand this to three instead of two and now all we have to do is create this uh clear list event handler here um so let me just copy this method declaration here and I will call this a clear list instead of add to list and I don't have uh a keyboard binding on here so I don't need to accept uh that event and now all I'm going to do is delete everything in this text list here so I can just say self. text list. delete and it's going to be very similar to what we did to our entry here after we add it to the list and we remove everything from the that entry uh we're also going to do this with the

Segment 7 (30:00 - 34:00)

text list and just delete everything from the beginning which is this zero all the way to the end which is this constant within the TK class so all we did was change our input form here we made no changes at all to our application class or anything like that so now if I save this and run it then we can see that both of these now have a clear button so if I add things back in again everything's still working and if I hit clear then it only clears that left frame if I hit clear here then it only clears that uh right list box so now I can close that down so it just makes it a lot easier uh whenever we are wanting to add something to our application to know exactly where to add it so there I wanted to change something in the input form uh by adding a button so I just went in and all I had to do is change that input form and change no more of the application I didn't have to touch anything outside of these classes here and now that we have everything written down here um let's actually walk through this code just really fast and just kind of take a look at how it kind of logically flows a little bit better now too so when we first run these scripts it's going to run this main function here if we go up we can see we're creating our application and running that main Loop so then we can just look here that we have our application we can see that we're setting the title everything with our main application is here so everything from our main window configurations to what's added to that main window we can see that we're adding to input forms here so we can go see what's in there and we can see that this has its own customizations and buttons and event handlers and things like that as well so I think the layout is just much better uh now a little side note here let me uh you can see that this has been underlined the whole time here this is just a pilent warning uh for our event handler and that's because pilent gives warnings when you have an unused argument uh so we need to accept this event object that t Kenner is sending to this function but we're not doing anything with it in this example and if you declare a variable that's not being used uh the common convention is to put an underscore in front of that variable so instead if I just called this uh underscore event instead then that will likely get rid of that pilent warning and let it know that we know that we're not using that argument and that it's by Design that's just a side note that uh pilent warning was bugging me uh but it doesn't actually have uh any effect on our application okay so we're just about wrapped up with the video but I did want to mention another positive to breaking up our code into classes like we did in this video now this might not be something that we think about until the application grows in size but once we have a lot of different frames or Pages for our guey application then these classes allow us to easily break these into separate files so for example if our input form that we created here is something that we use in multiple windows we could actually split that out into a utilities. py file or something like that and then within our main application uh we could just say from utilities import uh input form and that would clean up a lot of our code and our main files if we used Imports instead and these classes allow us to do that easily but with that said I think that's going to do it for this video uh hopefully you feel like you got a good understanding of why you'd want to use classes when working with t ker and a lot of the benefits it provides in the next video we're going to be learning how to use variables and effect i l link values to widgets which is another very important aspect to these gooey applications but if anyone has any questions about what we covered in this video then feel free to ask in the comment section below and I'll do my best to answer those and if you enjoy these tutorials and would like to support them then there are several ways you can do that the easiest way is to Simply like the video and give it a thumbs up also it's a huge help to share these videos with anyone who you think would find them useful and if you have the means you can contribute through patreon or YouTube and there are links to those pages in the description section below be sure to subscribe for future videos and thank you all for watching

Другие видео автора — Corey Schafer

Ctrl+V

Экстракт Знаний в Telegram

Экстракты и дистилляты из лучших YouTube-каналов — сразу после публикации.

Подписаться

Дайджест Экстрактов

Лучшие методички за неделю — каждый понедельник