1
00:00:00,210 --> 00:00:04,710
So now that our program works for text-based questions,

2
00:00:05,160 --> 00:00:10,160
it's time to upgrade this program so that it actually has a graphical user

3
00:00:10,530 --> 00:00:13,830
interface. You'll notice that in the project folder,

4
00:00:14,150 --> 00:00:16,790
there is a file called ui.py,

5
00:00:17,420 --> 00:00:22,160
and this is pretty much empty at the moment, other than a single constant

6
00:00:22,430 --> 00:00:24,950
which sets the theme color. Now,

7
00:00:24,950 --> 00:00:28,310
what we're trying to create is something that looks like this.

8
00:00:28,790 --> 00:00:31,850
So we have some sort of a score board label,

9
00:00:32,060 --> 00:00:36,110
we have a canvas with a white background,

10
00:00:36,440 --> 00:00:37,670
and then on the canvas,

11
00:00:37,700 --> 00:00:42,020
we've got some text in the middle and then we've got a true button and a false

12
00:00:42,020 --> 00:00:46,820
button each using the true and false images from this folder.

13
00:00:47,480 --> 00:00:51,680
Now we've seen how we can create graphical user interfaces using tkinter.

14
00:00:52,130 --> 00:00:55,190
But in this case, we're going to do something a little bit different.

15
00:00:55,790 --> 00:00:59,060
We're going to create our user interface in tkinter

16
00:00:59,330 --> 00:01:01,730
but we're going to do it inside a class.

17
00:01:02,210 --> 00:01:05,870
If you remember from day 17 where this project comes from,

18
00:01:06,260 --> 00:01:09,170
that was when we learned about Object Oriented Programming,

19
00:01:09,530 --> 00:01:13,850
and we learned about how to separate out our concerns like

20
00:01:13,910 --> 00:01:18,110
the part that fetches the data or the part that model each of the questions

21
00:01:18,380 --> 00:01:22,070
or the part that handles the actual quiz functionality,

22
00:01:22,430 --> 00:01:27,430
and they are all separated in that own class. In order to create our user

23
00:01:27,530 --> 00:01:28,280
interface

24
00:01:28,280 --> 00:01:33,280
we're also going to create our very own class inside this ui.py.

25
00:01:34,370 --> 00:01:36,710
So it's been a while since we've created classes.

26
00:01:36,770 --> 00:01:40,910
So I want to do a quick reminder session on how to do that.

27
00:01:41,330 --> 00:01:45,740
So we use the class keyword and then we provide the name of the class. Now,

28
00:01:45,770 --> 00:01:48,890
in my case, I'm going to call this the QuizInterface.

29
00:01:49,550 --> 00:01:54,350
And remember that the way that you name classes is what this is called. Pascal

30
00:01:54,380 --> 00:01:56,000
case. So a capital letter,

31
00:01:56,240 --> 00:02:01,240
and then each word is not separated by any underscores or any dashes.

32
00:02:01,880 --> 00:02:03,170
So this is our class

33
00:02:03,260 --> 00:02:07,490
and then we're going to create our init inside this class.

34
00:02:08,060 --> 00:02:10,550
Now, if any of this is unfamiliar to you,

35
00:02:10,550 --> 00:02:14,660
then make sure that you haven't skipped the lessons on Object Oriented

36
00:02:14,660 --> 00:02:18,410
Programming and creating and using classes in Python.

37
00:02:18,740 --> 00:02:21,890
We covered that in day 17. So if you skipped that,

38
00:02:22,040 --> 00:02:25,580
be sure to head back before you proceed, because otherwise, the rest of this

39
00:02:25,880 --> 00:02:29,780
is not going to make any sense at all. In our class init,

40
00:02:29,840 --> 00:02:32,930
we're going to create our user interface.

41
00:02:33,140 --> 00:02:35,870
So we're going to create all the buttons and all of the layouts.

42
00:02:36,200 --> 00:02:40,670
And we start off by simply creating our window. Now this time though,

43
00:02:40,700 --> 00:02:45,380
instead of creating our window as just a simple variable,

44
00:02:45,710 --> 00:02:48,500
we're going to make it a property of this class.

45
00:02:48,530 --> 00:02:50,480
So we're going to add self in front of it.

