1
00:00:00,540 --> 00:00:01,560
In the last lesson,

2
00:00:01,589 --> 00:00:05,880
we completed the user interface for our Pomodoro timer,

3
00:00:06,450 --> 00:00:11,130
and we've managed to get all the components that we need onto our graphical user

4
00:00:11,130 --> 00:00:16,050
interface. Now, the next step is to actually give it some functionality.

5
00:00:16,770 --> 00:00:21,480
I want to be able to create some sort of countdown mechanism that just does

6
00:00:21,480 --> 00:00:24,000
something really simple. For example,

7
00:00:24,000 --> 00:00:28,830
if it was just able to count down from five, four, three, two,

8
00:00:29,220 --> 00:00:33,600
one going down by one each time, that would be great.

9
00:00:34,110 --> 00:00:36,420
That's what we're going to be working on in this lesson.

10
00:00:36,480 --> 00:00:40,530
And we're going to go and see the countdown mechanism section to do that.

11
00:00:41,490 --> 00:00:46,470
One of the ways that you might think about approaching this is to use our time

12
00:00:46,470 --> 00:00:47,303
module

13
00:00:47,340 --> 00:00:52,020
cause we've seen before that we can say time.sleep and we can tell it to

14
00:00:52,020 --> 00:00:53,550
sleep for a second.

15
00:00:54,000 --> 00:00:58,980
So then we could maybe set up a while loop and while something or other is true,

16
00:00:59,280 --> 00:01:04,260
go ahead and sleep for one second. And then afterwards just,

17
00:01:04,319 --> 00:01:04,590
you know,

18
00:01:04,590 --> 00:01:09,240
subtract one from some sort of counter. So that we could start count at five

19
00:01:09,240 --> 00:01:14,240
and then each time we subtract by one and then each time we just simply update

20
00:01:15,360 --> 00:01:16,890
our label here,

21
00:01:17,250 --> 00:01:21,750
which we created in the canvas to whatever value count might be.

22
00:01:22,560 --> 00:01:24,990
Now, that sounds great in principle.

23
00:01:25,200 --> 00:01:30,200
The only problem is that we're working within a graphical user interface

24
00:01:30,690 --> 00:01:31,523
program.

25
00:01:32,310 --> 00:01:36,750
The reason why that's relevant is because if we think about a command line

26
00:01:36,750 --> 00:01:38,640
program, say for example,

27
00:01:38,640 --> 00:01:43,020
if we were to get our console to do something, print

28
00:01:43,080 --> 00:01:43,980
hello, well,

29
00:01:43,980 --> 00:01:48,980
it's only going to do something when you actually give it an instruction and you

30
00:01:49,380 --> 00:01:50,213
hit enter.

31
00:01:50,760 --> 00:01:55,530
It doesn't really need to keep an eye out for what you might do in between.

32
00:01:56,130 --> 00:01:59,190
But a graphical user interface is a little bit different.

33
00:01:59,520 --> 00:02:03,690
It needs to keep watching the screen to see whether

34
00:02:03,690 --> 00:02:06,870
if a user clicks on a button, for example.

35
00:02:07,350 --> 00:02:12,350
So it's basically going to refresh and keep listening for events.

36
00:02:13,110 --> 00:02:14,880
So every fraction of a second,

37
00:02:14,880 --> 00:02:16,890
it's going to keep checking, did something happene, did

38
00:02:16,920 --> 00:02:20,760
something happen, did something happen. And the moment when it does,

39
00:02:20,850 --> 00:02:24,510
then it's got to react. It's got to react to that event.

40
00:02:25,050 --> 00:02:30,030
In this case, we tend to call these types of GUI programs event-driven.

41
00:02:30,690 --> 00:02:35,040
And the way that it's driven is through our main loop.

42
00:02:35,490 --> 00:02:39,450
So when we set up our window and we start off the main loop,

43
00:02:39,780 --> 00:02:44,780
it's basically looping through and every millisecond it's checking to see did

44
00:02:44,940 --> 00:02:47,730
something happen, did something happen, did something happen?

45
00:02:48,210 --> 00:02:51,090
So that means if we have another loop in our program,

