WEBVTT

0
00:00.540 --> 00:01.560
In the last lesson,

1
00:01.590 --> 00:05.880
we completed the user interface for our Pomodoro timer,

2
00:06.450 --> 00:11.130
and we've managed to get all the components that we need onto our graphical user

3
00:11.130 --> 00:16.050
interface. Now, the next step is to actually give it some functionality.

4
00:16.770 --> 00:21.480
I want to be able to create some sort of countdown mechanism that just does

5
00:21.480 --> 00:24.000
something really simple. For example,

6
00:24.000 --> 00:28.830
if it was just able to count down from five, four, three, two,

7
00:29.220 --> 00:33.600
one going down by one each time, that would be great.

8
00:34.110 --> 00:36.420
That's what we're going to be working on in this lesson.

9
00:36.480 --> 00:40.530
And we're going to go and see the countdown mechanism section to do that.

10
00:41.490 --> 00:46.470
One of the ways that you might think about approaching this is to use our time

11
00:46.470 --> 00:47.303
module

12
00:47.340 --> 00:52.020
cause we've seen before that we can say time.sleep and we can tell it to

13
00:52.020 --> 00:53.550
sleep for a second.

14
00:54.000 --> 00:58.980
So then we could maybe set up a while loop and while something or other is true,

15
00:59.280 --> 01:04.260
go ahead and sleep for one second. And then afterwards just,

16
01:04.320 --> 01:04.590
you know,

17
01:04.590 --> 01:09.240
subtract one from some sort of counter. So that we could start count at five

18
01:09.240 --> 01:14.240
and then each time we subtract by one and then each time we just simply update

19
01:15.360 --> 01:16.890
our label here,

20
01:17.250 --> 01:21.750
which we created in the canvas to whatever value count might be.

21
01:22.560 --> 01:24.990
Now, that sounds great in principle.

22
01:25.200 --> 01:30.200
The only problem is that we're working within a graphical user interface

23
01:30.690 --> 01:31.523
program.

24
01:32.310 --> 01:36.750
The reason why that's relevant is because if we think about a command line

25
01:36.750 --> 01:38.640
program, say for example,

26
01:38.640 --> 01:43.020
if we were to get our console to do something, print

27
01:43.080 --> 01:43.980
hello, well,

28
01:43.980 --> 01:48.980
it's only going to do something when you actually give it an instruction and you

29
01:49.380 --> 01:50.213
hit enter.

30
01:50.760 --> 01:55.530
It doesn't really need to keep an eye out for what you might do in between.

31
01:56.130 --> 01:59.190
But a graphical user interface is a little bit different.

32
01:59.520 --> 02:03.690
It needs to keep watching the screen to see whether

33
02:03.690 --> 02:06.870
if a user clicks on a button, for example.

34
02:07.350 --> 02:12.350
So it's basically going to refresh and keep listening for events.

35
02:13.110 --> 02:14.880
So every fraction of a second,

36
02:14.880 --> 02:16.890
it's going to keep checking, did something happene, did

37
02:16.920 --> 02:20.760
something happen, did something happen. And the moment when it does,

38
02:20.850 --> 02:24.510
then it's got to react. It's got to react to that event.

39
02:25.050 --> 02:30.030
In this case, we tend to call these types of GUI programs event-driven.

40
02:30.690 --> 02:35.040
And the way that it's driven is through our main loop.

41
02:35.490 --> 02:39.450
So when we set up our window and we start off the main loop,

42
02:39.780 --> 02:44.780
it's basically looping through and every millisecond it's checking to see did

43
02:44.940 --> 02:47.730
something happen, did something happen, did something happen?

44
02:48.210 --> 02:51.090
So that means if we have another loop in our program,

45
02:51.120 --> 02:55.200
it actually won't be able to reach the main loop. And in this case,

46
02:55.230 --> 02:57.780
when you actually try to run it, nothing happens.

47
02:57.840 --> 02:59.830
Our program doesn't even launch.

48
03:00.340 --> 03:05.260
So we have to rethink this and we have to do it a little bit differently. In

49
03:05.260 --> 03:08.950
order to create interactive and interesting programs,

50
03:09.370 --> 03:12.640
you kind of need something to happen on screen, right?

51
03:12.640 --> 03:16.750
Every so often. You need this timing mechanism. Luckily,

52
03:16.780 --> 03:18.820
tkinter already thought of this.

