So now that our program works for text-based questions,
it's time to upgrade this program so that it actually has a graphical user
interface. You'll notice that in the project folder,
there is a file called ui.py,
and this is pretty much empty at the moment, other than a single constant
which sets the theme color. Now,
what we're trying to create is something that looks like this.
So we have some sort of a score board label,
we have a canvas with a white background,
and then on the canvas,
we've got some text in the middle and then we've got a true button and a false
button each using the true and false images from this folder.
Now we've seen how we can create graphical user interfaces using tkinter.
But in this case, we're going to do something a little bit different.
We're going to create our user interface in tkinter
but we're going to do it inside a class.
If you remember from day 17 where this project comes from,
that was when we learned about Object Oriented Programming,
and we learned about how to separate out our concerns like
the part that fetches the data or the part that model each of the questions
or the part that handles the actual quiz functionality,
and they are all separated in that own class. In order to create our user
interface
we're also going to create our very own class inside this ui.py.
So it's been a while since we've created classes.
So I want to do a quick reminder session on how to do that.
So we use the class keyword and then we provide the name of the class. Now,
in my case, I'm going to call this the QuizInterface.
And remember that the way that you name classes is what this is called. Pascal
case. So a capital letter,
and then each word is not separated by any underscores or any dashes.
So this is our class
and then we're going to create our init inside this class.
Now, if any of this is unfamiliar to you,
then make sure that you haven't skipped the lessons on Object Oriented
Programming and creating and using classes in Python.
We covered that in day 17. So if you skipped that,
be sure to head back before you proceed, because otherwise, the rest of this
is not going to make any sense at all. In our class init,
we're going to create our user interface.
So we're going to create all the buttons and all of the layouts.
And we start off by simply creating our window. Now this time though,
instead of creating our window as just a simple variable,
we're going to make it a property of this class.
So we're going to add self in front of it.
And then we're going to create it from the tkinter module.
So let's go ahead and import everything from the tkinter module,
and let's set our window as a new object from the tk class.
Next, I'm going to change some aspects of the window. So for example,
I'm going to change the title. So I'm going to set it to, um,
Quizzler which is going to be the name of this app.
We can continued like this inside the init function,
which remember, is going to be called whenever we create a new object from this
class.
This is where we're also going to have our self.window.mainloop
and that will set our program to run. In order to test this,
we're going to go back to our main.py
and we're going to import that class.
So from ui import the QuizInterface,
and then we're going to create that QuizInterface down here.
I'm going to call it the quiz_ui
and it's going to be initialized from our quiz interface. Now,
if you remember, when we first learned about
tkinter I told you that tkinter works by having this endless loop
essentially that's called the main loop.
And this is a bit like a never ending while loop.
It's constantly checking to see if it needs to update something in the graphical
user interface, or if the user has interacted with it in some sort of way.
So it will get confused if you have another while loop somewhere near it.
So we actually have to comment out this while loop
if we want this user interface to work properly. Now,
whenever we want to test out our UI,
all we have to do is to run our main.py.
And once it hits this line,
it's going to create a new object from our QuizInterface over here.
And once it does that, it's going to hit the init method
and it's going to create our new GUI.
You can continue coding up your user interface inside this init
and it will work just as it did before
when you created just a very simple tkinter graphical user interface
application. Here comes the challenging part.
I want you to create this particular user interface,
and I've created a slide that shows you all of the padding, the sizing,
the fonts, the background color,
and all of the components that go onto this screen.
I want you to pause the video here and see if you can create this user interface
inside the init method of the quiz interface class.
Pause the video now and give that a go.
Okay.
All right.
So we're going to continue where we left off here inside the init method.
And we already started off our window property
so we're going to continue making that a little bit better.
So we're going to call the config method on this self.window,
and we're going to change the padx and the pady so that we add 20 pixels
of padding on all four sites. Now,
the next thing I want to configure is the background color.
And as I mentioned in this slide,
we're going to use this theme color that I've already provided here,
which is a nice sort of midnight blue.
So I'm going to tap into the background attribute
and I'm going to set it to this theme color. Now,
if we go ahead and run our main file,
then we can see our user interface show up.
And it's now got the background color
and it's a little bit bigger because of the padding.
The next step is to create this label up here,
which is just a very simple text label.
So let's go ahead and create it as a property of this class.
So I'm going to call it the score label,
and I'm going to create it from the label class of tkinter. I'm going to set
the text as just Score: 0.
And I'm going to change the color of this text,
which is the foreground, to white.
That way it will actually be able to show up on this dark background.
Now, in order for this label to show up, we,
of course, have to give it some sort of layout.
So we're going to use the grid and I'm going to set the row to zero
cause it's the very top, and then the column to one.
So this way it sticks to the right. At this point when we run it,
because there is no column zero,
it's still going to be just showing up on the screen on the left.
And it's also got a white background making it impossible to see the white text.
Let's go ahead and change the background of this label to the same theme color.
And that way, when we run it, we actually see our label with its white text.
That's the label done. And the next step is to create our canvas.
Remember that our canvas is really useful because it allows us to layer lots of
things on top of it.
And we can also set its background and size very easily.
So we're going to create this canvas from the canvas class and straight away,
I'm going to set the width and height.
So the width is going to be 300 and the height is going to be 250
as we specified in the slide here. In addition,
I'm going to change the background color to white so that it stands out almost
like a card on the background. After I've created my canvas,
I'm going to add some question_text onto the canvas.
So I'm going to create this as the self.canvas.create_text
and this is going to have a single attribute called text,
and I'm going to set the text to say Some Question Text.
So we don't actually have that at the moment. We'll just put in a placeholder.
And if you want to, you can actually change the fill color of this text
which is the color of the text to the theme color as well
just to make it all matchy matchy. Now, finally,
we're going to put our canvas onto the screen by giving it a grid.
And this time we're going to set the row to one, so below the score label,
and then the column I'm going to set it to zero.
But then I'm going to set the column span to two so that it starts at column
zero and spans two columns to column one.
So now when I run this code,
I actually get an error and the error is super obscure.
Tells me tuple index out of range. Now, if you remember
when we worked with tkinter,
whenever we add an image or we add something to the canvas,
we always have to provide a position as the first two arguments.
And that is the tuple that represents the X and Y position of the text.
We have to make sure that we don't forget that.
So the first value is going to be the X,
so that's going to be half of the width so that it's centered on the canvas.
So that's 150, half of 300.
And then the Y is going to be half of 250, which is going to be 125.
Now, if we run this again,
hopefully we won't encounter the same error and you can now see this card show
up and our label show up on the right,
because its in the second column and this is the first column and this canvas
spans both columns. Now this text is a little bit small,
so let's take a look at the slide and see what we need to change it to.
So we want it to be arial font, 20 point size and italic.
Let's go ahead and add that to our text. And because its getting a little bit
crammed in here,
I'm actually going to put this on two separate lines just so I can read it a
little bit easier like this.
Now we can add that font argument and we're going to set it to a tuple with the
first item being the name of the font
and then the second is the size of the font
and finally we'll also make the text italic as well.
So now lets run that again and you'll see the text is now much bigger and its been
formatted in the way that we wanted it to. The next thing we want to do is to
add a bit of padding between this canvas and the score.
So we want to add 50 points of padding here and probably 50 here as well
just to push everything a little bit further away from each other. To do that
all we have to do is the tap into the canvas grid and then add a single pady.
We don't want to add any more padding on the X axis.
The original 20 is enough from the window. But we want to add 50
so that this way the canvas has some padding on the top and the bottom like this. And it
now looks a little bit more spaced out and easier to read.
That's pretty much the canvas done.
The last thing we need to add are the two buttons.
So we'll add a true button and a false button,
both created from the button class and neither of them are going to have to any
text.
Instead we're going to create a true_image from the PhotoImage class,
tapping into the file that comes from this folder.
So it's images/true.png.
And that is the image that we're going to slot in to this button.
Right.
Now. In addition,
I'm going to set the highlightthickness to zero. This way
it doesn't have that weird, awkward border around the button.
And I'm also going to put this button onto the screen using the grid method.
So now we're onto a row two,
so below the canvas, and the column
we're going to set it to column zero. Now, depending on what you prefer,
you can have the true button on the left or the true button on the right.
It doesn't really matter cause it's so big and it's quite easy for the user to
see. I'm going to set it on the left, so column zero.
And when I run this code, you can see that show up. So using the same method,
I'm going to create the false button.
Right.
Now we have a true and a false button and we pretty much have our final user
interface completed. Now,
one of the things you see here is almost everything that I've created inside my
init,
I've given it the 'self.' And remember from our lessons on OOP, this turns
this into a property which can be accessed anywhere in the class.
Now, some other things like the true image and the false image,
I've not done that for because we're not going to use it anywhere else
other than to set up our button right here.
This, hopefully, will be a good review of what we learned about Object Oriented
Programming and classes and objects as well as tkinter and all of the new stuff
around APIs. But this completes the user interface
and we're now ready to head over to the next lesson
where we're going to fill some text into this question area.
So for all of that and more, I'll see you there.