Difference between revisions of "CSC111 Lab 8"

From dftwiki3
Jump to: navigation, search
(Obstacles)
 
(30 intermediate revisions by the same user not shown)
Line 17: Line 17:
 
<br />
 
<br />
  
 +
=Moving Balls in a Graphic Window=
 
==Moving Ball, Part 1==
 
==Moving Ball, Part 1==
  
Line 35: Line 36:
 
:Your program will have a structure close to this one (but you may be inspired to organize your program differently):
 
:Your program will have a structure close to this one (but you may be inspired to organize your program differently):
  
<code><pre>
+
<source lang="python">
 
#----------------------------------------------------------------
 
#----------------------------------------------------------------
 
def simul( c1, dx1, dy1, c2, dx2, dy2 ):
 
def simul( c1, dx1, dy1, c2, dx2, dy2 ):
Line 60: Line 61:
 
     ...
 
     ...
  
</pre></code>
+
</source>
  
 
==Moving Balls in a List==
 
==Moving Balls in a List==
Line 147: Line 148:
 
* Make your program ask the user for the number of balls he/she wants to see move around the window.
 
* Make your program ask the user for the number of balls he/she wants to see move around the window.
  
==Holes==
+
==Balls of different colors==
 +
 
 +
* The colors supported by the Zelle graphics library are listed in [[Tk_Color_Names|this document]].
 +
 
 +
* Figure out a way to make your program use different colors (at least 5) for the balls.
 +
 
 +
* Make your program robust, so that if the user requests to have 10 balls, but you have defined only 5 colors, the program will rotate through the colors.
 +
 
 +
: '''Hints''': remember the modulo operator %!  In particular, try this code in interactive python:
 +
 
 +
  >>> for i in range( 20 ):
 +
  >>> ...    print i % 7
 +
 
 +
==Pit Stop==
 +
[[Image:BallPitStop.png | right | 200px]]
 +
* Go back to the original program with only 1 ball, and no list, no colors.
  
 
* Add a 60x60 square somewhere in your window.
 
* Add a 60x60 square somewhere in your window.
  
* Modify your code so that when a ball falls '''completely''' in the square it stops.
+
* Modify your code so that when the ball falls '''completely''' in the square it stops.
 +
 
 +
* Once your program works, modify the last version of your program, with lists and colors, and add the stopping hole to it.
 +
 
 +
:'''Hints''': Be careful that when you take a ball out of the list, like so:
 +
 +
      c, dx, dy  = L[i]   
 +
 +
:and you modify either dx, or dy, it will not modify the dx or dy that are in the list.  Just the copy of them that you got out.  You have to store c, dx, and dy back in the list to "remember" the changed quantities:
 +
 
 +
      L[i] = [ c, dx, dy ]
 +
 
 +
=Exceptions=
 +
 
 +
==TypeError Exceptions==
 +
 
 +
The material for this section is covered in the book, in Section 7.4.
 +
 
 +
The code section below should remind you of a recent homework assignment.  Create a program called '''stats.py''' with this code:
 +
 
 +
<br />
 +
<br />
 +
<source lang="python">
 +
