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.
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.
Add the following set of menu items and key bindings to your program.
- Write a method that clears all the data points. It should have
def clearData(self, event=None):
- Bind the method to Cntl-N (Control-n). You should model this after the binding of Cntl-Q to handleQuit
- 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.
- Test it. I suggest creating a bunch of random points, clearing them, and then making sure you can create more points.
- Write a method that clears all the data points. It should have the signature
- Add new capabilities to the mouse.
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 + diff, loc + diff, loc + diff, loc + diff )
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 )
- 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.
Create a list panel in the main window. Add this code to the buildControls function.
- 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.
- Insert two entries into the listbox: Random, and Gaussian. Set one of them to be active by default by using the selection_set method.
- 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.
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.
Create a dialog window that will allow the user to choose how many points to create.
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.
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.
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.
- Write a getNumPoints accessor method that returns the number of points the user wants to plot.
- 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.
- 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.
- 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
- 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.
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.
- Make it so the button-2 movement adjusts the size of the ovals. Movement up the window should increase the size (e.g. moving the mouse 10% of the way up the screen increases the size by 1 pixel). Recall that in your button-1 motion handler, you kept track of incremental changes. In this case, you want to keep track of how much the mouse has moved since you first clicked it. Why? If you keep track of incremental changes, you run the risk of having each movement result in no change in size. I suggest you add a field that keeps track of the original size.
- Add a list box with different shapes that lets you control the shape drawn for each data point.
- Get the mouse-over to report the position of the data point underneath it. Either display it in the terminal (less prefered) or in a status area (a new frame below the canvas).
- Make it so clicking Shift-Cntl-mouse-button-1 will delete the point underneath it.
- Make it so the number of points dialog remembers the last value entered.
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.
- Write a brief summary of your project that describes the purpose, the task, and your solution to it. The summary should be 200 words or less. This should be a paragrpah that is separate from the rest of your report.
- Describe the functionality of your GUI. Include two screen shots: one showing at least 100 points in a random distribution, the second showing at least 100 points in a Gaussian distribution. They should look different.
- Include pictures and descriptions of any extensions, making clear how they are extensions to the project.
- What concepts you learned in class helped you to complete the project? What part of the project was most interesting to you?
- Acknowledge any sources, collaborators, or people who helped you complete the project.
Once you have written up your assignment, give the page the label:
Turn in your code by putting it into a project1 directory in your Private directory on the Courses server.