WEBVTT 1 00:00:00.350 --> 00:00:01.880 Hi guys, welcome back. 2 00:00:01.880 --> 00:00:04.390 In this video, we're going to be taking the contents 3 00:00:04.390 --> 00:00:07.380 of one of our text areas and we're going to be saving them, 4 00:00:07.380 --> 00:00:10.410 and putting them into a file in our system. 5 00:00:10.410 --> 00:00:13.270 Let's start by creating a function that will do that. 6 00:00:13.270 --> 00:00:15.060 So I'm gonna call this save_file(), 7 00:00:16.190 --> 00:00:20.410 and what we have to do here is we have to ask the user 8 00:00:20.410 --> 00:00:23.360 where in the system they want to save the file. 9 00:00:23.360 --> 00:00:25.230 We're gonna do this first of all, 10 00:00:25.230 --> 00:00:28.440 so that we know that they actually want to save it, 11 00:00:28.440 --> 00:00:30.240 because they can always press cancel, 12 00:00:30.240 --> 00:00:32.370 and so we know where the file is gonna go. 13 00:00:32.370 --> 00:00:34.370 So we'll say file_path equal, 14 00:00:34.370 --> 00:00:39.300 file dialogue dot ask save as file name, all one word, 15 00:00:39.300 --> 00:00:42.330 which is not very nice and but nonetheless that's how it is, 16 00:00:42.330 --> 00:00:47.040 and make sure that from tkinter you import file dialogue. 17 00:00:47.040 --> 00:00:50.000 This file dialogue here is going to ask the user, 18 00:00:50.000 --> 00:00:52.900 using the system dialogues, 19 00:00:52.900 --> 00:00:56.080 where to save the thing that you wanna save. 20 00:00:56.080 --> 00:00:59.660 So let's go ahead and before continuing with this, 21 00:00:59.660 --> 00:01:01.062 just to show you what it looks like, 22 00:01:01.062 --> 00:01:05.270 add an element to the menu that will call this function. 23 00:01:05.270 --> 00:01:08.840 So down here in the file_menu dot add_command, 24 00:01:08.840 --> 00:01:10.330 we're going to copy that, 25 00:01:10.330 --> 00:01:12.657 but instead of "New" we're gonna call this "Save", 26 00:01:12.657 --> 00:01:14.910 and the command is gonna be save_file, 27 00:01:14.910 --> 00:01:17.313 and again, make sure to not call the function. 28 00:01:18.240 --> 00:01:20.140 Let me run this app here, 29 00:01:20.140 --> 00:01:24.563 and I'll type "Hello world" and I'm gonna go and save, 30 00:01:26.870 --> 00:01:30.550 and notice that we get a few things going on. 31 00:01:30.550 --> 00:01:33.540 First thing that we got was an error, 32 00:01:33.540 --> 00:01:35.330 or seemingly something that looks like an error, 33 00:01:35.330 --> 00:01:36.690 it's actually a warning, 34 00:01:36.690 --> 00:01:38.670 and what it says is that somewhere in your system, 35 00:01:38.670 --> 00:01:42.590 there is an ambiguous way of calling this dialogue. 36 00:01:42.590 --> 00:01:44.510 So you don't have to worry about that at all. 37 00:01:44.510 --> 00:01:46.420 That's just a problem in my system. 38 00:01:46.420 --> 00:01:48.410 You may not even encounter this issue, 39 00:01:48.410 --> 00:01:51.150 and the second thing is that you get this dialogue. 40 00:01:51.150 --> 00:01:53.200 This is a Mac dialogue, 41 00:01:53.200 --> 00:01:55.350 and in Windows and in Linux it'll look different, 42 00:01:55.350 --> 00:01:58.780 and here you can save as a specific name, 43 00:01:58.780 --> 00:02:01.030 and you can give it tags as it's just a Mac thing, 44 00:02:01.030 --> 00:02:02.980 and you can decide where you wanna save it. 45 00:02:02.980 --> 00:02:05.920 Now, I'm gonna press cancel and that's it. 46 00:02:05.920 --> 00:02:09.800 So this is what our save file function does for now, 47 00:02:09.800 --> 00:02:12.310 but note that it only asks the user 48 00:02:12.310 --> 00:02:15.000 for the file name and the location. 49 00:02:15.000 --> 00:02:17.110 It doesn't actually perform the saving, 50 00:02:17.110 --> 00:02:19.000 which we do have to do. 51 00:02:19.000 --> 00:02:23.902 So we're going to go ahead and import os at the top, 52 00:02:23.902 --> 00:02:27.270 and then we're going to try to get the file name 53 00:02:27.270 --> 00:02:28.340 from the file path. 54 00:02:28.340 --> 00:02:30.950 So we do os dot path dot base name, 55 00:02:30.950 --> 00:02:33.097 and we will pass in the file_path. 56 00:02:34.210 --> 00:02:36.881 Given a path such as for example, 57 00:02:36.881 --> 00:02:41.320 slash users slash jose slash file dot txt, 58 00:02:41.320 --> 00:02:45.000 os_path based name will give you file dot txt. 59 00:02:45.000 --> 00:02:47.040 If you're using a Windows style path, 60 00:02:47.040 --> 00:02:48.560 it'll also work with that. 61 00:02:48.560 --> 00:02:51.210 So this is a great way of ensuring 62 00:02:51.210 --> 00:02:53.280 Cross-Platform Compatibility. 63 00:02:53.280 --> 00:02:54.810 Next up, we're going to get 64 00:02:54.810 --> 00:02:58.140 the currently selected text widget. 65 00:02:58.140 --> 00:03:02.150 Remember that we can have multiple tabs in our system, 66 00:03:02.150 --> 00:03:04.200 so by selecting the current one, 67 00:03:04.200 --> 00:03:06.110 that is the one that we've just tried 68 00:03:06.110 --> 00:03:08.120 to save to our computer. 69 00:03:08.120 --> 00:03:10.690 So let's say that text_widget is equal to, 70 00:03:10.690 --> 00:03:15.690 root dot name to widget, notebook dot select. 71 00:03:15.730 --> 00:03:17.570 Now this does a few things. 72 00:03:17.570 --> 00:03:22.570 First of all, the select in the notebook is 73 00:03:23.240 --> 00:03:25.590 Something that returns the widget name 74 00:03:25.590 --> 00:03:27.733 of the currently selected pane. 75 00:03:28.670 --> 00:03:30.600 Now, you can pass in a tab ID, 76 00:03:30.600 --> 00:03:31.480 and then what it does is, 77 00:03:31.480 --> 00:03:34.470 it actually goes and selects a particular tab, 78 00:03:34.470 --> 00:03:36.270 but if you don't pass in a tab ID, 79 00:03:36.270 --> 00:03:37.400 which we'll not doing, 80 00:03:37.400 --> 00:03:39.763 then it's gonna give us the name of the widget. 81 00:03:40.660 --> 00:03:44.400 In tkinter, names can be used 82 00:03:44.400 --> 00:03:46.970 to uniquely identify particular widgets, 83 00:03:46.970 --> 00:03:51.040 such as text_areas, notebooks, frames and so on. 84 00:03:51.040 --> 00:03:55.200 So by getting the currently selected widget, 85 00:03:55.200 --> 00:03:57.180 we're then going to use the root, 86 00:03:57.180 --> 00:04:01.240 which is the main window to turn that name into a widget. 87 00:04:01.240 --> 00:04:04.270 This has to be called on the container of 88 00:04:04.270 --> 00:04:06.893 whatever widget name you are getting. 89 00:04:08.840 --> 00:04:13.030 Then we're going to perform some pretty ugly black magic. 90 00:04:13.030 --> 00:04:15.730 We're gonna say that the content of the text widget 91 00:04:15.730 --> 00:04:18.840 is text underscore widget dot get, 92 00:04:18.840 --> 00:04:20.480 and we're gonna go 93 00:04:20.480 --> 00:04:24.250 from the first line and the first character, 94 00:04:24.250 --> 00:04:27.400 so this is the first line starts at one, 95 00:04:27.400 --> 00:04:29.910 and the zeroth character, which is the first one. 96 00:04:29.910 --> 00:04:31.570 So just before the first character, 97 00:04:31.570 --> 00:04:33.830 we're gonna go until the end 98 00:04:35.190 --> 00:04:38.170 of the content in the widget; 99 00:04:38.170 --> 00:04:42.610 however, due to the way that text areas work in tkinter, 100 00:04:42.610 --> 00:04:45.130 if you do from 1.0 to end, 101 00:04:45.130 --> 00:04:45.963 then you're gonna end up with 102 00:04:45.963 --> 00:04:48.500 an extra new line character at the end. 103 00:04:48.500 --> 00:04:52.230 just because, that's always there in these widgets. 104 00:04:52.230 --> 00:04:54.660 There's always an empty new line character at the end. 105 00:04:54.660 --> 00:04:56.424 So what you wanna do, is you wanna go to the end, 106 00:04:56.424 --> 00:04:58.810 except for the very last character, 107 00:04:58.810 --> 00:05:01.290 which is always this new line character, 108 00:05:01.290 --> 00:05:03.320 that you didn't add yourself. 109 00:05:03.320 --> 00:05:07.150 So we do end minus 1c for end, 110 00:05:07.150 --> 00:05:08.993 except the last character. 111 00:05:10.890 --> 00:05:12.740 All right, so now that's out of the way, 112 00:05:12.740 --> 00:05:16.300 you can go ahead and actually open the file path, 113 00:05:16.300 --> 00:05:20.093 and we're going to open for writing, 114 00:05:21.260 --> 00:05:26.193 as file and then we will do file dot write content. 115 00:05:27.350 --> 00:05:30.950 Okay, next up, we're going to add an except, 116 00:05:30.950 --> 00:05:32.390 and we're gonna except for AttributeError, 117 00:05:32.390 --> 00:05:35.010 and FileNotFoundError, 118 00:05:35.010 --> 00:05:36.807 and we're gonna just print an error message, 119 00:05:36.807 --> 00:05:39.570 "Save operation cancelled", 120 00:05:39.570 --> 00:05:41.370 and then we're going to return. 121 00:05:41.370 --> 00:05:45.350 So if we close the window or we didn't get a file name, 122 00:05:45.350 --> 00:05:48.030 or the file was not found in the system, 123 00:05:48.030 --> 00:05:51.669 then we are going to print "Save operation cancelled", 124 00:05:51.669 --> 00:05:52.870 and we're gonna return; 125 00:05:52.870 --> 00:05:54.690 Otherwise, we're going to of course, 126 00:05:54.690 --> 00:05:57.470 save the file into our disc, 127 00:05:57.470 --> 00:06:01.083 and then we're also going to say, notebook dot tab, 128 00:06:02.140 --> 00:06:04.750 and we're gonna pass in a tab ID, 129 00:06:04.750 --> 00:06:07.480 but there is a special tab ID in notebook tabs, 130 00:06:07.480 --> 00:06:09.250 which is "current", 131 00:06:09.250 --> 00:06:12.340 and that selects the currently selected tab, 132 00:06:12.340 --> 00:06:15.850 and here we're gonna pass in a text of file name, 133 00:06:15.850 --> 00:06:18.350 and this file name here is what we extracted earlier, 134 00:06:18.350 --> 00:06:20.570 and what this is going to do as you might imagine, 135 00:06:20.570 --> 00:06:23.250 it is going to rename the current tab, 136 00:06:23.250 --> 00:06:28.083 so that the name of the tab will henceforth be filename. 137 00:06:29.380 --> 00:06:30.770 Let me run app dot pv, 138 00:06:30.770 --> 00:06:33.360 just to show you everything we've done in this video. 139 00:06:33.360 --> 00:06:36.970 And here we have our first empty file, 140 00:06:36.970 --> 00:06:39.500 that we created using the create file function. 141 00:06:39.500 --> 00:06:42.757 I'm gonna call it "Welcome to our text editor", 142 00:06:44.010 --> 00:06:46.400 Then I'm gonna go ahead and save this, 143 00:06:46.400 --> 00:06:48.730 and what's happening when we do this is, 144 00:06:48.730 --> 00:06:51.450 first of all, we ask where we wanna save. 145 00:06:51.450 --> 00:06:55.403 So I'm gonna save it as test dot txt in my desktop, 146 00:06:56.350 --> 00:06:59.550 and then a bunch of things happened all in one place. 147 00:06:59.550 --> 00:07:01.540 First, we got the file name, 148 00:07:01.540 --> 00:07:04.080 which is test dot txt from the path, 149 00:07:04.080 --> 00:07:06.720 that the dialogue gave us back. 150 00:07:06.720 --> 00:07:08.570 Then we got the current widget, 151 00:07:08.570 --> 00:07:10.030 which is the one that we have selected 152 00:07:10.030 --> 00:07:11.610 at the time of saving. 153 00:07:11.610 --> 00:07:13.110 We got all the content. 154 00:07:13.110 --> 00:07:16.270 We opened the file using just good old Python, 155 00:07:16.270 --> 00:07:19.410 and we wrote the contents into the disc, 156 00:07:19.410 --> 00:07:22.740 and then at the end, we renamed the current tab, 157 00:07:22.740 --> 00:07:25.410 so that it is now no longer called Untitled, 158 00:07:25.410 --> 00:07:27.293 It is called test dot txt. 159 00:07:28.340 --> 00:07:30.020 So, hopefully that makes sense. 160 00:07:30.020 --> 00:07:32.810 We have done quite a lot of work in this video, 161 00:07:32.810 --> 00:07:36.200 but most of it is just normal Python, 162 00:07:36.200 --> 00:07:39.330 and there is a few tkinter use cases, 163 00:07:39.330 --> 00:07:43.320 like asking for a file name, getting a particular widget, 164 00:07:43.320 --> 00:07:46.980 this piece of beautiful code that gets you the content, 165 00:07:46.980 --> 00:07:50.480 and then of course renaming a tab. 166 00:07:50.480 --> 00:07:52.340 So thank you for joining me in this video. 167 00:07:52.340 --> 00:07:53.580 I hope you've learned something. 168 00:07:53.580 --> 00:07:55.053 I'll see you in the next one.