53
03:19.150 --> 03:23.800
And we can in fact use one of the builtin methods to every widget.

54
03:24.220 --> 03:26.140
So if we tap into our window widget,

55
03:26.290 --> 03:31.290
we can get hold of a method called after and after is quite simple.

56
03:32.230 --> 03:36.760
It's a method that takes an amount of time that it should wait

57
03:37.270 --> 03:39.190
and then after that amount of time,

58
03:39.460 --> 03:44.460
it simply calls a particular function that you tell it to call passing in any

59
03:44.650 --> 03:48.760
arguments that you want to give it. Here's how it works.

60
03:48.820 --> 03:53.820
We call window.after, we first provide the amount of time to wait in

61
03:54.460 --> 03:58.720
milliseconds. So if we want one second, then that's 1000 milliseconds.

62
03:59.320 --> 04:02.200
Next we pass in a function to call.

63
04:02.530 --> 04:07.390
So let's create a function up here. Let's just call it, say something,

64
04:07.990 --> 04:10.330
and then we'll pass in the thing,

65
04:12.610 --> 04:16.420
like this. And then all we do is we just print that thing.

66
04:16.960 --> 04:18.760
So super simple function.

67
04:18.790 --> 04:23.790
And then we give the name of this function as the function to call after 1000

68
04:24.310 --> 04:25.143
milliseconds.

69
04:25.600 --> 04:29.590
Now the final thing in this list of arguments,

70
04:29.920 --> 04:34.090
if I just go ahead and cut that and show you again, when it gives me the prompt,

71
04:34.510 --> 04:39.010
the last thing is actually a *args.

72
04:39.490 --> 04:44.230
This, if you remember, allows us to put in an unlimited number 

73
04:44.290 --> 04:46.090
of positional arguments.

74
04:46.600 --> 04:50.890
What that means is we can give as many arguments as we want

75
04:51.220 --> 04:56.220
and those arguments, in this case, is simply going to be passed to the function

76
04:56.350 --> 04:59.920
that we want to call. So in this case, it's going to be that thing.

77
05:00.190 --> 05:03.580
So if I put hello here then I run my code,

78
05:03.940 --> 05:07.990
you can see that after 1000 milliseconds, basically one second,

79
05:08.440 --> 05:10.750
it calls this function,

80
05:10.810 --> 05:15.400
say_something, and it passes this hello as the input

81
05:15.580 --> 05:18.370
to that function. As I said,

82
05:18.400 --> 05:22.000
you can have an infinite amount of positional arguments.

83
05:22.330 --> 05:26.530
So let's put in some other arguments which we'll call a,

84
05:26.530 --> 05:27.363
b, and c,

85
05:27.610 --> 05:32.320
and then we'll print a, print b, and print c.

86
05:33.970 --> 05:36.640
And now instead of passing in hello,

87
05:36.670 --> 05:41.650
we're going to pass in lots of positional parameters. So we'll say 3, 5,

88
05:41.680 --> 05:44.500
and 8. Now, when I hit run,

89
05:44.560 --> 05:46.840
you will see it waits for one second

90
05:47.170 --> 05:51.850
and then it passes all three of these parameters to say something

91
05:52.270 --> 05:55.480
and it goes ahead and prints all of those out at once.

92
05:56.050 --> 06:01.010
So this is how the after method works. But what we wanted to do though,

93
06:01.370 --> 06:03.920
is we want it to repeat itself,

94
06:04.010 --> 06:09.010
to essentially loop. One way of getting that behavior is to simply put this

95
06:10.760 --> 06:11.240
method,

96
06:11.240 --> 06:16.240
call somewhere inside a function and then call itself.

97
06:16.370 --> 06:20.930
So here's what I mean. Let's create a function called a count_down,

98
06:21.740 --> 06:26.740
and this is going to take a input in the form of the number to count down by.

99
06:28.550 --> 06:32.270
And then inside this function, we call window.after.

100
06:32.720 --> 06:35.510
And we say that after 1000 milliseconds,

101
06:35.750 --> 06:40.750
call this function count_down and then pass in a count number.

102
06:44.570 --> 06:47.390
If that count number started out as 5,

103
06:47.630 --> 06:50.840
then we want to say count - 1.

104
06:51.320 --> 06:55.700
Now all we have to do is to call this countdown method.

105
06:56.030 --> 07:00.530
So let's call count_down and passing the starting count,

