Event-Driven Programming

In this example project, we’ll go through the steps of modifying our previously created GUI and adding in the code to handle events generated by users clicking on various buttons. This will allow us to construct an order of several sundaes within our GUI.

We’ll also add some simple unit tests to verify that our GUI is correctly populated by the object it is given, and that it correctly updates the object when saving it.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Event-Driven Programming

Assignment Requirements

This page lists the example project requirements for Example 7B in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing GUI to handle events, as well as adding some unit tests for our GUI panels.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests for TheChocoPanel and TheClassicPanel are required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following new GUI features:

  • Changes to the previous milestone:
    • Add a “Cancel” button to each of the sundae panels. (This is included in the starter)
    • Add a “Delete” button to the SidebarPanel panel. (This is included in the starter)
    • Switch the list box in the SidebarPanel to a tree element (Java JTree or tkinter Treeview).

Once the entire project is working, you should observe the following behavior on the new tree element.

  • When an item is added to the tree element in the SidebarPanel, the following should happen:
    • The item’s string representation should be a top-level node in the tree.
    • Any special instructions for that item should be represented as child nodes of the top-level node.
    • The item should be shown fully expanded by default.
    • The tree element should only allow the user to select one item at a time.

In addition, the following events should be implemented in the GUI:

  • When the “Save” button in any of the sundae panels is clicked, the following should happen:
    • The item currently represented by the panel should be updated such that each attribute matches the current status of the associated GUI element.
    • The item should be placed into the tree in the SidebarPanel if it is a new item, or the item should be updated if it is being edited.
    • The main panel in MainWindow should be replaced with the OrderScreen (This was part of the previous milestone).
  • When the “Cancel” button in any of the sundae panels is clicked, the following should happen:
    • If an item is being edited, any changes made in the GUI should be discarded (the item should not be changed).
    • The main panel in MainWindow should be replaced with the OrderScreen (This was part of the previous milestone).
  • When the “Edit” button in the SidebarPanel is clicked, the following should happen:
    • The OrderItem that is currently selected should be determined. If the selection is an ingredient of that item, the code should work upwards in the tree to find the OrderItem.
    • The appropriate sundae panel should be loaded into the main panel in MainWindow and populated with the current status of the item (Most of this should work from the previous milestone).
    • If the item is saved via the “Save” button, it’s entry in the tree element should be updated without changing the order of the items in the tree.
    • If the changes are cancelled via the “Cancel” button, no changes should be made.
  • When the “Delete” button in the SidebarPanel is clicked, the following should happen:
    • The OrderItem that is currently selected should be determined. If the selection is an ingredient of that item, the code should work upwards in the tree to find the OrderItem.
    • That item should be removed from the tree element and any other relevant data structures in the SidebarPanel class.

Unit tests should be added to the corresponding test package for the following classes:

  • Each sundae panel in starfleettreats.gui.sundaes

See below for a list of suggested unit tests. You should achieve at or near 100% coverage on these classes. We will not unit test the MainWindow, OrderPanel, or SidebarPanel classes in this milestone.

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Tree element displays order items correctly: 10%
  • “Save” buttons work properly for all items: 20%
  • “Cancel” buttons work properly for all items: 10%
  • “Edit” button works properly for all items: 20%
  • “Delete” button works properly for all items: 10%
  • Unit Tests: 30%

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.

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.

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.




Sundae Panels

Each sundae panel test class should contain unit tests for the following:

  • testDefaultConstructor() - create the panel without providing an existing element, and assert that it creates a new instance of the correct item.
  • testBadActionCommand() - call the actionPerformed() method with a bad action command, and assert that an exception is not thrown.
  • testContainerComboBox(Container) - instantiate a panel with an existing item, change the value of the container combo box in the GUI to the Container value, and fire a “save” action, then verify that the item has the correct container value.
  • testContainerComboBoxSetCorrectly(Container) - instantiate a panel with an existing item using the given Container, and assert that the Container combo box is set to the correct value.
  • test<Ingredient>CheckBox() - instantiate a panel with an existing item, change the value of the ingredient check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • test<Ingredient>CheckBoxSetCorrectly() - instantiate a panel with an existing item with a given value for ingredient, and assert that the ingredient checkbox is set to the correct value. Do this for both true and false.
  • testToppingCheckBox(Topping) - instantiate a panel with an existing item, change the value of the topping check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • testToppingCheckBoxSetCorrectly(Topping) - instantiate a panel with an existing item with a given value for topping, and assert that the topping checkbox is set to the correct value. Do this for both true and false.
  • testCancelButton() - instantiate a panel with an existing item, change several values in the GUI, and fire a “cancel” action, then assert that the item is unchanged from its previous state.
Update Permissions

To allow proper unit testing, you may need to relax the permissions on several elements inside of your GUI classes. I recommend using package-private in Java, with no modifier - see this document. Then, any unit tests that are in the same package can have access to those members. For Python, switching from double underscore private attributes to single underscore protected attributes is sufficient.

This has already been done on the item attribute in the sundae panels.

Java

Part 1
YouTube Video
Part 2
YouTube Video

Gradle Changes

The build.gradle file includes the library for JUnit5 parameterized tests:

dependencies {
    // Use JUnit Jupiter API for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2', 'org.hamcrest:hamcrest:2.2', 'org.junit.jupiter:junit-jupiter-params'

    // Use JUnit Jupiter Engine for testing.
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:29.0-jre'
}

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Handle Save Events

  3. Handle Cancel Events

  4. Switch Listbox to Tree in Sidebar

  5. Populate Tree with Elements

  6. Handle Edit Events

  7. Handle Delete Events

  8. Create Unit Tests

  9. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

Part 1
YouTube Video
Part 2
YouTube Video

Tox Changes

The tox.ini file has been updated to allow testing with a display, and will now run the full unit test suite.

[testenv]
deps = -rrequirements.txt
ignore_errors = True
passenv = DISPLAY
commands = python3 -m mypy -p src --html-report reports/mypy
           python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage
           python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Handle Save Events

  3. Handle Cancel Events

  4. Switch Listbox to Tree in Sidebar

  5. Populate Tree with Elements

  6. Handle Edit Events

  7. Handle Delete Events

  8. Create Unit Tests

  9. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.