46
00:02:51,050 --> 00:02:54,710
And then we're going to create it from the tkinter module.

47
00:02:54,770 --> 00:02:59,180
So let's go ahead and import everything from the tkinter module,

48
00:03:01,450 --> 00:03:06,450
and let's set our window as a new object from the tk class.

49
00:03:07,780 --> 00:03:11,920
Next, I'm going to change some aspects of the window. So for example,

50
00:03:11,920 --> 00:03:16,090
I'm going to change the title. So I'm going to set it to, um,

51
00:03:16,420 --> 00:03:19,090
Quizzler which is going to be the name of this app.

52
00:03:19,960 --> 00:03:23,470
We can continued like this inside the init function,

53
00:03:23,800 --> 00:03:28,510
which remember, is going to be called whenever we create a new object from this

54
00:03:28,510 --> 00:03:29,343
class.

55
00:03:29,590 --> 00:03:34,590
This is where we're also going to have our self.window.mainloop

56
00:03:36,340 --> 00:03:41,080
and that will set our program to run. In order to test this,

57
00:03:41,140 --> 00:03:43,180
we're going to go back to our main.py

58
00:03:43,570 --> 00:03:46,000
and we're going to import that class.

59
00:03:46,180 --> 00:03:49,870
So from ui import the QuizInterface,

60
00:03:50,290 --> 00:03:53,650
and then we're going to create that QuizInterface down here.

61
00:03:55,210 --> 00:03:57,010
I'm going to call it the quiz_ui

62
00:03:57,550 --> 00:04:01,990
and it's going to be initialized from our quiz interface. Now,

63
00:04:02,020 --> 00:04:04,360
if you remember, when we first learned about 

64
00:04:04,360 --> 00:04:09,360
tkinter I told you that tkinter works by having this endless loop

65
00:04:10,810 --> 00:04:13,090
essentially that's called the main loop.

66
00:04:13,900 --> 00:04:16,630
And this is a bit like a never ending while loop.

67
00:04:16,899 --> 00:04:21,130
It's constantly checking to see if it needs to update something in the graphical

68
00:04:21,130 --> 00:04:25,810
user interface, or if the user has interacted with it in some sort of way.

69
00:04:26,410 --> 00:04:31,270
So it will get confused if you have another while loop somewhere near it.

70
00:04:31,840 --> 00:04:34,060
So we actually have to comment out this while loop

71
00:04:34,420 --> 00:04:38,560
if we want this user interface to work properly. Now,

72
00:04:38,590 --> 00:04:40,810
whenever we want to test out our UI,

73
00:04:40,840 --> 00:04:44,200
all we have to do is to run our main.py.

74
00:04:44,860 --> 00:04:46,570
And once it hits this line,

75
00:04:46,600 --> 00:04:51,340
it's going to create a new object from our QuizInterface over here.

76
00:04:51,850 --> 00:04:55,090
And once it does that, it's going to hit the init method

77
00:04:55,420 --> 00:04:57,310
and it's going to create our new GUI.

78
00:04:57,940 --> 00:05:01,540
You can continue coding up your user interface inside this init

79
00:05:01,840 --> 00:05:03,880
and it will work just as it did before

80
00:05:04,030 --> 00:05:08,680
when you created just a very simple tkinter graphical user interface

81
00:05:08,710 --> 00:05:12,100
application. Here comes the challenging part.

82
00:05:12,670 --> 00:05:15,790
I want you to create this particular user interface,

83
00:05:16,300 --> 00:05:21,300
and I've created a slide that shows you all of the padding, the sizing,

84
00:05:21,610 --> 00:05:23,980
the fonts, the background color,

85
00:05:24,010 --> 00:05:26,440
and all of the components that go onto this screen.

86
00:05:27,070 --> 00:05:31,840
I want you to pause the video here and see if you can create this user interface

87
00:05:32,050 --> 00:05:35,380
inside the init method of the quiz interface class.

88
00:05:36,070 --> 00:05:38,020
Pause the video now and give that a go.

89
00:05:38,130 --> 00:05:38,963
Okay.

90
00:05:42,780 --> 00:05:43,200
All right.