106
07:00.590 --> 07:04.430
let's say 5 seconds. So now when I run the code,

107
07:04.430 --> 07:07.760
it's going to call this method passing in 5 over here,

108
07:08.300 --> 07:11.720
and then it's going to wait for one second,

109
07:12.110 --> 07:17.060
and then it's going to call this function count_down passing in five minus one

110
07:17.240 --> 07:21.920
so it becomes four. And then afterward it repeats again, becomes three,

111
07:21.950 --> 07:26.180
two, one. So now if we catch that number

112
07:26.360 --> 07:30.500
which we can print, then we'll be able to see it count down

113
07:30.530 --> 07:34.190
when we run the code; five, four, three,

114
07:34.580 --> 07:38.390
two, one. One every second,

115
07:38.450 --> 07:42.770
and it basically keeps on going and it even continues to the negatives.

116
07:43.310 --> 07:46.310
So if we don't want it to go to negative time,

117
07:46.340 --> 07:49.220
then all we have to do is add an if statement.

118
07:49.640 --> 07:54.620
If count is greater than zero, then go ahead and execute this line of code.

119
07:55.880 --> 07:59.780
So now it'll go from five, four, three, two,

120
07:59.810 --> 08:02.570
one, zero, and then it will stop.

121
08:03.470 --> 08:08.450
This is the kind of behavior that we would need if we want to update our

122
08:08.450 --> 08:11.180
countdown in our Pomodoro timer.

123
08:11.900 --> 08:14.180
So how can we instead of printing

124
08:14.180 --> 08:19.180
the count actually change this text on our canvas? We'll,

125
08:19.820 --> 08:24.820
the way that we do that is by assigning this text a variable.

126
08:25.430 --> 08:28.460
So I'm going to call it timer_text

127
08:29.870 --> 08:34.870
and now that we've got timer_text being a set as the text that was created in

128
08:35.360 --> 08:38.870
the canvas, then we can access it right here.

129
08:39.620 --> 08:44.120
And the way that we'd change a piece of text or anything for that matter in a

130
08:44.120 --> 08:48.110
canvas is slightly different from how we would do for a label.

131
08:48.530 --> 08:52.070
If it was just the title label that we wanted to change, we would say title_

132
08:52.070 --> 08:56.310
label.config, and then let's change the text to something new.

133
08:56.940 --> 08:58.920
But to change a canvas element,

134
08:58.950 --> 09:02.640
you actually have to tap into the particular canvas you want to change and

135
09:03.240 --> 09:08.220
Then you call a method called itemconfig. And then in this method,

136
09:08.280 --> 09:12.240
you pass in the particular item that you actually want to configure,

137
09:12.540 --> 09:14.910
so in our case it's the timer_text,

138
09:15.570 --> 09:20.570
and then you pass in the thing about it that you actually want to change in

139
09:20.970 --> 09:24.300
terms of a kwarg, so this is a keyword argument.

140
09:24.870 --> 09:28.380
We're going to change the text to the current count.

141
09:28.650 --> 09:31.620
Now notice how this is not the string count,

142
09:31.830 --> 09:34.290
because then it would just show that word,

143
09:34.620 --> 09:39.270
but its actually the live countdown time. At this point in time

144
09:39.300 --> 09:40.500
if I run the code,

145
09:40.500 --> 09:45.500
I actually get a error and it tells us that the name canvas is not defined

146
09:46.320 --> 09:50.940
and that's because I'm calling this method countdown before I actually created

147
09:50.940 --> 09:51.690
the canvas.

148
09:51.690 --> 09:56.250
So if I move that to below this line and I run it again,

149
09:56.280 --> 09:58.020
then you'll see it actually work.

150
09:58.320 --> 10:02.370
And you see it starts out from five and it counts down to zero.

151
10:03.180 --> 10:08.180
Now how can we tie that behavior to the start button so that I can press the

152
10:08.250 --> 10:13.140
start button and then and only then does it start counting down from five,

153
10:13.140 --> 10:17.700
four, three, two, one? Well, let's go ahead and add 

154
10:17.760 --> 10:22.500
another function and I'm gonna add it in the timer mechanism section and I'm

155
10:22.500 --> 10:24.120
going to call it start_timer.

156
10:24.900 --> 10:27.750
Now this function is super simple.

157
10:27.930 --> 10:32.930
All it's going to do is it's going to be responsible for calling that function