statistics = [['Afghanistan', 28710000, 'NA', 'NA', 1], ['Albania', 3580000,
 +
              12000, 'NA', 10], ['Algeria', 32810000, 180000, 'NA', 2],
 +
              ['Andorra', 69150, 24500, 'NA', 1], ['Angola', 10760000,
 +
              60000, 'NA', 1], ['Anguilla', 12738, 919, 'NA', 16],
 +
              ['Antigua_and_Barbuda', 67897, 5000, 'NA', 16], ['Argentina',
 +
              38740000, 4650000, 'NA', 33], ['Armenia', 3320000, 30000, 'NA',
 +
              9], ['Aruba', 70844, 24000, 'NA', 'NA'], ['Australia', 19730000,
 +
              13010000, 9020000, 571], ['Austria', 8180000, 4650000,
 +
              1300000, 37], ['Azerbaijan', 7830000, 25000, 'NA', 2],
 +
              ['Bahrain', 667238, 140200, 'NA', 1], ['Bangladesh',
 +
              138440000, 150000, 'NA', 10], ['Barbados', 277264, 6000, 'NA',
 +
              19], ['Belarus', 10330000, 422000, 'NA', 23], ['Belgium',
 +
              10280000, 4870000, 1600000, 61], ['Belize', 266440, 18000,
 +
              'NA', 2], ['Benin', 7040000, 25000, 'NA', 4], ['Bhutan',
 +
              2130000, 2500, 'NA', 'NA'], ['Bolivia', 8580000, 78000, 'NA', 9],
 +
              ['Bosnia_and_Herzegovian', 3980000, 45000, 'NA', 3],
 +
              ['Botswana', 1570000, 33000, 'NA', 11], ['Brazil', 182030000,
 +
              22320000, 10860000, 50], ['Brunei', 358098, 35000, 'NA', 2],
 +
              ['Bulgaria', 7530000, 1610000, 'NA', 200], ['Burkina_Faso',
 +
              13220000, 25000, 'NA', 1], ['Burma', 42510000, 10000, 'NA', 1],
 +
              ['Burundi', 6090000, 6000, 'NA', 1], ['Cambodia', 13120000,
 +
              10000, 'NA', 2], ['Cameroon', 15740000, 45000, 'NA', 1] ]
 +
 
 +
def main():
 +
    for country, pop, users, active, isp in statistics:
 +
        print "%30s (%d habitants)" % (country, pop ),
 +
        print "Users=", users, " Active=", active, " ISP=", isp
 +
 
 +
 
 +
main()
 +
 
 +
</source>
 +
<br />
 +
<br />
 +
* Run the program.
 +
* Notice that the data are in their original form.  When a quantity was not known, the creators of the list used '''"NA"''' to indicate that it was '''N'''ot '''A'''vailable.
 +
 
 +
