GUI Basics
This page lists the milestone requirements for Milestone 6 of the CC 410 Restaurant Project. Read the requirements carefully and discuss any questions with the instructors or TAs.
Purpose
The CC 410 Restaurant Project project for this semester is centered around building a point of sale (POS) system for a fictional restaurant named Game Grub, offering food of all kinds to celebrate our love of games of all kinds.
The sixth milestone involves creating the various GUI windows and panels required for this project. The next milestone will involve adding functionality to these GUI elements beyond the ability to load different panels into the main window area.
General Requirements
Assignment Requirements
This milestone should include the following features:
New Packages
- A
gamegrub.gui
package to store all GUI code. - A
gamegrub.gui.entrees
package to store all GUI panels for entrees. - A
gamegrub.gui.sides
package to store all GUI panels for sides. - A
gamegrub.gui.drinks
package to store all GUI panels for drinks.
Main GUI Classes
- A
gamegrub.Main
class that properly loads and displays the program’s GUI. - A
gamegrub.gui.PrimaryWindow
class that represents the main GUI window.- It should contain two panels - a main panel and a sidebar panel.
- It should also contain two methods: one to load a particular panel into the main panel, and another to load the order screen into the main panel.
- A
gamegrub.gui.MenuPanel
class to represent the main ordering screen panel.- It should contain three panels of buttons, one each for entrees, sides, and drinks. They may be automatically generated from the menu.
- Each entree will be listed once, but each side and drink will have three buttons - one for each size.
- When clicked, those buttons should call a method to load the appropriate panel into the main panel to allow customization of the menu item.
- A
gamegrub.gui.OrderPanel
class to represent the sidebar panel containing a user’s order.- It should contain labels for order number, subtotal, tax, and total.
- It should contain an Edit button that does nothing when clicked.
- It should also include a list box as a placeholder that can be used to keep track of the order. The list box should expand to fill all remaining vertical space in the window.
Menu Item Panels
- A panel class in the
gamegrub.gui.entrees
package for each entree.- It should include appropriate controls for modifying the ingredients, base, and toppings.
- You may want to include a parent
EntreePanel
class to reduce the amount of duplicate code.
- A SINGLE panel class
SidePanel
in thegamegrub.gui.sides
package.- It should include appropriate controls for modifying the size of the item.
- Since each side only has a single option, this panel will be generalized to work with the parent
Side
class instead of individual sides themselves. When the buttons on the menu are clicked, you’ll need to make sure an instance of the correct menu item is created.
- A panel class in the
gamegrub.gui.drinks
package for each drink item.- It should include appropriate controls for modifying the ingredients and size.
- You may with include a parent
DrinkPanel
class to reduce the amount of duplicate code.
Each of the menu item panels should also implement the following functionality:
- When given an instance of the item as a parameter to the constructor, the values of the controls should be set to match the values in the instance.
- It should include a Save button that, when clicked, will replace the main panel with the order screen. We will handle actually saving the item in a future milestone.
Documentation and Tests
- Classes in the
gamegrub.gui
package do require all appropriate documentation comments, and must be free of style errors. Every method must include full documentation comments. - Classes in the
gamegrub.gui
package do not require unit tests at this time. - Classes in the
gamegrub.gui
package do not require type hints in Python, though you may continue to use them if they are helpful. Any errors from Mypy originating in these classes will be ignored. - Create a new UML diagram that shows the structure of the
gamegrub.gui
package and how all GUI classes are related. You should also show any links to the classes in thegamegrub.data
package, but you may choose to show simplified links between packages instead of individual classes. You do not have to include full details from classes in thegamegrub.data
packages.- For example, you can show that the classes in the
gamegrub.gui.entrees
package are all related to similar classes in thegamegrub.data.entrees
package without listing the individual classes in that package.
- For example, you can show that the classes in the
Other Instructions
You are welcome to add additional methods to the existing content in the gamegrub.data
package. If so, make sure you include appropriate documentation, type checking and unit tests.
See below for a few sketches of what your GUI might look like.
You are encouraged to use the code from Example 6 as a basis for this GUI, or you may create a different design. There are no set requirements for the design other than what is listed above, and the overall focus in this milestone is on simply getting the content on the screen and the ability to move between the various panels. You are welcome to spend additional time on the design if desired, but focus on getting the the content on the screen before doing any design work.
Time Requirements
Completing this project is estimated to require 3-8 hours.
A rough estimate for this milestone would be around 1500 lines of new code. It could vary widely based on how you choose to implement the various portions of the GUI. I was able to reuse many portions of the example project and expand on them to build this milestone. -Russ
Grading Rubric
This assignment will be graded based on the rubric below:
Main
class - 2%PrimaryWindow
class - 4%OrderPanel
class - 4%MenuPanel
class - 20%- Entree Panel classes - 40%
SidePanel
class - 5%- Drink Panel classes - 15%
- Updated UML diagram - 10%
The following deductions apply:
- Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.
- Any portion of the project which does not meet the general requirements listed above will have a commensurate amount of points deducted.
This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.
As part of the grading of all assignments in this course, I will be doing a deep dive into a few classes in your code. This will include leaving detailed comments on code style and format in GitHub. I will usually choose various classes to review at random, and any issues found in that class will be verified in other classes of the same type. For any GUI portions, I’ll also be testing the functionality of the GUI for each class under review. - Russ
Submission
Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.
GUI Sketches
Below are some GUI sketches to help you visualize one possible GUI for a previous version of this project. You do not have to match this design at all, but this is at least a good starting point that you can reach based on what you learned in Example 6.
I chose to increase the default size of my GUI to 1024x740 pixels, as that made the buttons fit better into the window. - Russ
Main Window
Entree Panel
Side Panel
Drink Panel
Helpful Hints & Code
Here are a couple of helpful pieces of code that you may wish to use in your project.
Java
In many cases, I found it easiest to create private or protected methods that will construct my GridBagConstraints
objects, either within the class I was working in or in a parent class in the case of entree and drink panels. Here’s an example:
/**
* Construct a GridBagConstraints object.
*
* @param y the y coordinate of the object
* @param start set anchor to LINE_START
* @return the constructed GridBagConstraints object
*/
protected GridBagConstraints makeGbc(int y, boolean start) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = y;
if (start) {
gbc.anchor = GridBagConstraints.LINE_START;
}
gbc.insets = new Insets(2, 2, 2, 2);
return gbc;
}
Then, when I want to place something in my layout using a GridBagConstraints
object build from this method, I can use this:
this.add(check, this.makeGbc(i++, true));
The biggest benefit to this approach is that I can easily adjust all of the buttons by changing this one method. This is a great example of the “Don’t Repeat Yourself” or DRY principle.
Python
In many cases, I found it helpful to create a dictionary of settings that I’d like use with the grid()
method, and then pass that entire dictionary as keyword arguments to the method. I usually did this either in a helper method within the class itself, or in a parent class in the case of pizza and drink panels. Here’s an example:
def _grid_dict(self, row: int, sticky: str) -> Mapping[str, Any]:
"""Create a dictionary of settings.
Args:
row: the row for the item
sticky: the sticky settings
"""
settings: Dict[str, Union[str, int]] = dict()
settings["row"] = row
settings["column"] = 1
settings["padx"] = 2
settings["pady"] = 2
settings["sticky"] = sticky
return settings
Then, when I want to place something in my layout using the grid()
method with these settings, I can use this:
checkbutton.grid(**self._grid_dict(i, "W"))
Notice that I have to place two asterisks **
before the method. That tells Python to “unpack” the dictionary returned by that method so that it can be read as individual keyword parameters. A deeper explanation is found here.
The biggest benefit to this approach is that I can easily adjust all of the buttons by changing this one method. This is a great example of the “Don’t Repeat Yourself” or DRY principle.
I also had to tell Mypy to ignore the lambda expressions used in the MenuPanel
class, as it cannot properly determine the type of the lambda. You can do this by adding a # type: ignore
comment at the end of the offending line.
button = Button(master=side_frame, text=str(side),
command=lambda x=str(side): # type: ignore
self.action_performed(x))
If anyone is able to figure out exactly how to properly get Mypy to handle this, there are major Bug Bounty points available!