158
10:33.030 --> 10:36.660
countdown and it's going to count down from five seconds.

159
10:36.810 --> 10:39.840
So I'll move that inside the start_timer.

160
10:40.500 --> 10:45.500
And now the start timer is going to be the function that needs to be triggered

161
10:47.070 --> 10:49.560
when the start button gets pressed.

162
10:49.920 --> 10:53.280
Do you remember how to tie a function to a button in

163
10:53.280 --> 10:58.260
tkinter? Pause the video and see if you can solve this challenge so that you'll

164
10:58.260 --> 11:03.260
be able to run the code, hit start and the timer to start counting down.

165
11:06.630 --> 11:11.630
So the keyword argument is command and all we have to do is to tie it to the

166
11:12.210 --> 11:15.990
start_timer function, but without the parentheses.

167
11:16.500 --> 11:19.920
So now when I hit run and I click start,

168
11:20.340 --> 11:24.840
it starts the timer setting that text from five, four, three,

169
11:24.930 --> 11:25.763
two, one.

170
11:26.220 --> 11:31.220
So the main loop is listening and when the user interacts with the start button

171
11:31.890 --> 11:36.660
it actually calls the start_timer function which calls the count_down function

172
11:36.960 --> 11:39.900
and get it to count down from five seconds.

173
11:40.560 --> 11:43.980
Now we don't actually want to count down from five seconds.

174
11:44.010 --> 11:48.750
We want to count down in minutes because we're probably not going to be working

175
11:48.750 --> 11:50.130
for five seconds at a time.

176
11:50.160 --> 11:54.040
We're going to be working for 25 minutes or having a minute break.

177
11:54.580 --> 11:59.580
So how can I change this countdown to interpret this instead of as five seconds

178
12:01.390 --> 12:04.990
to five minutes? Well, let's have a think about that.

179
12:05.680 --> 12:09.850
If we wanted to count down, let's say one minute,

180
12:10.240 --> 12:14.230
then that in terms of seconds would be 60 seconds.

181
12:14.860 --> 12:18.670
All we have to do is to take the number of minutes that we want to count down

182
12:18.670 --> 12:22.540
by and multiply it by 60. In this case,

183
12:22.540 --> 12:27.040
if we wanted to count down it by five minutes instead of five seconds,

184
12:27.400 --> 12:30.160
all we have to do is multiply by 60.

185
12:30.670 --> 12:33.880
So then when we call this function count_down,

186
12:34.150 --> 12:36.790
instead of getting five seconds to count down,

187
12:36.820 --> 12:39.190
we get 300 seconds to count from.

188
12:39.880 --> 12:44.470
But now if we run our code, you can see it's going to start from 300.

189
12:44.740 --> 12:49.240
It's going to go down all the way down to zero. Now in terms of time,

190
12:49.390 --> 12:54.040
that is five minutes, but this is not a very good way to visualize it.

191
12:54.370 --> 12:58.570
Nobody thinks in terms of 288 seconds remaining, right?

192
12:59.140 --> 13:04.140
So we have to format this count so that we can display it in the format of 

193
13:04.720 --> 13:09.010
00:00 like the usual kind of time, where for example,

194
13:09.010 --> 13:14.010
you have one minute and 35 seconds remaining or something like that.

195
13:15.520 --> 13:20.520
So how can we create something like this? If we have the count in terms of

196
13:20.680 --> 13:21.220
seconds,

197
13:21.220 --> 13:26.220
so let's say we have 300, and we wanted to know how many minutes where in that

198
13:26.800 --> 13:31.800
then all we have to do is take 300 and then divide it by 60 and we would get

199
13:32.680 --> 13:37.150
5, so that's 5 minutes. But what if the countdown has already been going

200
13:37.210 --> 13:42.210
and instead we had 245 seconds remaining?

201
13:42.790 --> 13:43.000
Well,

202
13:43.000 --> 13:48.000
we can actually get hold of how many minutes and seconds that is equivalent to.

203
13:50.020 --> 13:54.430
And the way we would do that is by taking that number, say 245,

204
13:54.760 --> 13:56.920
dividing it by 60 seconds

205
13:57.280 --> 14:02.280
and we would get a number 245 / 60 is 4.08,

206
14:05.440 --> 14:06.820
3 recurring.

207
14:07.570 --> 14:12.570
If we rounded that number down to get rid of all of the decimal places,

208
14:13.240 --> 14:16.060
then that would be equal to 4 minutes.

