Title image Spring 2018

GUI Design

Due midnight, Monday 19 February 2018

The purpose of this assignment is to start the process of building an interactive visualization system. In the project, you will be adding menus, mouse callbacks, listboxes, and a dialog window to your program.


Tasks

The skeleton program from lab gives you a structure for making an application. The GUI pieces of this project give you practice creating other types of GUI elements like buttons, lists, labels, popup menus and modal dialog windows.

  1. Add the following set of menu items and key bindings to your program.

    1. Write a method that clears all the data points. It should have the signature
          def clearData(self, event=None):
    2. Bind the method to Cntl-N (Control-n). You should model this after the binding of Cntl-Q to handleQuit
    3. Bind the method to a new item in the File menu. You should model this after the binding of handleQuit to the Quit item in the File menu.
    4. Test it. I suggest creating a bunch of random points, clearing them, and then making sure you can create more points.

  2. Add new capabilities to the mouse.

    1. In the method that handles the motion of button 1, add code to update the location of all objects in the self.objects list. You can get the current coordinates of a graphical object using the coords method.
          loc = self.canvas.coords(obj)

      and you can modify the coordinates of a graphic object using the same function with arguments.

          self.canvas.coords( obj, 
                              loc[0] + diff[0], 
                              loc[1] + diff[1], 
                              loc[2] + diff[0],
                              loc[3] + diff[1] )
    2. In the method that handles your second mouse button (use Control-Button-1), add code to create a circular object at the mouse click location and store the object in the self.objects list (created and initialized in the init function).
          dx = 3
          rgb = "#%02x%02x%02x" % (random.randint(0, 255), 
                                   random.randint(0, 255), 
                                   random.randint(0, 255) )
          oval = self.canvas.create_oval( event.x - dx,
                                          event.y - dx, 
                                          event.x + dx, 
                                          event.y + dx,
                                          fill = rgb,
                                          outline='')
          self.objects.append( oval )

  3. Create a list panel in the main window. Add this code to the buildControls function.

    1. Look on the effbot list box documentation for examples of how to create a Listbox, insert elements, and access user selections. Use the height option to control the number of visible elements when you create the listbox. It's also usually helpful to set the exportselection option to 0. Pack it into the right control frame.
    2. Insert two entries into the listbox: Random, and Gaussian. Set one of them to be active by default by using the selection_set method.
    3. In your createRandomDataPoints function, use the Listbox curselection function to get the user's selection and print it to the Terminal. Looking at how it is being printed to the terminal, figure out how to get the index of the first selection from the return value of curselection. Make sure you handle the case where there is no selection, just in case.
    4. In createRandomDataPoints, update your loop where you create the points. If the user selected the Random distribution, use random.random() or random.randint() to generate the (x, y) locations for the points. Note that you can use the winfo_height and winfo_width to get the size of the Canvas widget if you want the points to fill the window.

      To generate points using a Gaussian distribution, use random.gauss(). It takes a mean (e.g. the middle of your window) and a standard deviation (e.g. 1/6 or 1/8 of the width of the window). If it is working correctly, the points should clump towards the middle of the screen.

  4. Create a dialog window that will allow the user to choose how many points to create.

    1. Visit the effbot dialog window page and read about dialog boxes. Copy the code for the dialog support class into display.py. Note that the code is written under the assumption that you wrote from TkInter import *. We use import TkInter as tk, so put tk. wherever you need it.
    2. Make a new class derived from Dialog to create a modal dialog that allows the user to enter text into an Entry widget indicating the number of points to create. The Dialog should also have a Cancel and an Ok button. The Cancel and Ok buttons are generated automatically by the parent Dialog class. You will need to override the body method and create the Entry widget there, using a StringVar object to handle setting and getting the text from the Entry widget.

      See if you can put a default number of points into the field and use the select_range and focus_set methods to highlight the text.

    3. A modal dialog is a standard method of obtaining information from the user. In some cases, the dialog needs inputs when it is created in order to present the correct options to the user. Such information should be passed into the __init__ method as parameters when creating the Dialog object. In this case, you may want to pass in a minimum and maximum number of points.

      When the modal dialog has been dismissed by the user, you need to be able to access the information provided by the user, or handle a Cancel button press. The validate method is used by the Dialog class to determine if the information provided by the user is sufficient and valid. If the function returns True, then the dialog will close and return, otherwise it will stay active. Ideally, you should tell the user something is wrong. You should override the validate method in your child Dialog class so that it tests if the value the user entered is a number and in the proper range.

      The apply method is used by the parent Dialog class to extract any information from the dialog fields before returning to the user. The apply method is called only if the validate method has returned True, indicating that the information is valid. The apply method is not called if the user selected Cancel.

      Write validate and apply methods in your child dialog class. In the apply method, grab the string from the Entry widget and convert it to an int, then store it in a field (e.g. self.numPoints). You may also want to use it to write to a field that indicates the user did not hit Cancel.

    4. Write a getNumPoints accessor method that returns the number of points the user wants to plot.
    5. Write a userCancelled method that returns True if the user hit the Cancel button. It may be easier to think of it as returning False if the user hit Ok, which means the apply method was called.
    6. Update your createRandomDataPoints so that it creates the number entry dialog, responds properly to a Cancel signal, and plots the number of data points provided by the user. You call the dialog by creating a new entry dialog object and assigning it to a local variable. You can then user that local variable to call userCancelled or getNumPoints, as needed, after the dialog has closed.

  5. Test your application and make sure everything is working properly. There should be a noticeable difference between plotting points using a Random distribution and a Gaussian distribution.

Extensions

The following are some suggested extensions. In general, extensions are a way for your to explore additional facets of the project that are of interest to you. Pick something you want to explore more deeply. You are not restricted to the items below, they are only suggestions.


Report

Make a wiki page for your project report. The wiki page should be in your personal space. If you want to be nice and organized, create a CS 251 projects page and link them all from there.


Handin

Once you have written up your assignment, give the page the label:

cs251s18project1

Turn in your code by putting it into a project1 directory in your Private directory on the Courses server.