91
00:05:43,200 --> 00:05:46,770
So we're going to continue where we left off here inside the init method.

92
00:05:47,190 --> 00:05:50,160
And we already started off our window property

93
00:05:50,190 --> 00:05:53,100
so we're going to continue making that a little bit better.

94
00:05:53,490 --> 00:05:56,940
So we're going to call the config method on this self.window,

95
00:05:57,350 --> 00:06:02,210
and we're going to change the padx and the pady so that we add 20 pixels

96
00:06:02,210 --> 00:06:05,420
of padding on all four sites. Now,

97
00:06:05,450 --> 00:06:08,180
the next thing I want to configure is the background color.

98
00:06:08,540 --> 00:06:10,280
And as I mentioned in this slide,

99
00:06:10,520 --> 00:06:13,610
we're going to use this theme color that I've already provided here,

100
00:06:13,940 --> 00:06:16,070
which is a nice sort of midnight blue.

101
00:06:16,580 --> 00:06:19,040
So I'm going to tap into the background attribute

102
00:06:19,130 --> 00:06:22,700
and I'm going to set it to this theme color. Now,

103
00:06:22,730 --> 00:06:25,130
if we go ahead and run our main file,

104
00:06:25,490 --> 00:06:28,550
then we can see our user interface show up.

105
00:06:30,830 --> 00:06:32,720
And it's now got the background color

106
00:06:32,900 --> 00:06:34,940
and it's a little bit bigger because of the padding.

107
00:06:35,870 --> 00:06:39,800
The next step is to create this label up here,

108
00:06:39,860 --> 00:06:42,320
which is just a very simple text label.

109
00:06:42,950 --> 00:06:46,730
So let's go ahead and create it as a property of this class.

110
00:06:47,000 --> 00:06:48,890
So I'm going to call it the score label,

111
00:06:49,400 --> 00:06:53,690
and I'm going to create it from the label class of tkinter. I'm going to set

112
00:06:53,690 --> 00:06:56,300
the text as just Score: 0.

113
00:06:57,020 --> 00:07:00,050
And I'm going to change the color of this text,

114
00:07:00,080 --> 00:07:03,410
which is the foreground, to white.

115
00:07:03,710 --> 00:07:07,580
That way it will actually be able to show up on this dark background.

116
00:07:08,510 --> 00:07:10,790
Now, in order for this label to show up, we,

117
00:07:10,790 --> 00:07:13,040
of course, have to give it some sort of layout.

118
00:07:13,340 --> 00:07:18,020
So we're going to use the grid and I'm going to set the row to zero

119
00:07:18,050 --> 00:07:21,980
cause it's the very top, and then the column to one.

120
00:07:22,490 --> 00:07:26,720
So this way it sticks to the right. At this point when we run it,

121
00:07:26,750 --> 00:07:28,460
because there is no column zero,

122
00:07:28,760 --> 00:07:32,390
it's still going to be just showing up on the screen on the left.

123
00:07:33,200 --> 00:07:38,200
And it's also got a white background making it impossible to see the white text.

124
00:07:39,320 --> 00:07:44,030
Let's go ahead and change the background of this label to the same theme color.

125
00:07:44,570 --> 00:07:48,890
And that way, when we run it, we actually see our label with its white text.

126
00:07:49,880 --> 00:07:54,050
That's the label done. And the next step is to create our canvas.

127
00:07:54,470 --> 00:07:59,470
Remember that our canvas is really useful because it allows us to layer lots of

128
00:07:59,540 --> 00:08:00,470
things on top of it.

129
00:08:01,220 --> 00:08:05,120
And we can also set its background and size very easily.

130
00:08:05,630 --> 00:08:09,890
So we're going to create this canvas from the canvas class and straight away,

131
00:08:09,890 --> 00:08:11,660
I'm going to set the width and height.

132
00:08:12,020 --> 00:08:17,020
So the width is going to be 300 and the height is going to be 250

133
00:08:19,490 --> 00:08:23,600
as we specified in the slide here. In addition,

134
00:08:23,600 --> 00:08:28,600
I'm going to change the background color to white so that it stands out almost

135
00:08:28,610 --> 00:08:33,610
like a card on the background. After I've created my canvas,