209
14:16.810 --> 14:21.810
And then if we want to get hold of how many seconds there are after we've gotten

210
14:22.420 --> 14:23.620
hold of the four minutes,

211
14:24.010 --> 14:29.010
then the way we do that is to use the modular because remember the modular

212
14:29.650 --> 14:34.450
divides a number by another number, so 245 divided by 60,

213
14:34.870 --> 14:37.240
and then it will give us the remainder.

214
14:37.420 --> 14:42.280
How much is left after it's cleanly divided. And in this case,

215
14:42.340 --> 14:47.340
this would actually be the number of seconds remaining after the four minutes

216
14:48.100 --> 14:51.650
has been taken away. So let's write this code out.

217
14:52.550 --> 14:57.550
The count minutes would be the count divided by 60,

218
14:59.780 --> 15:04.330
but then we have to round it down so that we get rid of all of the remainder.

219
15:04.330 --> 15:09.110
Now we don't want to round it so that if it was something like 3.6

220
15:09.170 --> 15:10.640
it becomes 4.

221
15:10.910 --> 15:15.770
We actually just want to get rid of everything after the decimal place. To do

222
15:15.770 --> 15:16.310
that,

223
15:16.310 --> 15:21.310
the easiest way is to import the math module and then use a function called math

224
15:23.870 --> 15:27.800
.floor and this math.floor,

225
15:27.830 --> 15:32.830
if I hover over it, is going to return the largest whole number that is less than

226
15:36.020 --> 15:37.310
or equal to x.

227
15:37.790 --> 15:42.790
If this was a 4.8, then the largest hole number less than 4.8 is 4.

228
15:46.250 --> 15:47.960
So that's basically what this is going to do.

229
15:48.170 --> 15:51.020
And this is going to give us the number of minutes. Now,

230
15:51.020 --> 15:53.000
the next thing we want to do is to know, well,

231
15:53.000 --> 15:58.000
how many seconds is left after we've taken away the minutes. To do this,

232
15:58.700 --> 15:59.660
we're going to do counts

233
15:59.720 --> 16:03.200
and then we're going to use the modulo to divide it by 60.

234
16:03.770 --> 16:08.770
So the modulo is going to give us the remainder number of seconds after we've

235
16:09.440 --> 16:12.650
cleanly divided it by 600. For example,

236
16:12.650 --> 16:16.250
if we had 100 seconds and we divide that by 60, well,

237
16:16.250 --> 16:17.870
that's going to be equal to one.

238
16:18.290 --> 16:23.290
So we can minus 60 from 100 and we get 40 as the remainder.

239
16:25.010 --> 16:27.830
That is what we will get after doing the modulo.

240
16:28.430 --> 16:31.220
So that's basically the number of seconds that remains.

241
16:32.240 --> 16:36.260
So now that we've got the minute and the second, we can actually change this

242
16:36.260 --> 16:41.260
count to use an f-string and we can format it so that we add the count minute

243
16:42.170 --> 16:43.003
first,

244
16:43.130 --> 16:48.130
and then we add a colon and then we add our count in seconds.

245
16:49.430 --> 16:53.900
Now look at what happens. We're going to try and count down five minutes.

246
16:54.260 --> 16:58.490
So we get hold of the number of seconds, we pass it over to this function,

247
16:58.880 --> 17:03.080
we work out what the count is equivalent to in minutes and seconds

248
17:03.470 --> 17:06.920
and then afterwards we subtract one second each time.

249
17:07.520 --> 17:10.310
So now when I run the code and I hit start,

250
17:10.640 --> 17:14.270
notice how it starts from 5 minutes, goes down to 4 minutes,

251
17:14.270 --> 17:15.980
50, 59,

252
17:16.070 --> 17:19.310
and then it keeps ongoing like a real timer.

253
17:20.180 --> 17:21.440
That's pretty cool.

254
17:21.650 --> 17:26.650
The only thing left to figure out is how can we get it to not display 5:0?

255
17:27.350 --> 17:32.060
How can we get it to display 5:00

256
17:32.840 --> 17:36.260
like you would see on a clock? To find out how to do that

257
17:36.290 --> 17:39.170
we have to learn about Python dynamic typing.

258
17:39.440 --> 17:43.370
We have to understand how that works and that is what we're going to be talking

259
17:43.370 --> 17:46.340
about in the next lesson. So I'll see you there.