* Let's compute the total population of the (partial) list of countries in the list '''statistics'''. 
 +
 
 +
    count = 0
 +
    for country, pop, users, active, isp in statistics:
 +
          count += pop
 +
 
 +
:Go ahead and see if your get a total population of 618039669.
 +
 
 +
* Now, same idea: modify your program so that it computes as well the total number of Users.  Make it use the very same construct as shown above, but now for the quantity '''users'''.
 +
 
 +
* Run your program.
 +
 
 +
* Did you get an error of this type?
 +
 
 +
Traceback (most recent call last):
 +
  File "stats2.py", line 36, in <module>
 +
    main()
 +
  File "stats2.py", line 31, in main
 +
    userCount += users
 +
TypeError: unsupported operand type(s) for +=: 'int' and 'str'
 +
 
 +
:If so, why?  What made the program crash? 
 +
:Think hard!
 +
:Harder!
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<center>[[Image:FrogThinking.jpg | 200px]]</center>
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
:Do not move on until you know for sure why this error was generated.
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
 
 +
* I trust that if you are reading this, it is because you know what caused your program to crash.  The clue is '''unsupported operand type(s) for +=: 'int' and 'str' ''', indicating that we are trying to add ''' 'NA' ''' which is a string to '''count''', which is an integer.
 +
 
 +
:We could use an if statement to test whether '''users''' contains 'NA' or not, but we'll use another approach.  We will tell python to '''try''' adding '''users''' to the counting variable, and if there is an error of type '''TypeError''' (see the last ligne of the error message when the program crashed), then we won't add anything to the count variable; we'll just ''pass''.
 +
 
 +
:The main program becomes:
 +
 
 +
def main():
 +
    popCount = 0
 +
    userCount = 0
 +
    for country, pop, users, active, isp in statistics:
 +
        print "%30s (%d habitants)" % (country, pop ),
 +
        print "Users=", users, " Active=", active, " ISP=", isp
 +
        popCount += pop
 +
        <font color="magenta">'''try''':
 +
            userCount += users
 +
        '''except''' ''TypeError'':
 +
            # don't do anything
 +
            pass
 +
        </font>
 +
    print "total population = ", popCount
 +
    print "total users      = ", userCount
 +
 
 +
* Try it!
 +
* Use the same method to compute the total number of ISPs.
 +
 
 +
==Other Exceptions==
 +
 
 +
* Imagine that now the list '''statistics''' is defined as shown below, and that for some countries the ISP value (the last one) is missing:
 +
 
 +
<source lang="python">
 +
.
 +
statistics = [['Afghanistan', 28710000, 'NA', 'NA', 1], ['Albania', 3580000,
 +
                12000, 'NA', 10], ['Algeria', 32810000, 180000, 'NA'],
 +
                ['Andorra', 69150, 24500, 'NA', 1], ['Angola', 10760000,
 +
                60000, 'NA', 1], ['Anguilla', 12738, 919, 'NA', 16],
 +
                ['Antigua_and_Barbuda', 67897, 5000, 'NA', 16], ['Argentina',
 +
                38740000, 4650000, 'NA', 33], ['Armenia', 3320000, 30000, 'NA',
 +
                9], ['Aruba', 70844, 24000, 'NA' ], ['Australia', 19730000,
 +
                13010000, 9020000, 571], ['Austria', 8180000, 4650000,
 +
                1300000, 37], ['Azerbaijan', 7830000, 25000, 'NA'],
 +
                ['Bahrain', 667238, 140200, 'NA', 1], ['Bangladesh',
 +
                138440000, 150000, 'NA', 10], ['Barbados', 277264, 6000, 'NA',
 +
                19], ['Belarus', 10330000, 422000, 'NA', 23], ['Belgium',
 +
                10280000, 4870000, 1600000, 61], ['Belize', 266440, 18000,
 +
                'NA', 2], ['Benin', 7040000, 25000, 'NA', 4], ['Bhutan',
 +
                2130000, 2500, 'NA', 'NA'], ['Bolivia', 8580000, 78000, 'NA', 9],
 +
                ['Bosnia_and_Herzegovian', 3980000, 45000, 'NA', 3],
 +
                ['Botswana', 1570000, 33000, 'NA', 11], ['Brazil', 182030000,
 +
                22320000, 10860000, 50], ['Brunei', 358098, 35000, 'NA', 2],
 +
                ['Bulgaria', 7530000, 1610000, 'NA', 200], ['Burkina_Faso',
 +
                13220000, 25000, 'NA'], ['Burma', 42510000, 10000, 'NA', 1],
 +
                ['Burundi', 6090000, 6000, 'NA', 1], ['Cambodia', 13120000,
 +
              10000, 'NA', 2], ['Cameroon', 15740000, 45000, 'NA', 1] ]
 +
 
 +
.
 +
</source>
 +
 
 +
* Write a program (or copy/paste part of the previous one) that will read the list above, and output the total population (which should be the same as before).
 +
** For this you should write your program as if all the country lists contained 5 quantities.
 +
** Then you run your program.
 +
** You observe that it crashes when it tries to extract the 5th quantity from a country list.
 +
** You note the name of the error generated (which is on the last line, and usually of the form '''XxxxError''',  such as '''IndexError''', '''TypeError''', '''NameError''', '''ValueError''', etc.)
 +
** Add a '''try/except''' statement around the code that generates the error (Python refers to a run-time error that creates a crash as an ''exception''):
 +
 
 +
      try:
 +
          country, pop, users, active, isp = ''someVariableOfYours''
 +
      except '''XxxxError''':
 +
          country, pop, users, active = ''someVariableOfYours''
 +
 
 +
** Run your program again and observe that it doesn't crash and that it reports the correct information.
 +
 
 +
         
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
[[Category:CSC111]][[Category:Labs]][[Category:Python]]

Latest revision as of 10:01, 8 March 2015

This lab deals with the moving ball example we saw in class recently. Your work for today is to add new functionality similarly to the way we added functionality to the graphics example with buttons.

You should start the Mac in Mac OS mode for today (graphics is more easily done this way). Make sure you start the X11 utility first, then the Terminal window.



Fileicon-pdf.png
You may find the following document describing the different graphic objects introduce in the Zelle's textbook useful.


Moving Balls in a Graphic Window

Moving Ball, Part 1

  • Create a program called movingBall.py, which contains the version we ended up in class yesterday. It is available here.
  • make sure the graphics.py library is in the same directory where your program is located.
  • Run the program and make sure it works.
  • Clean up the testing of x and y coordinates of the center, in the function simul(), so that it is simpler. Use AND or OR to simplify the testing.
  • Verify that your ball still moves well. You may increase the number of simulation steps in simul to make the simulation go longer.

Moving Balls, Part 2

  • Add an extra ball to the simulation. The result should be two balls moving around the box and bouncing off the sides.
  • You will need to create a new circle, say c2 in main(), and pass it to the simul() function as well. But make sure simul() contains only one for-loop. The same for-loop should move both balls around.
Your program will have a structure close to this one (but you may be inspired to organize your program differently):
#----------------------------------------------------------------
def simul( c1, dx1, dy1, c2, dx2, dy2 ):

    for step in range( 2000 ):
         ...
         ...
         ...
 
#----------------------------------------------------------------
def main():
    c1 = Circle( Point( ..., ... ), 15 )
    c2 = Circle( Point( ..., ... ), 15 )
    ...

    waitForClick( win, "Click to Start" )
    dx1 = ...     # some random number
    dy1 = ...     # some random number
    dx2 = ...     # some random number
    dy2 = ...     # some random number

    simul( c1, dx1, dy1, c2, dx2, dy2 )

    ...

Moving Balls in a List

  • Once the program of the previous section works, put the two balls in a list L.
  • Try to use for-loops as much as possible. Use the program we wrote in class with buttons for ways of using a list of graphic objects. The code of this program is shown below. In particular see if you can
    • create the circles in a for-loop and append them to the list
    • use a for-loop inside the for-step-in-range( 2000 ) statement.



# clickMe0.py
# A demo program that draws a button on the graphics window
# and waits for the user to click the mouse 10 times, counting
# and displaying the count at every tick. 
from graphics import *

def drawButton( win, x1, y1, w, h, label ):
    """draws a rectangle with the top-left corner at x1, y1,
    with width w and height h on the screen.  Puts the label
    in the middle of the rectangle"""
    r = Rectangle( Point( x1, y1 ),
                   Point( x1+w, y1+h ) )
    r.draw( win )
    r.setFill( "red" )
    t = Text( Point( x1 + w / 2, y1 + h / 2 ), label )
    t.draw( win )

def isInside( userPoint, x, y, w, h ):
    mx = userPoint.getX()
    my = userPoint.getY()
    if x <= mx <= x+w and y <= my <= y+h:
        return True
    else:
        return False

def main():
    """Opens a graphics window 300x300 and draws a button on
    it.  Then wait for the user to click the mouse 10 times
    before exiting"""
    W = 300
    H = 300
    win = GraphWin( "Click Me!", W, H )
    border = 10

    #--- draw the button ---
    L = [ [ border, border, 70, 20, "Button1" ],
          [ W-border-70, border,70, 20, "Button 2" ],
          [ W-border-70, H-border-20, 70, 20, "Button 3" ] ]

    for x, y, w, h, label in L:
        drawButton( win, x, y, w, h, label )

    #--- draw "click me!" in middle of screen ---
    t = Text( Point( W/2, H/2 ), "click me!" )
    t.draw( win )

    #--- get 10 mouse clicks and count them ---
    for i in range( 1000 ):
        userPoint = win.getMouse()
        count = 0
        for x, y, w, h, label in L:
            if isInside( userPoint, x, y, w, h ):
                count += 1
        if count!=0:
            t.setText( "Inside #" + str(i+1) )
        else:
            t.setText( "Outside #" + str(i+1) )

        
    #--- wait for  one more click and close up window---
    #win.getMouse()
    win.close()

main()



Adding more balls

  • If you get stuck on the previous part, you may want to take a look at this code for inspiration...
  • Make your program ask the user for the number of balls he/she wants to see move around the window.

Balls of different colors

  • The colors supported by the Zelle graphics library are listed in this document.
  • Figure out a way to make your program use different colors (at least 5) for the balls.
  • Make your program robust, so that if the user requests to have 10 balls, but you have defined only 5 colors, the program will rotate through the colors.
Hints: remember the modulo operator %! In particular, try this code in interactive python:
 >>> for i in range( 20 ):
 >>> ...    print i % 7

Pit Stop

BallPitStop.png
  • Go back to the original program with only 1 ball, and no list, no colors.
  • Add a 60x60 square somewhere in your window.
  • Modify your code so that when the ball falls completely in the square it stops.
  • Once your program works, modify the last version of your program, with lists and colors, and add the stopping hole to it.
Hints: Be careful that when you take a ball out of the list, like so:
      c, dx, dy  = L[i]    

and you modify either dx, or dy, it will not modify the dx or dy that are in the list. Just the copy of them that you got out. You have to store c, dx, and dy back in the list to "remember" the changed quantities:
      L[i] = [ c, dx, dy ]

Exceptions

TypeError Exceptions

The material for this section is covered in the book, in Section 7.4.

The code section below should remind you of a recent homework assignment. Create a program called stats.py with this code:



statistics = [['Afghanistan', 28710000, 'NA', 'NA', 1], ['Albania', 3580000,
               12000, 'NA', 10], ['Algeria', 32810000, 180000, 'NA', 2],
               ['Andorra', 69150, 24500, 'NA', 1], ['Angola', 10760000,
               60000, 'NA', 1], ['Anguilla', 12738, 919, 'NA', 16],
               ['Antigua_and_Barbuda', 67897, 5000, 'NA', 16], ['Argentina',
               38740000, 4650000, 'NA', 33], ['Armenia', 3320000, 30000, 'NA',
               9], ['Aruba', 70844, 24000, 'NA', 'NA'], ['Australia', 19730000,
               13010000, 9020000, 571], ['Austria', 8180000, 4650000,
               1300000, 37], ['Azerbaijan', 7830000, 25000, 'NA', 2],
               ['Bahrain', 667238, 140200, 'NA', 1], ['Bangladesh',
               138440000, 150000, 'NA', 10], ['Barbados', 277264, 6000, 'NA',
               19], ['Belarus', 10330000, 422000, 'NA', 23], ['Belgium',
               10280000, 4870000, 1600000, 61], ['Belize', 266440, 18000,
               'NA', 2], ['Benin', 7040000, 25000, 'NA', 4], ['Bhutan',
               2130000, 2500, 'NA', 'NA'], ['Bolivia', 8580000, 78000, 'NA', 9],
               ['Bosnia_and_Herzegovian', 3980000, 45000, 'NA', 3],
               ['Botswana', 1570000, 33000, 'NA', 11], ['Brazil', 182030000,
               22320000, 10860000, 50], ['Brunei', 358098, 35000, 'NA', 2],
               ['Bulgaria', 7530000, 1610000, 'NA', 200], ['Burkina_Faso',
               13220000, 25000, 'NA', 1], ['Burma', 42510000, 10000, 'NA', 1],
               ['Burundi', 6090000, 6000, 'NA', 1], ['Cambodia', 13120000,
               10000, 'NA', 2], ['Cameroon', 15740000, 45000, 'NA', 1] ]

def main():
    for country, pop, users, active, isp in statistics:
        print "%30s (%d habitants)" % (country, pop ),
        print "Users=", users, " Active=", active, " ISP=", isp


main()



  • Run the program.
  • Notice that the data are in their original form. When a quantity was not known, the creators of the list used "NA" to indicate that it was Not Available.
  • Let's compute the total population of the (partial) list of countries in the list statistics.
    count = 0
    for country, pop, users, active, isp in statistics:
         count += pop
Go ahead and see if your get a total population of 618039669.
  • Now, same idea: modify your program so that it computes as well the total number of Users. Make it use the very same construct as shown above, but now for the quantity users.
  • Run your program.
  • Did you get an error of this type?
Traceback (most recent call last):
  File "stats2.py", line 36, in <module>
    main()
  File "stats2.py", line 31, in main
    userCount += users
TypeError: unsupported operand type(s) for +=: 'int' and 'str'
If so, why? What made the program crash?
Think hard!
Harder!







FrogThinking.jpg







Do not move on until you know for sure why this error was generated.







  • I trust that if you are reading this, it is because you know what caused your program to crash. The clue is unsupported operand type(s) for +=: 'int' and 'str' , indicating that we are trying to add 'NA' which is a string to count, which is an integer.
We could use an if statement to test whether users contains 'NA' or not, but we'll use another approach. We will tell python to try adding users to the counting variable, and if there is an error of type TypeError (see the last ligne of the error message when the program crashed), then we won't add anything to the count variable; we'll just pass.
The main program becomes:
def main():
    popCount = 0
    userCount = 0
    for country, pop, users, active, isp in statistics:
        print "%30s (%d habitants)" % (country, pop ),
        print "Users=", users, " Active=", active, " ISP=", isp
        popCount += pop
        try:
            userCount += users
        except TypeError:
            # don't do anything
            pass 
        
    print "total population = ", popCount
    print "total users      = ", userCount
  • Try it!
  • Use the same method to compute the total number of ISPs.

Other Exceptions

  • Imagine that now the list statistics is defined as shown below, and that for some countries the ISP value (the last one) is missing:
.
statistics = [['Afghanistan', 28710000, 'NA', 'NA', 1], ['Albania', 3580000,
                12000, 'NA', 10], ['Algeria', 32810000, 180000, 'NA'],
                ['Andorra', 69150, 24500, 'NA', 1], ['Angola', 10760000,
                60000, 'NA', 1], ['Anguilla', 12738, 919, 'NA', 16],
                ['Antigua_and_Barbuda', 67897, 5000, 'NA', 16], ['Argentina',
                38740000, 4650000, 'NA', 33], ['Armenia', 3320000, 30000, 'NA',
                9], ['Aruba', 70844, 24000, 'NA' ], ['Australia', 19730000,
                13010000, 9020000, 571], ['Austria', 8180000, 4650000,
                1300000, 37], ['Azerbaijan', 7830000, 25000, 'NA'],
                ['Bahrain', 667238, 140200, 'NA', 1], ['Bangladesh',
                138440000, 150000, 'NA', 10], ['Barbados', 277264, 6000, 'NA',
                19], ['Belarus', 10330000, 422000, 'NA', 23], ['Belgium',
                10280000, 4870000, 1600000, 61], ['Belize', 266440, 18000,
                'NA', 2], ['Benin', 7040000, 25000, 'NA', 4], ['Bhutan',
                2130000, 2500, 'NA', 'NA'], ['Bolivia', 8580000, 78000, 'NA', 9],
                ['Bosnia_and_Herzegovian', 3980000, 45000, 'NA', 3],
                ['Botswana', 1570000, 33000, 'NA', 11], ['Brazil', 182030000,
                22320000, 10860000, 50], ['Brunei', 358098, 35000, 'NA', 2],
                ['Bulgaria', 7530000, 1610000, 'NA', 200], ['Burkina_Faso', 
                13220000, 25000, 'NA'], ['Burma', 42510000, 10000, 'NA', 1],
                ['Burundi', 6090000, 6000, 'NA', 1], ['Cambodia', 13120000,
               10000, 'NA', 2], ['Cameroon', 15740000, 45000, 'NA', 1] ]

.
  • Write a program (or copy/paste part of the previous one) that will read the list above, and output the total population (which should be the same as before).
    • For this you should write your program as if all the country lists contained 5 quantities.
    • Then you run your program.
    • You observe that it crashes when it tries to extract the 5th quantity from a country list.
    • You note the name of the error generated (which is on the last line, and usually of the form XxxxError, such as IndexError, TypeError, NameError, ValueError, etc.)
    • Add a try/except statement around the code that generates the error (Python refers to a run-time error that creates a crash as an exception):
     try:
         country, pop, users, active, isp = someVariableOfYours
     except XxxxError:
         country, pop, users, active = someVariableOfYours
    • Run your program again and observe that it doesn't crash and that it reports the correct information.