WEBVTT 1 00:00:00.180 --> 00:00:02.020 Hi, guys, and welcome back. 2 00:00:02.020 --> 00:00:05.330 In this video we're going to start checking for changes. 3 00:00:05.330 --> 00:00:07.320 What that means is when we know 4 00:00:07.320 --> 00:00:09.400 whether a file has been changed 5 00:00:09.400 --> 00:00:11.680 we will be able to then tell the user that 6 00:00:11.680 --> 00:00:15.010 they're about to quit without saving their changes. 7 00:00:15.010 --> 00:00:16.780 So, it's important that our text editor 8 00:00:16.780 --> 00:00:18.780 has this functionality just in case 9 00:00:18.780 --> 00:00:21.130 they close it by accident. 10 00:00:21.130 --> 00:00:25.120 How we're going to do is we're going to keep track 11 00:00:25.120 --> 00:00:28.280 of the contents of each text field. 12 00:00:28.280 --> 00:00:30.640 And when the contents change, 13 00:00:30.640 --> 00:00:34.993 then we will mark that specific text field as modified. 14 00:00:36.310 --> 00:00:38.860 Okay, so how we're gonna do that specifically 15 00:00:38.860 --> 00:00:42.530 is we're going to define a variable called text contents 16 00:00:42.530 --> 00:00:44.540 and it's going to be a dictionary, 17 00:00:44.540 --> 00:00:49.040 and then what we're going to do is when we create a file 18 00:00:49.040 --> 00:00:54.040 we're gonna say text contents of the text area 19 00:00:54.500 --> 00:00:57.100 that we've just created is going to be 20 00:00:57.100 --> 00:01:01.730 equal to a hash of empty string, 21 00:01:01.730 --> 00:01:04.330 so this may look like a little bit of magic, 22 00:01:04.330 --> 00:01:06.070 but here's what's happening. 23 00:01:06.070 --> 00:01:07.510 We're going to have an empty 24 00:01:07.510 --> 00:01:09.840 dictionary called text contents. 25 00:01:09.840 --> 00:01:11.330 When we create a new file 26 00:01:11.330 --> 00:01:13.830 we're going to start populating this dictionary 27 00:01:13.830 --> 00:01:17.560 and we will say that an entry for this specific 28 00:01:17.560 --> 00:01:21.540 text area has a content of empty string. 29 00:01:21.540 --> 00:01:22.600 Now, we're not going to put 30 00:01:22.600 --> 00:01:24.600 the actual content in the dictionary. 31 00:01:24.600 --> 00:01:27.130 We're going to put a hash of the content. 32 00:01:27.130 --> 00:01:30.830 Hashing is essentially turning a piece of data 33 00:01:30.830 --> 00:01:35.720 of arbitrary length into a piece of data of specific length. 34 00:01:35.720 --> 00:01:39.300 So, no matter how long the contents of a file are, 35 00:01:39.300 --> 00:01:43.600 when we hash them they will become a certain length. 36 00:01:43.600 --> 00:01:45.770 That's so that if we have very long files 37 00:01:45.770 --> 00:01:47.480 we don't have them all inside a dictionary. 38 00:01:47.480 --> 00:01:50.270 We only have a hash of the contents. 39 00:01:50.270 --> 00:01:53.810 If the file contents change, the hash will change, 40 00:01:53.810 --> 00:01:56.320 so this is just as good for keeping track 41 00:01:56.320 --> 00:01:58.920 of whether something's changed or not. 42 00:01:58.920 --> 00:02:02.260 So, whenever we create an empty file or a new file, 43 00:02:02.260 --> 00:02:06.283 that is going to have a hash of empty string. 44 00:02:07.300 --> 00:02:10.650 Now, instead of empty string, which is the content, 45 00:02:10.650 --> 00:02:12.620 we can just put content in here 46 00:02:12.620 --> 00:02:15.243 in case we're using this for opening a file. 47 00:02:16.360 --> 00:02:20.350 Next up, we do want to make sure that when 48 00:02:20.350 --> 00:02:24.450 we save a file this text contents gets updated. 49 00:02:24.450 --> 00:02:27.280 So, in save file down here we will say 50 00:02:27.280 --> 00:02:31.060 text contents of the current widget 51 00:02:33.020 --> 00:02:37.030 is equal to a hash of the current content. 52 00:02:37.030 --> 00:02:39.560 Now, str of text widget actually 53 00:02:39.560 --> 00:02:41.610 gives you the name of the widget, 54 00:02:41.610 --> 00:02:43.720 and you can have a look at it when 55 00:02:43.720 --> 00:02:45.900 you, for example, create a file. 56 00:02:45.900 --> 00:02:48.300 I'll just print str of text area, 57 00:02:48.300 --> 00:02:50.720 and you'll see what it does. 58 00:02:50.720 --> 00:02:55.340 It prints out a tcl name of .!frame.!notebook.!text. 59 00:02:55.340 --> 00:02:57.380 This is just their specific notation. 60 00:02:57.380 --> 00:02:59.130 Every time you create a new one 61 00:02:59.130 --> 00:03:01.430 it will have a different name down there, 62 00:03:01.430 --> 00:03:04.160 and you can see that it's text2 when I've created a new one. 63 00:03:04.160 --> 00:03:06.160 I know that you couldn't see me create a new one, 64 00:03:06.160 --> 00:03:07.423 but nonetheless, I did. 65 00:03:09.370 --> 00:03:12.310 All right, next up what we want to do is go ahead 66 00:03:12.310 --> 00:03:15.270 and implement something that will check for changes. 67 00:03:15.270 --> 00:03:18.920 So, we're going to create a new function down here, 68 00:03:18.920 --> 00:03:21.533 and I'm gonna call it check_for_changes, 69 00:03:23.200 --> 00:03:26.430 and all this function is going to do is 70 00:03:26.430 --> 00:03:31.430 it is going to get the currently selected text widget. 71 00:03:31.610 --> 00:03:34.940 So, it will do current equal, 72 00:03:34.940 --> 00:03:36.010 and you can remember that we've got 73 00:03:36.010 --> 00:03:39.000 the currently selected text widget here 74 00:03:40.270 --> 00:03:41.953 when we did root.nametowidget. 75 00:03:43.080 --> 00:03:45.040 So, what I'm also gonna do is I'm gonna 76 00:03:45.040 --> 00:03:46.710 extract that into a function, 77 00:03:46.710 --> 00:03:48.687 so we'll do get_text_widget, 78 00:03:50.800 --> 00:03:52.670 and it will just return this. 79 00:03:52.670 --> 00:03:56.033 So, it'll do that and then return text widget. 80 00:03:57.960 --> 00:03:59.853 Then in here we can use that instead. 81 00:04:02.030 --> 00:04:04.860 And here we can also use it. 82 00:04:04.860 --> 00:04:06.690 All right, so back to our original function. 83 00:04:06.690 --> 00:04:09.763 Check for changes gets the currently open text widget. 84 00:04:10.650 --> 00:04:13.370 Why does it get the currently open text widget? 85 00:04:13.370 --> 00:04:15.940 Well, because whenever you're typing 86 00:04:15.940 --> 00:04:18.070 we're going to be checking for changes, 87 00:04:18.070 --> 00:04:21.540 and you can only be typing in the currently selected widget, 88 00:04:21.540 --> 00:04:23.340 so it makes sense to only check 89 00:04:23.340 --> 00:04:25.040 for changes in the current widget, 90 00:04:25.040 --> 00:04:28.293 as you cannot possibly have made changes to another widget. 91 00:04:29.740 --> 00:04:34.040 Then we'll get the content, which is current.get 92 00:04:34.040 --> 00:04:38.370 from 1.0 to end minus one c, 93 00:04:38.370 --> 00:04:41.130 and then we will get the name of the current tab, 94 00:04:41.130 --> 00:04:45.650 which is notebook.tab, current, text. 95 00:04:45.650 --> 00:04:48.500 Next up, if the hash of the content is different 96 00:04:48.500 --> 00:04:50.990 from what we've got already in our dictionary, 97 00:04:50.990 --> 00:04:52.870 that means that it has changed. 98 00:04:52.870 --> 00:04:55.810 So, we will say if the hash of content 99 00:04:55.810 --> 00:05:00.810 is different from text contents, str current, 100 00:05:02.650 --> 00:05:06.180 then we want to append a asterisk 101 00:05:06.180 --> 00:05:08.060 to the end of the file name just to tell 102 00:05:08.060 --> 00:05:10.010 the user that this is currently unsaved, 103 00:05:10.010 --> 00:05:15.010 so we'll say if the name minus 1 is not equal to asterisk, 104 00:05:15.480 --> 00:05:19.133 we will say notebook tab, current, 105 00:05:20.200 --> 00:05:24.720 text equal name plus asterisk. 106 00:05:24.720 --> 00:05:26.480 All right, so what this is doing is 107 00:05:26.480 --> 00:05:29.180 if the file is currently unsaved 108 00:05:29.180 --> 00:05:31.280 and if the name doesn't currently 109 00:05:31.280 --> 00:05:33.570 have an asterisk at the end, 110 00:05:33.570 --> 00:05:35.060 then we're gonna put an asterisk at the end, 111 00:05:35.060 --> 00:05:36.720 and that is quite normal is text editors 112 00:05:36.720 --> 00:05:40.203 and this signifies that the file is unsaved. 113 00:05:42.290 --> 00:05:47.290 Finally, if the name minus one is equal to asterisk, 114 00:05:48.680 --> 00:05:52.070 then we're going to remove it. 115 00:05:52.070 --> 00:05:56.040 That is because if this did not happen, 116 00:05:56.040 --> 00:05:59.220 that means that the file is not modified. 117 00:05:59.220 --> 00:06:02.260 So, what we want to do if the file has not been modified, 118 00:06:02.260 --> 00:06:04.890 but the last character is the asterisk, 119 00:06:04.890 --> 00:06:06.740 we want to remove that last character 120 00:06:06.740 --> 00:06:08.670 because it in no longer necessary. 121 00:06:08.670 --> 00:06:10.160 The last character being an asterisk 122 00:06:10.160 --> 00:06:12.090 means the file is unsaved, 123 00:06:12.090 --> 00:06:13.670 but because this did not run 124 00:06:13.670 --> 00:06:16.500 it means the file is not unsaved. 125 00:06:16.500 --> 00:06:20.120 So, we'll say notebook.tab, current, 126 00:06:20.120 --> 00:06:23.943 text equal name, except the last character. 127 00:06:26.420 --> 00:06:28.870 All right, so I know this is a little bit confusing. 128 00:06:28.870 --> 00:06:30.480 This is probably the most confusing code 129 00:06:30.480 --> 00:06:32.540 that we will write in this section, 130 00:06:32.540 --> 00:06:35.140 but it is to do with adding or removing 131 00:06:35.140 --> 00:06:38.360 the asterisk from the file name 132 00:06:38.360 --> 00:06:41.190 depending on whether it is unsaved or not. 133 00:06:41.190 --> 00:06:42.570 The last thing we have to do is 134 00:06:42.570 --> 00:06:44.920 to make sure that we call this function, 135 00:06:44.920 --> 00:06:49.660 so we will go down here to our root binds, 136 00:06:49.660 --> 00:06:51.833 and we're gonna do root.bind, 137 00:06:52.830 --> 00:06:57.830 and now it is KeyPress, with the K and the P uppercase, 138 00:06:58.330 --> 00:07:03.170 and the event is check_for_changes, like that. 139 00:07:03.170 --> 00:07:05.850 Let's run it, and I'll bring it down here 140 00:07:05.850 --> 00:07:07.217 and you can see that as soon as 141 00:07:07.217 --> 00:07:10.050 I start typing the asterisk appears, 142 00:07:10.050 --> 00:07:13.640 and if I remove the H the asterisk disappears 143 00:07:13.640 --> 00:07:16.960 because it realises that the contents right now 144 00:07:16.960 --> 00:07:18.250 are the same as the contents 145 00:07:18.250 --> 00:07:19.710 that it's got in the dictionary. 146 00:07:19.710 --> 00:07:22.763 Now, if I type in "H" and then I save it, 147 00:07:25.760 --> 00:07:30.760 I'll do test2.txt, then notice that the asterisk disappears, 148 00:07:31.080 --> 00:07:34.660 but if I type an "e" now, the asterisk comes back, 149 00:07:34.660 --> 00:07:36.930 and if I remove the e the asterisk disappears 150 00:07:36.930 --> 00:07:39.230 because the contents currently 151 00:07:39.230 --> 00:07:42.510 in the dictionary is when it was last saved, 152 00:07:42.510 --> 00:07:46.190 as we did here when we did the save file 153 00:07:46.190 --> 00:07:48.630 we replaced the text contents of our widget 154 00:07:48.630 --> 00:07:51.180 with the new contents that we've just saved. 155 00:07:51.180 --> 00:07:53.960 So, this is how you can signal a user whether 156 00:07:53.960 --> 00:07:57.710 some of their files are saved or pending saving 157 00:07:57.710 --> 00:08:00.220 so that they don't close this by accident 158 00:08:00.220 --> 00:08:02.440 without saving all their files. 159 00:08:02.440 --> 00:08:04.810 However, even if you have an unsaved file 160 00:08:04.810 --> 00:08:07.320 you can still close this and it will go away 161 00:08:07.320 --> 00:08:09.780 without telling you that you have unsaved changes. 162 00:08:09.780 --> 00:08:12.460 So, that's what we're gonna do in the next video. 163 00:08:12.460 --> 00:08:13.410 I'll see you there.