46
00:02:51,120 --> 00:02:55,200
it actually won't be able to reach the main loop. And in this case,

47
00:02:55,230 --> 00:02:57,780
when you actually try to run it, nothing happens.

48
00:02:57,840 --> 00:02:59,830
Our program doesn't even launch.

49
00:03:00,340 --> 00:03:05,260
So we have to rethink this and we have to do it a little bit differently. In

50
00:03:05,260 --> 00:03:08,950
order to create interactive and interesting programs,

51
00:03:09,370 --> 00:03:12,640
you kind of need something to happen on screen, right?

52
00:03:12,640 --> 00:03:16,750
Every so often. You need this timing mechanism. Luckily,

53
00:03:16,780 --> 00:03:18,820
tkinter already thought of this.

54
00:03:19,150 --> 00:03:23,800
And we can in fact use one of the builtin methods to every widget.

55
00:03:24,220 --> 00:03:26,140
So if we tap into our window widget,

56
00:03:26,290 --> 00:03:31,290
we can get hold of a method called after and after is quite simple.

57
00:03:32,230 --> 00:03:36,760
It's a method that takes an amount of time that it should wait

58
00:03:37,270 --> 00:03:39,190
and then after that amount of time,

59
00:03:39,460 --> 00:03:44,460
it simply calls a particular function that you tell it to call passing in any

60
00:03:44,650 --> 00:03:48,760
arguments that you want to give it. Here's how it works.

61
00:03:48,820 --> 00:03:53,820
We call window.after, we first provide the amount of time to wait in

62
00:03:54,460 --> 00:03:58,720
milliseconds. So if we want one second, then that's 1000 milliseconds.

63
00:03:59,320 --> 00:04:02,200
Next we pass in a function to call.

64
00:04:02,530 --> 00:04:07,390
So let's create a function up here. Let's just call it, say something,

65
00:04:07,990 --> 00:04:10,330
and then we'll pass in the thing,

66
00:04:12,610 --> 00:04:16,420
like this. And then all we do is we just print that thing.

67
00:04:16,959 --> 00:04:18,760
So super simple function.

68
00:04:18,790 --> 00:04:23,790
And then we give the name of this function as the function to call after 1000

69
00:04:24,310 --> 00:04:25,143
milliseconds.

70
00:04:25,600 --> 00:04:29,590
Now the final thing in this list of arguments,

71
00:04:29,920 --> 00:04:34,090
if I just go ahead and cut that and show you again, when it gives me the prompt,

72
00:04:34,510 --> 00:04:39,010
the last thing is actually a *args.

73
00:04:39,490 --> 00:04:44,230
This, if you remember, allows us to put in an unlimited number 

74
00:04:44,290 --> 00:04:46,090
of positional arguments.

75
00:04:46,600 --> 00:04:50,890
What that means is we can give as many arguments as we want

76
00:04:51,220 --> 00:04:56,220
and those arguments, in this case, is simply going to be passed to the function

77
00:04:56,350 --> 00:04:59,920
that we want to call. So in this case, it's going to be that thing.

78
00:05:00,190 --> 00:05:03,580
So if I put hello here then I run my code,

79
00:05:03,940 --> 00:05:07,990
you can see that after 1000 milliseconds, basically one second,

80
00:05:08,440 --> 00:05:10,750
it calls this function,

81
00:05:10,810 --> 00:05:15,400
say_something, and it passes this hello as the input

82
00:05:15,580 --> 00:05:18,370
to that function. As I said,

83
00:05:18,400 --> 00:05:22,000
you can have an infinite amount of positional arguments.

84
00:05:22,330 --> 00:05:26,530
So let's put in some other arguments which we'll call a,

85
00:05:26,530 --> 00:05:27,363
b, and c,

86
00:05:27,610 --> 00:05:32,320
and then we'll print a, print b, and print c.

87
00:05:33,970 --> 00:05:36,640
And now instead of passing in hello,

88
00:05:36,670 --> 00:05:41,650
we're going to pass in lots of positional parameters. So we'll say 3, 5,

