In this lab, you'll be creating a Graphical User Interface (also known as a GUI) for the game of Pig.
To build the user interface, we'll be using Tkinter, which is part of Python's standard library. (Tkinter itself is a Python binding for Tk, which is an open source, cross-platform toolkit for creating graphical user interfaces)
Create a new file (say, my_ui.py), and enter in the following program:
from tkinter import * root = Frame() root.mainloop()
In the first line, we're importing the tkinter library.
Next, we instantiate a Frame object - this object will serve as the main
window of our UI.
Finally, to transfer control to the UI, we call the mainloop method
on the root. Run it, and you'll see that a (blank) window comes up.
Not a very interesting window, I know. Patience, young grasshopper...
Unlike the Python programs we've been programming so far in 61A, UI programs aren't structured in terms of function/method calls. UI programs are instead structured around events that the user triggers through various means (mouse, keyboard, sound, etc). For instance, when you click a button in an application, that button object is 'woken up' by the UI, and the button then performs its relevant action (This is a bit reminiscent of Constraint Networks). The User Interface is constantly monitoring the window, so that whenever the user performs some action (a mouse click, or a keyboard stroke), the appropriate entities are notified.
As a concrete example, let's set up a simple example involving a Button. Modify your code in my_ui.py to be the following:
from tkinter import * def handle_button_push(): print("Button pushed.") root = Frame() root.grid() root.rowconfigure(0, pad=200) root.columnconfigure(0, pad=200) root.master.title("My First Button") button = Button(root, text="Press me!", command=handle_button_push) button.grid(row=0, column=0) root.mainloop()
Here, we see the interesting line: button = Button(root, text="Press me!", command=handle_button_push).
Here, we are creating a Button widget, whose parent is
root (i.e. main window), whose text is "Press me!", and whose callback function is
the handle_button_push function.
Some terminology:
Now, let's get started on building a GUI for the game of Pig!
Copy the provided skeleton framework into your current directory by doing:
cp -r ~cs61a/lib/lab/lab10 .
Note the . (period) as the third parameter to cp.
Take a second and read through the code - much of it should be understandable
to you at this point (i.e. the Pig game logic).
Try running it right now!
python3 pig_gui.pyYou'll notice that a simple GUI pops up:
...but nothing works. Here's where you come in!
Right now, the Roll/Hold Buttons don't do anything when you click them. This
is because they don't have any callbacks registered. Modify the
PigGUI.initialize_ui method to register each button with
handle_roll_click and handle_hold_click as callback functions. You
should consider using the provided register_callback function.
Then, fill in the body of handle_roll_click and handle_hold_click.
You should use the provided methods PigGUI.roll, PigGUI.hold.
You'll know you did this question correctly if the PigState is being printed out in the
terminal at each Button click.
Rolls and holds are now happening behind the scenes. However, these
changes aren't being reflected on the UI. Bummer!
Modify the PigGUI.roll and PigGUI.hold methods to also update the
Label widget that displays the game state information: self.state_label.
You'll want to use the provided set_widget_label function.
You might notice that we have some nice .gif images living inside of
lab10/images/:
Add the necessary modifications so that, on each dice roll, the die value is
reflected on the screen (i.e. rolling a 4 should result in images/die4.gif
being displayed). In addition, performing a 'hold' action should result
in images/hold.gif being displayed.
Tip: A good amount of the work has been done inside of PigGUI.set_dice_image.
Your job is to figure out how to use this nice function to achieve your goal.
At the moment, the end of a game is a rather lack-luster experience for the
winner - all that happens is an AssertionError is raised on the terminal. Let's do better!
Add functionality such that, as soon as one of the players wins, a MessageBox pops up
with a message saying which Player won, and two options: one to restart the game, and
another to exit:
To get the MessageBox to pop up, we'll be using the Tkinter askyesno provided function:
from tkinter import * from tkinter.messagebox import askyesno response = askyesno("Important Question", "Do you like CS 61A?") if response: print("Hooray, you like 61A!") else: print("Hm, we'll convince you yet!")Calling the askyesno function creates a Yes/No MessageBox, and returns True if the user clicked 'Yes', and False if the user clicked 'No'. Try it out!
At this point, we have a pretty complete UI for the Pig game - awesome! However,
as always, we can do a little better. It would be nice to be able to restart the game at
whim. For instance, you might want to start over if the current game is taking too long, or you
want to play against someone else, or you're losing too badly, etc...
Add the ability for a player to restart the current game - this will have you add a new Button
to the PigGUI, and a new callback.
Congratulations! You've created your first GUI Application!