CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: Object-Oriented Programming (OOP) (part1)
(Or, defining and using classes)


  1. Vocabulary
    By the end of this note, you need to understand these terms, and in the case of specific methods, you need to know how and why to implement them.
    • struct, class, object, instance
    • attribute, method, constructor
    • __init__

    Note that this is a subset of material from previous semesters. In particular, the following may be covered next week, or in some cases not at all this semester:
    • subclass, superclass, super, inherit, override
    • static method, @staticmethod, class method, @classmethod
    • type, isinstance
    • __str__, __repr__, __eq__, __hash__
    • __ne__, __add__, __radd__, __gt__, __ge__, __lt__, __le__, etc..
    • __dict__

  2. Structs (download structClass.py)
    # Here we use 'struct' in the classic sense: # https://en.wikipedia.org/wiki/Struct_(C_programming_language) # Note that this is not the same as Python's struct module: # https://docs.python.org/3/library/struct.html # Also note: you are not responsible this week for the Struct implementation. # That is, you do not need to know how to write the Struct class, but # rather only how to use it. from structClass import Struct # (downloaded in structClass.py, see link above) obj = Struct() obj.x = 42 print("obj.x = ", obj.x)

  3. Example: dotsDemo
    ###################################################### # Struct Example: dotsDemo ###################################################### import random from structClass import Struct # defined above (saved in structClass.py) from tkinter import * class Dot(Struct): pass def init(data): data.dots = [ ] def dotContainsPoint(dot, x, y): d = ((dot.x - x)**2 + (dot.y - y)**2)**0.5 return (d <= dot.r) def mousePressed(event, data): for dot in reversed(data.dots): if (dotContainsPoint(dot, event.x, event.y)): dot.clickCount += 1 return radius = random.randint(20,50) color = random.choice(["pink","orange","yellow","green","cyan","purple"]) data.dots.append(Dot(x=event.x, y=event.y, r=radius, fill=color, clickCount=0)) def redrawAll(canvas, data): for dot in data.dots: canvas.create_oval(dot.x-dot.r, dot.y-dot.r, dot.x+dot.r, dot.y+dot.r, fill=dot.fill) canvas.create_text(dot.x, dot.y, text=str(dot.clickCount)) def keyPressed(event, data): pass def timerFired(data): pass #################################### # use the run function as-is #################################### def run(width=300, height=300): def redrawAllWrapper(canvas, data): canvas.delete(ALL) redrawAll(canvas, data) canvas.update() def mousePressedWrapper(event, canvas, data): mousePressed(event, data) redrawAllWrapper(canvas, data) def keyPressedWrapper(event, canvas, data): keyPressed(event, data) redrawAllWrapper(canvas, data) def timerFiredWrapper(canvas, data): timerFired(data) redrawAllWrapper(canvas, data) # pause, then call timerFired again canvas.after(data.timerDelay, timerFiredWrapper, canvas, data) # Set up data and call init class Struct(object): pass data = Struct() data.width = width data.height = height data.timerDelay = 100 # milliseconds init(data) # create the root and the canvas root = Tk() canvas = Canvas(root, width=data.width, height=data.height) canvas.pack() # set up events root.bind("<Button-1>", lambda event: mousePressedWrapper(event, canvas, data)) root.bind("<Key>", lambda event: keyPressedWrapper(event, canvas, data)) timerFiredWrapper(canvas, data) # and launch the app root.mainloop() # blocks until window is closed print("bye!") run(400, 200)

  4. Example: betterDotsDemo
    ###################################################### # Struct-Free Example: betterDotsDemo ###################################################### import random from tkinter import * class Dot(object): def __init__(self, x, y): self.x = x self.y = y self.r = random.randint(20,50) self.fill = random.choice(["pink","orange","yellow","green", "cyan","purple"]) self.clickCount = 0 def containsPoint(self, x, y): d = ((self.x - x)**2 + (self.y - y)**2)**0.5 return (d <= self.r) def draw(self, canvas): canvas.create_oval(self.x-self.r, self.y-self.r, self.x+self.r, self.y+self.r, fill=self.fill) canvas.create_text(self.x, self.y, text=str(self.clickCount)) def init(data): data.dots = [ ] def mousePressed(event, data): for dot in reversed(data.dots): if (dot.containsPoint(event.x, event.y)): dot.clickCount += 1 return data.dots.append(Dot(event.x, event.y)) def redrawAll(canvas, data): for dot in data.dots: dot.draw(canvas) def keyPressed(event, data): pass def timerFired(data): pass #################################### # use the run function as-is #################################### def run(width=300, height=300): def redrawAllWrapper(canvas, data): canvas.delete(ALL) redrawAll(canvas, data) canvas.update() def mousePressedWrapper(event, canvas, data): mousePressed(event, data) redrawAllWrapper(canvas, data) def keyPressedWrapper(event, canvas, data): keyPressed(event, data) redrawAllWrapper(canvas, data) def timerFiredWrapper(canvas, data): timerFired(data) redrawAllWrapper(canvas, data) # pause, then call timerFired again canvas.after(data.timerDelay, timerFiredWrapper, canvas, data) # Set up data and call init class Struct(object): pass data = Struct() data.width = width data.height = height data.timerDelay = 100 # milliseconds init(data) # create the root and the canvas root = Tk() canvas = Canvas(root, width=data.width, height=data.height) canvas.pack() # set up events root.bind("<Button-1>", lambda event: mousePressedWrapper(event, canvas, data)) root.bind("<Key>", lambda event: keyPressedWrapper(event, canvas, data)) timerFiredWrapper(canvas, data) # and launch the app root.mainloop() # blocks until window is closed print("bye!") run(400, 200)

  5. Example: smileyClassDemo
    ###################################################### # smileyClassDemo (similar to betterDotsDemo but more fun!) ###################################################### import random from tkinter import * class Smiley(object): def __init__(self, x, y): self.x = x self.y = y self.r = random.randint(50,100) self.color = random.choice(["cyan", "yellow", "lightGreen", "pink", "orange", "green", "lightBlue", "purple"]) self.maxSmileyPoints = 100 self.smileyPoints = self.maxSmileyPoints # the more points the happier def draw(self, canvas): (x, y, r) = (self.x, self.y, self.r) # face canvas.create_oval(x-r, y-r, x+r, y+r, fill=self.color, outline="black", width=4) # eyes eyeY = y-r/3 eyeR = r/6 for eyeX in [x-r/3, x+r/3]: canvas.create_oval(eyeX-eyeR, eyeY-eyeR, eyeX+eyeR, eyeY+eyeR, fill="black") # mouth mouthY1 = y+r/3 mouthDy = r/6 # max height of mouth smile halfMax = self.maxSmileyPoints/2 mouthY0 = mouthY1 - mouthDy*(self.smileyPoints - halfMax)/halfMax mouthY2 = mouthY0 mouthDx = r*2/3 for mouthX0 in [x-mouthDx, x+mouthDx]: canvas.create_line(mouthX0, mouthY0, x, mouthY1, fill="black", width=4) def contains(self, x, y): return ((self.x - x)**2 + (self.y - y)**2 <= self.r**2) def init(data): data.timerDelay = 100 data.smileys = [ ] def mousePressed(event, data): for smiley in reversed(data.smileys): if (smiley.contains(event.x, event.y)): smiley.smileyPoints = smiley.maxSmileyPoints return # clicked outside of all smileys, so make a new smiley newSmiley = Smiley(event.x, event.y) data.smileys.append(newSmiley) def drawSmileys(canvas, data): for smiley in data.smileys: smiley.draw(canvas) def drawText(canvas, data): texts = ["Smile!!!", "Click in Smileys to make them Smile!", "Click outside Smileys to make a new Smiley!" ] font = "Arial 20 bold" x = data.width/2 for i in range(len(texts)): y = 25*(i+1) canvas.create_text(x, y, text=texts[i], font=font) def redrawAll(canvas, data): drawSmileys(canvas, data) drawText(canvas, data) def keyPressed(event, data): pass def timerFired(data): for smiley in data.smileys: if (smiley.smileyPoints > 0): smiley.smileyPoints -= 1 #################################### # use the run function as-is #################################### def run(width=300, height=300): def redrawAllWrapper(canvas, data): canvas.delete(ALL) redrawAll(canvas, data) canvas.update() def mousePressedWrapper(event, canvas, data): mousePressed(event, data) redrawAllWrapper(canvas, data) def keyPressedWrapper(event, canvas, data): keyPressed(event, data) redrawAllWrapper(canvas, data) def timerFiredWrapper(canvas, data): timerFired(data) redrawAllWrapper(canvas, data) # pause, then call timerFired again canvas.after(data.timerDelay, timerFiredWrapper, canvas, data) # Set up data and call init class Struct(object): pass data = Struct() data.width = width data.height = height data.timerDelay = 100 # milliseconds init(data) # create the root and the canvas root = Tk() canvas = Canvas(root, width=data.width, height=data.height) canvas.pack() # set up events root.bind("<Button-1>", lambda event: mousePressedWrapper(event, canvas, data)) root.bind("<Key>", lambda event: keyPressedWrapper(event, canvas, data)) timerFiredWrapper(canvas, data) # and launch the app root.mainloop() # blocks until window is closed print("bye!") run(800, 600)