89
00:05:41,680 --> 00:05:44,500
and 8. Now, when I hit run,

90
00:05:44,560 --> 00:05:46,840
you will see it waits for one second

91
00:05:47,170 --> 00:05:51,850
and then it passes all three of these parameters to say something

92
00:05:52,270 --> 00:05:55,480
and it goes ahead and prints all of those out at once.

93
00:05:56,050 --> 00:06:01,010
So this is how the after method works. But what we wanted to do though,

94
00:06:01,370 --> 00:06:03,920
is we want it to repeat itself,

95
00:06:04,010 --> 00:06:09,010
to essentially loop. One way of getting that behavior is to simply put this

96
00:06:10,760 --> 00:06:11,240
method,

97
00:06:11,240 --> 00:06:16,240
call somewhere inside a function and then call itself.

98
00:06:16,370 --> 00:06:20,930
So here's what I mean. Let's create a function called a count_down,

99
00:06:21,740 --> 00:06:26,740
and this is going to take a input in the form of the number to count down by.

100
00:06:28,550 --> 00:06:32,270
And then inside this function, we call window.after.

101
00:06:32,720 --> 00:06:35,510
And we say that after 1000 milliseconds,

102
00:06:35,750 --> 00:06:40,750
call this function count_down and then pass in a count number.

103
00:06:44,570 --> 00:06:47,390
If that count number started out as 5,

104
00:06:47,630 --> 00:06:50,840
then we want to say count - 1.

105
00:06:51,320 --> 00:06:55,700
Now all we have to do is to call this countdown method.

106
00:06:56,030 --> 00:07:00,530
So let's call count_down and passing the starting count,

107
00:07:00,590 --> 00:07:04,430
let's say 5 seconds. So now when I run the code,

108
00:07:04,430 --> 00:07:07,760
it's going to call this method passing in 5 over here,

109
00:07:08,300 --> 00:07:11,720
and then it's going to wait for one second,

110
00:07:12,110 --> 00:07:17,060
and then it's going to call this function count_down passing in five minus one

111
00:07:17,240 --> 00:07:21,920
so it becomes four. And then afterward it repeats again, becomes three,

112
00:07:21,950 --> 00:07:26,180
two, one. So now if we catch that number

113
00:07:26,360 --> 00:07:30,500
which we can print, then we'll be able to see it count down

114
00:07:30,530 --> 00:07:34,190
when we run the code; five, four, three,

115
00:07:34,580 --> 00:07:38,390
two, one. One every second,

116
00:07:38,450 --> 00:07:42,770
and it basically keeps on going and it even continues to the negatives.

117
00:07:43,310 --> 00:07:46,310
So if we don't want it to go to negative time,

118
00:07:46,340 --> 00:07:49,220
then all we have to do is add an if statement.

119
00:07:49,640 --> 00:07:54,620
If count is greater than zero, then go ahead and execute this line of code.

120
00:07:55,880 --> 00:07:59,780
So now it'll go from five, four, three, two,

121
00:07:59,810 --> 00:08:02,570
one, zero, and then it will stop.

122
00:08:03,470 --> 00:08:08,450
This is the kind of behavior that we would need if we want to update our

123
00:08:08,450 --> 00:08:11,180
countdown in our Pomodoro timer.

124
00:08:11,900 --> 00:08:14,180
So how can we instead of printing

125
00:08:14,180 --> 00:08:19,180
the count actually change this text on our canvas? We'll,

126
00:08:19,820 --> 00:08:24,820
the way that we do that is by assigning this text a variable.

127
00:08:25,430 --> 00:08:28,460
So I'm going to call it timer_text

128
00:08:29,870 --> 00:08:34,870
and now that we've got timer_text being a set as the text that was created in

129
00:08:35,360 --> 00:08:38,870
the canvas, then we can access it right here.

130
00:08:39,620 --> 00:08:44,120
And the way that we'd change a piece of text or anything for that matter in a

131
00:08:44,120 --> 00:08:48,110
canvas is slightly different from how we would do for a label.

132
00:08:48,530 --> 00:08:52,070
If it was just the title label that we wanted to change, we would say title_