136
00:08:33,740 --> 00:08:38,090
I'm going to add some question_text onto the canvas.

137
00:08:38,299 --> 00:08:43,299
So I'm going to create this as the self.canvas.create_text

138
00:08:44,750 --> 00:08:48,860
and this is going to have a single attribute called text,

139
00:08:49,400 --> 00:08:54,400
and I'm going to set the text to say Some Question Text.

140
00:08:55,110 --> 00:08:58,170
So we don't actually have that at the moment. We'll just put in a placeholder.

141
00:08:59,040 --> 00:09:02,610
And if you want to, you can actually change the fill color of this text

142
00:09:02,640 --> 00:09:06,090
which is the color of the text to the theme color as well

143
00:09:06,210 --> 00:09:09,450
just to make it all matchy matchy. Now, finally,

144
00:09:09,480 --> 00:09:13,290
we're going to put our canvas onto the screen by giving it a grid.

145
00:09:13,890 --> 00:09:18,540
And this time we're going to set the row to one, so below the score label,

146
00:09:18,990 --> 00:09:21,390
and then the column I'm going to set it to zero.

147
00:09:21,810 --> 00:09:26,810
But then I'm going to set the column span to two so that it starts at column

148
00:09:27,210 --> 00:09:31,200
zero and spans two columns to column one.

149
00:09:31,620 --> 00:09:33,630
So now when I run this code,

150
00:09:33,780 --> 00:09:37,710
I actually get an error and the error is super obscure.

151
00:09:37,980 --> 00:09:41,520
Tells me tuple index out of range. Now, if you remember

152
00:09:41,520 --> 00:09:43,050
when we worked with tkinter,

153
00:09:43,080 --> 00:09:46,470
whenever we add an image or we add something to the canvas,

154
00:09:46,800 --> 00:09:51,300
we always have to provide a position as the first two arguments.

155
00:09:51,840 --> 00:09:56,700
And that is the tuple that represents the X and Y position of the text.

156
00:09:57,000 --> 00:09:58,920
We have to make sure that we don't forget that.

157
00:09:59,370 --> 00:10:01,500
So the first value is going to be the X,

158
00:10:01,530 --> 00:10:05,160
so that's going to be half of the width so that it's centered on the canvas.

159
00:10:05,490 --> 00:10:07,920
So that's 150, half of 300.

160
00:10:08,280 --> 00:10:13,050
And then the Y is going to be half of 250, which is going to be 125.

161
00:10:14,430 --> 00:10:15,900
Now, if we run this again,

162
00:10:15,930 --> 00:10:20,930
hopefully we won't encounter the same error and you can now see this card show

163
00:10:21,480 --> 00:10:24,330
up and our label show up on the right,

164
00:10:24,360 --> 00:10:28,920
because its in the second column and this is the first column and this canvas

165
00:10:28,950 --> 00:10:33,450
spans both columns. Now this text is a little bit small,

166
00:10:33,450 --> 00:10:38,400
so let's take a look at the slide and see what we need to change it to.

167
00:10:38,820 --> 00:10:42,810
So we want it to be arial font, 20 point size and italic.

168
00:10:43,800 --> 00:10:48,800
Let's go ahead and add that to our text. And because its getting a little bit

169
00:10:48,960 --> 00:10:49,680
crammed in here,

170
00:10:49,680 --> 00:10:54,680
I'm actually going to put this on two separate lines just so I can read it a

171
00:10:54,810 --> 00:10:56,790
little bit easier like this.

172
00:10:58,530 --> 00:11:03,530
Now we can add that font argument and we're going to set it to a tuple with the

173
00:11:03,570 --> 00:11:05,520
first item being the name of the font

174
00:11:05,910 --> 00:11:07,770
and then the second is the size of the font

175
00:11:07,950 --> 00:11:11,940
and finally we'll also make the text italic as well.

176
00:11:12,450 --> 00:11:16,770
So now lets run that again and you'll see the text is now much bigger and its been

177
00:11:16,770 --> 00:11:21,770
formatted in the way that we wanted it to. The next thing we want to do is to

178
00:11:21,930 --> 00:11:25,950
add a bit of padding between this canvas and the score.

179
00:11:26,400 --> 00:11:31,170
So we want to add 50 points of padding here and probably 50 here as well