133
00:08:52,070 --> 00:08:56,310
label.config, and then let's change the text to something new.

134
00:08:56,940 --> 00:08:58,920
But to change a canvas element,

135
00:08:58,950 --> 00:09:02,640
you actually have to tap into the particular canvas you want to change and

136
00:09:03,240 --> 00:09:08,220
Then you call a method called itemconfig. And then in this method,

137
00:09:08,280 --> 00:09:12,240
you pass in the particular item that you actually want to configure,

138
00:09:12,540 --> 00:09:14,910
so in our case it's the timer_text,

139
00:09:15,570 --> 00:09:20,570
and then you pass in the thing about it that you actually want to change in

140
00:09:20,970 --> 00:09:24,300
terms of a kwarg, so this is a keyword argument.

141
00:09:24,870 --> 00:09:28,380
We're going to change the text to the current count.

142
00:09:28,650 --> 00:09:31,620
Now notice how this is not the string count,

143
00:09:31,830 --> 00:09:34,290
because then it would just show that word,

144
00:09:34,620 --> 00:09:39,270
but its actually the live countdown time. At this point in time

145
00:09:39,300 --> 00:09:40,500
if I run the code,

146
00:09:40,500 --> 00:09:45,500
I actually get a error and it tells us that the name canvas is not defined

147
00:09:46,320 --> 00:09:50,940
and that's because I'm calling this method countdown before I actually created

148
00:09:50,940 --> 00:09:51,690
the canvas.

149
00:09:51,690 --> 00:09:56,250
So if I move that to below this line and I run it again,

150
00:09:56,280 --> 00:09:58,020
then you'll see it actually work.

151
00:09:58,320 --> 00:10:02,370
And you see it starts out from five and it counts down to zero.

152
00:10:03,180 --> 00:10:08,180
Now how can we tie that behavior to the start button so that I can press the

153
00:10:08,250 --> 00:10:13,140
start button and then and only then does it start counting down from five,

154
00:10:13,140 --> 00:10:17,700
four, three, two, one? Well, let's go ahead and add 

155
00:10:17,760 --> 00:10:22,500
another function and I'm gonna add it in the timer mechanism section and I'm

156
00:10:22,500 --> 00:10:24,120
going to call it start_timer.

157
00:10:24,900 --> 00:10:27,750
Now this function is super simple.

158
00:10:27,930 --> 00:10:32,930
All it's going to do is it's going to be responsible for calling that function

159
00:10:33,030 --> 00:10:36,660
countdown and it's going to count down from five seconds.

160
00:10:36,810 --> 00:10:39,840
So I'll move that inside the start_timer.

161
00:10:40,500 --> 00:10:45,500
And now the start timer is going to be the function that needs to be triggered

162
00:10:47,070 --> 00:10:49,560
when the start button gets pressed.

163
00:10:49,920 --> 00:10:53,280
Do you remember how to tie a function to a button in

164
00:10:53,280 --> 00:10:58,260
tkinter? Pause the video and see if you can solve this challenge so that you'll

165
00:10:58,260 --> 00:11:03,260
be able to run the code, hit start and the timer to start counting down.

166
00:11:06,630 --> 00:11:11,630
So the keyword argument is command and all we have to do is to tie it to the

167
00:11:12,210 --> 00:11:15,990
start_timer function, but without the parentheses.

168
00:11:16,500 --> 00:11:19,920
So now when I hit run and I click start,

169
00:11:20,340 --> 00:11:24,840
it starts the timer setting that text from five, four, three,

170
00:11:24,930 --> 00:11:25,763
two, one.

171
00:11:26,220 --> 00:11:31,220
So the main loop is listening and when the user interacts with the start button

172
00:11:31,890 --> 00:11:36,660
it actually calls the start_timer function which calls the count_down function

173
00:11:36,960 --> 00:11:39,900
and get it to count down from five seconds.

174
00:11:40,560 --> 00:11:43,980
Now we don't actually want to count down from five seconds.

175
00:11:44,010 --> 00:11:48,750
We want to count down in minutes because we're probably not going to be working

176
00:11:48,750 --> 00:11:50,130
for five seconds at a time.

177
00:11:50,160 --> 00:11:54,040
We're going to be working for 25 minutes or having a minute break.

178
00:11:54,580 --> 00:11:59,580
So how can I change this countdown to interpret this instead of as five seconds

179
00:12:01,390 --> 00:12:04,990
to five minutes? Well, let's have a think about that.

180
00:12:05,680 --> 00:12:09,850
If we wanted to count down, let's say one minute,

181
00:12:10,240 --> 00:12:14,230
then that in terms of seconds would be 60 seconds.

182
00:12:14,860 --> 00:12:18,670
All we have to do is to take the number of minutes that we want to count down

183
00:12:18,670 --> 00:12:22,540
by and multiply it by 60. In this case,

184
00:12:22,540 --> 00:12:27,040
if we wanted to count down it by five minutes instead of five seconds,

185
00:12:27,400 --> 00:12:30,160
all we have to do is multiply by 60.

186
00:12:30,670 --> 00:12:33,880
So then when we call this function count_down,

187
00:12:34,150 --> 00:12:36,790
instead of getting five seconds to count down,

188
00:12:36,820 --> 00:12:39,190
we get 300 seconds to count from.

189
00:12:39,880 --> 00:12:44,470
But now if we run our code, you can see it's going to start from 300.

190
00:12:44,740 --> 00:12:49,240
It's going to go down all the way down to zero. Now in terms of time,

191
00:12:49,390 --> 00:12:54,040
that is five minutes, but this is not a very good way to visualize it.

192
00:12:54,370 --> 00:12:58,570
Nobody thinks in terms of 288 seconds remaining, right?

193
00:12:59,140 --> 00:13:04,140
So we have to format this count so that we can display it in the format of 

194
00:13:04,720 --> 00:13:09,010
00:00 like the usual kind of time, where for example,

195
00:13:09,010 --> 00:13:14,010
you have one minute and 35 seconds remaining or something like that.

196
00:13:15,520 --> 00:13:20,520
So how can we create something like this? If we have the count in terms of

197
00:13:20,680 --> 00:13:21,220
seconds,

198
00:13:21,220 --> 00:13:26,220
so let's say we have 300, and we wanted to know how many minutes where in that

199
00:13:26,800 --> 00:13:31,800
then all we have to do is take 300 and then divide it by 60 and we would get

200
00:13:32,680 --> 00:13:37,150
5, so that's 5 minutes. But what if the countdown has already been going

201
00:13:37,210 --> 00:13:42,210
and instead we had 245 seconds remaining?

202
00:13:42,790 --> 00:13:43,000
Well,

203
00:13:43,000 --> 00:13:48,000
we can actually get hold of how many minutes and seconds that is equivalent to.

204
00:13:50,020 --> 00:13:54,430
And the way we would do that is by taking that number, say 245,

205
00:13:54,760 --> 00:13:56,920
dividing it by 60 seconds

206
00:13:57,280 --> 00:14:02,280
and we would get a number 245 / 60 is 4.08,

207
00:14:05,440 --> 00:14:06,820
3 recurring.

208
00:14:07,570 --> 00:14:12,570
If we rounded that number down to get rid of all of the decimal places,

209
00:14:13,240 --> 00:14:16,060
then that would be equal to 4 minutes.

210
00:14:16,810 --> 00:14:21,810
And then if we want to get hold of how many seconds there are after we've gotten

211
00:14:22,420 --> 00:14:23,620
hold of the four minutes,

212
00:14:24,010 --> 00:14:29,010
then the way we do that is to use the modular because remember the modular

213
00:14:29,650 --> 00:14:34,450
divides a number by another number, so 245 divided by 60,

214
00:14:34,870 --> 00:14:37,240
and then it will give us the remainder.

215
00:14:37,420 --> 00:14:42,280
How much is left after it's cleanly divided. And in this case,

216
00:14:42,340 --> 00:14:47,340
this would actually be the number of seconds remaining after the four minutes

217
00:14:48,100 --> 00:14:51,650
has been taken away. So let's write this code out.