180
00:11:31,170 --> 00:11:36,030
just to push everything a little bit further away from each other. To do that

181
00:11:36,390 --> 00:11:41,390
all we have to do is the tap into the canvas grid and then add a single pady.

182
00:11:42,630 --> 00:11:45,330
We don't want to add any more padding on the X axis.

183
00:11:45,390 --> 00:11:50,040
The original 20 is enough from the window. But we want to add 50

184
00:11:50,250 --> 00:11:55,250
so that this way the canvas has some padding on the top and the bottom like this. And it

185
00:11:56,380 --> 00:11:59,290
now looks a little bit more spaced out and easier to read.

186
00:12:00,370 --> 00:12:01,990
That's pretty much the canvas done.

187
00:12:02,230 --> 00:12:04,840
The last thing we need to add are the two buttons.

188
00:12:05,170 --> 00:12:09,910
So we'll add a true button and a false button,

189
00:12:09,970 --> 00:12:14,970
both created from the button class and neither of them are going to have to any

190
00:12:15,580 --> 00:12:16,413
text.

191
00:12:16,420 --> 00:12:21,420
Instead we're going to create a true_image from the PhotoImage class,

192
00:12:22,420 --> 00:12:25,750
tapping into the file that comes from this folder.

193
00:12:26,230 --> 00:12:30,460
So it's images/true.png.

194
00:12:31,060 --> 00:12:34,990
And that is the image that we're going to slot in to this button.

195
00:12:35,850 --> 00:12:36,683
Right.

196
00:12:38,130 --> 00:12:39,090
Now. In addition,

197
00:12:39,090 --> 00:12:43,680
I'm going to set the highlightthickness to zero. This way

198
00:12:43,680 --> 00:12:46,620
it doesn't have that weird, awkward border around the button.

199
00:12:47,430 --> 00:12:52,430
And I'm also going to put this button onto the screen using the grid method.

200
00:12:53,220 --> 00:12:55,830
So now we're onto a row two,

201
00:12:55,950 --> 00:12:59,580
so below the canvas, and the column

202
00:12:59,610 --> 00:13:04,140
we're going to set it to column zero. Now, depending on what you prefer,

203
00:13:04,140 --> 00:13:07,620
you can have the true button on the left or the true button on the right.

204
00:13:07,890 --> 00:13:11,070
It doesn't really matter cause it's so big and it's quite easy for the user to

205
00:13:11,070 --> 00:13:15,060
see. I'm going to set it on the left, so column zero.

206
00:13:15,270 --> 00:13:20,220
And when I run this code, you can see that show up. So using the same method,

207
00:13:20,250 --> 00:13:22,350
I'm going to create the false button.

208
00:13:28,200 --> 00:13:28,950
Right.

209
00:13:28,950 --> 00:13:33,870
Now we have a true and a false button and we pretty much have our final user

210
00:13:33,870 --> 00:13:36,930
interface completed. Now,

211
00:13:37,080 --> 00:13:41,250
one of the things you see here is almost everything that I've created inside my

212
00:13:41,250 --> 00:13:42,030
init,

213
00:13:42,030 --> 00:13:47,030
I've given it the 'self.' And remember from our lessons on OOP, this turns

214
00:13:48,270 --> 00:13:52,590
this into a property which can be accessed anywhere in the class.

215
00:13:53,130 --> 00:13:57,060
Now, some other things like the true image and the false image,

216
00:13:57,360 --> 00:14:01,050
I've not done that for because we're not going to use it anywhere else

217
00:14:01,290 --> 00:14:04,050
other than to set up our button right here.

218
00:14:04,770 --> 00:14:09,180
This, hopefully, will be a good review of what we learned about Object Oriented

219
00:14:09,180 --> 00:14:14,180
Programming and classes and objects as well as tkinter and all of the new stuff

220
00:14:14,820 --> 00:14:18,660
around APIs. But this completes the user interface

221
00:14:18,930 --> 00:14:21,210
and we're now ready to head over to the next lesson

222
00:14:21,310 --> 00:14:24,840
where we're going to fill some text into this question area.

223
00:14:25,140 --> 00:14:27,810
So for all of that and more, I'll see you there.