218
00:14:52,550 --> 00:14:57,550
The count minutes would be the count divided by 60,

219
00:14:59,780 --> 00:15:04,330
but then we have to round it down so that we get rid of all of the remainder.

220
00:15:04,330 --> 00:15:09,110
Now we don't want to round it so that if it was something like 3.6

221
00:15:09,170 --> 00:15:10,640
it becomes 4.

222
00:15:10,910 --> 00:15:15,770
We actually just want to get rid of everything after the decimal place. To do

223
00:15:15,770 --> 00:15:16,310
that,

224
00:15:16,310 --> 00:15:21,310
the easiest way is to import the math module and then use a function called math

225
00:15:23,870 --> 00:15:27,800
.floor and this math.floor,

226
00:15:27,830 --> 00:15:32,830
if I hover over it, is going to return the largest whole number that is less than

227
00:15:36,020 --> 00:15:37,310
or equal to x.

228
00:15:37,790 --> 00:15:42,790
If this was a 4.8, then the largest hole number less than 4.8 is 4.

229
00:15:46,250 --> 00:15:47,960
So that's basically what this is going to do.

230
00:15:48,170 --> 00:15:51,020
And this is going to give us the number of minutes. Now,

231
00:15:51,020 --> 00:15:53,000
the next thing we want to do is to know, well,

232
00:15:53,000 --> 00:15:58,000
how many seconds is left after we've taken away the minutes. To do this,

233
00:15:58,700 --> 00:15:59,660
we're going to do counts

234
00:15:59,720 --> 00:16:03,200
and then we're going to use the modulo to divide it by 60.

235
00:16:03,770 --> 00:16:08,770
So the modulo is going to give us the remainder number of seconds after we've

236
00:16:09,440 --> 00:16:12,650
cleanly divided it by 600. For example,

237
00:16:12,650 --> 00:16:16,250
if we had 100 seconds and we divide that by 60, well,

238
00:16:16,250 --> 00:16:17,870
that's going to be equal to one.

239
00:16:18,290 --> 00:16:23,290
So we can minus 60 from 100 and we get 40 as the remainder.

240
00:16:25,010 --> 00:16:27,830
That is what we will get after doing the modulo.

241
00:16:28,430 --> 00:16:31,220
So that's basically the number of seconds that remains.

242
00:16:32,240 --> 00:16:36,260
So now that we've got the minute and the second, we can actually change this

243
00:16:36,260 --> 00:16:41,260
count to use an f-string and we can format it so that we add the count minute

244
00:16:42,170 --> 00:16:43,003
first,

245
00:16:43,130 --> 00:16:48,130
and then we add a colon and then we add our count in seconds.

246
00:16:49,430 --> 00:16:53,900
Now look at what happens. We're going to try and count down five minutes.

247
00:16:54,260 --> 00:16:58,490
So we get hold of the number of seconds, we pass it over to this function,

248
00:16:58,880 --> 00:17:03,080
we work out what the count is equivalent to in minutes and seconds

249
00:17:03,470 --> 00:17:06,920
and then afterwards we subtract one second each time.

250
00:17:07,520 --> 00:17:10,310
So now when I run the code and I hit start,

251
00:17:10,640 --> 00:17:14,270
notice how it starts from 5 minutes, goes down to 4 minutes,

252
00:17:14,270 --> 00:17:15,980
50, 59,

253
00:17:16,069 --> 00:17:19,310
and then it keeps ongoing like a real timer.

254
00:17:20,180 --> 00:17:21,440
That's pretty cool.

255
00:17:21,650 --> 00:17:26,650
The only thing left to figure out is how can we get it to not display 5:0?

256
00:17:27,349 --> 00:17:32,060
How can we get it to display 5:00

257
00:17:32,840 --> 00:17:36,260
like you would see on a clock? To find out how to do that

258
00:17:36,290 --> 00:17:39,170
we have to learn about Python dynamic typing.

259
00:17:39,440 --> 00:17:43,370
We have to understand how that works and that is what we're going to be talking

260
00:17:43,370 --> 00:17:46,340
about in the next lesson. So I'll see you there.

